diff --git a/src/crm/components/CustomerDetailsCard.tsx b/src/crm/components/CustomerDetailsCard.tsx
new file mode 100644
index 0000000..43b2b2a
--- /dev/null
+++ b/src/crm/components/CustomerDetailsCard.tsx
@@ -0,0 +1,209 @@
+import * as React from "react";
+import Dialog from "@mui/material/Dialog";
+import DialogTitle from "@mui/material/DialogTitle";
+import DialogContent from "@mui/material/DialogContent";
+import DialogActions from "@mui/material/DialogActions";
+import Button from "@mui/material/Button";
+import Box from "@mui/material/Box";
+import Typography from "@mui/material/Typography";
+import Grid from "@mui/material/Grid";
+import Divider from "@mui/material/Divider";
+import IconButton from "@mui/material/IconButton";
+import EditIcon from "@mui/icons-material/Edit";
+import EmailIcon from "@mui/icons-material/Email";
+import PhoneIcon from "@mui/icons-material/Phone";
+import LocationOnIcon from "@mui/icons-material/LocationOn";
+import PersonIcon from "@mui/icons-material/Person";
+import CakeIcon from "@mui/icons-material/Cake";
+import AccessTimeIcon from "@mui/icons-material/AccessTime";
+import CustomerHeaderInfo from "./CustomerHeaderInfo";
+import type { Customer } from "../types/customer";
+
+interface CustomerDetailsCardProps {
+ open: boolean;
+ customer: Customer | null;
+ onClose: () => void;
+ onEdit: (customer: Customer) => void;
+}
+
+interface DetailItemProps {
+ icon: React.ReactNode;
+ label: string;
+ value: string | React.ReactNode;
+}
+
+function DetailItem({ icon, label, value }: DetailItemProps) {
+ return (
+
+ {icon}
+
+
+ {label}
+
+ {value}
+
+
+ );
+}
+
+export default function CustomerDetailsCard({
+ open,
+ customer,
+ onClose,
+ onEdit,
+}: CustomerDetailsCardProps) {
+ if (!customer) {
+ return null;
+ }
+
+ const fullName = `${customer.name.title} ${customer.name.first} ${customer.name.last}`;
+ const address = `${customer.location.street.number} ${customer.location.street.name}, ${customer.location.city}, ${customer.location.state} ${customer.location.postcode}, ${customer.location.country}`;
+ const registeredDate = new Date(
+ customer.registered.date,
+ ).toLocaleDateString();
+ const birthDate = new Date(customer.dob.date).toLocaleDateString();
+
+ const handleEdit = () => {
+ onEdit(customer);
+ };
+
+ return (
+
+ );
+}
diff --git a/src/crm/components/CustomerDialog.tsx b/src/crm/components/CustomerDialog.tsx
new file mode 100644
index 0000000..795767a
--- /dev/null
+++ b/src/crm/components/CustomerDialog.tsx
@@ -0,0 +1,463 @@
+import * as React from "react";
+import { useState, useEffect } from "react";
+import Dialog from "@mui/material/Dialog";
+import DialogTitle from "@mui/material/DialogTitle";
+import DialogContent from "@mui/material/DialogContent";
+import DialogActions from "@mui/material/DialogActions";
+import Button from "@mui/material/Button";
+import TextField from "@mui/material/TextField";
+import Grid from "@mui/material/Grid";
+import FormControl from "@mui/material/FormControl";
+import InputLabel from "@mui/material/InputLabel";
+import Select from "@mui/material/Select";
+import MenuItem from "@mui/material/MenuItem";
+import Box from "@mui/material/Box";
+import Alert from "@mui/material/Alert";
+import CircularProgress from "@mui/material/CircularProgress";
+import { customersApi } from "../utils/customersApi";
+import type {
+ Customer,
+ CreateCustomerRequest,
+ UpdateCustomerRequest,
+} from "../types/customer";
+
+interface CustomerDialogProps {
+ open: boolean;
+ customer?: Customer | null;
+ onClose: () => void;
+ onSave: () => void;
+}
+
+interface FormData {
+ firstName: string;
+ lastName: string;
+ title: string;
+ email: string;
+ username: string;
+ password: string;
+ gender: string;
+ phone: string;
+ cell: string;
+ streetNumber: string;
+ streetName: string;
+ city: string;
+ state: string;
+ country: string;
+ postcode: string;
+}
+
+const initialFormData: FormData = {
+ firstName: "",
+ lastName: "",
+ title: "Mr",
+ email: "",
+ username: "",
+ password: "",
+ gender: "male",
+ phone: "",
+ cell: "",
+ streetNumber: "",
+ streetName: "",
+ city: "",
+ state: "",
+ country: "",
+ postcode: "",
+};
+
+export default function CustomerDialog({
+ open,
+ customer,
+ onClose,
+ onSave,
+}: CustomerDialogProps) {
+ const [formData, setFormData] = useState(initialFormData);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ const isEditing = !!customer;
+
+ useEffect(() => {
+ if (customer) {
+ setFormData({
+ firstName: customer.name.first,
+ lastName: customer.name.last,
+ title: customer.name.title,
+ email: customer.email,
+ username: customer.login.username,
+ password: "", // Don't populate password for editing
+ gender: customer.gender,
+ phone: customer.phone || "",
+ cell: customer.cell || "",
+ streetNumber: customer.location.street.number.toString(),
+ streetName: customer.location.street.name,
+ city: customer.location.city,
+ state: customer.location.state,
+ country: customer.location.country,
+ postcode: customer.location.postcode,
+ });
+ } else {
+ setFormData(initialFormData);
+ }
+ setError(null);
+ }, [customer, open]);
+
+ const handleInputChange =
+ (field: keyof FormData) =>
+ (event: React.ChangeEvent) => {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: event.target.value,
+ }));
+ };
+
+ const handleSelectChange = (field: keyof FormData) => (event: any) => {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: event.target.value,
+ }));
+ };
+
+ const validateForm = (): boolean => {
+ if (!formData.firstName.trim()) {
+ setError("First name is required");
+ return false;
+ }
+ if (!formData.lastName.trim()) {
+ setError("Last name is required");
+ return false;
+ }
+ if (!formData.email.trim()) {
+ setError("Email is required");
+ return false;
+ }
+ if (!formData.username.trim()) {
+ setError("Username is required");
+ return false;
+ }
+ if (!isEditing && !formData.password.trim()) {
+ setError("Password is required for new customers");
+ return false;
+ }
+ return true;
+ };
+
+ const handleSave = async () => {
+ if (!validateForm()) {
+ return;
+ }
+
+ setLoading(true);
+ setError(null);
+
+ try {
+ if (isEditing && customer) {
+ const updateData: UpdateCustomerRequest = {
+ name: {
+ first: formData.firstName,
+ last: formData.lastName,
+ title: formData.title,
+ },
+ location: {
+ street: {
+ number: parseInt(formData.streetNumber) || 0,
+ name: formData.streetName,
+ },
+ city: formData.city,
+ state: formData.state,
+ country: formData.country,
+ postcode: formData.postcode,
+ },
+ email: formData.email,
+ phone: formData.phone,
+ cell: formData.cell,
+ };
+
+ await customersApi.updateCustomer(customer.login.uuid, updateData);
+ } else {
+ const createData: CreateCustomerRequest = {
+ email: formData.email,
+ login: {
+ username: formData.username,
+ password: formData.password,
+ },
+ name: {
+ first: formData.firstName,
+ last: formData.lastName,
+ title: formData.title,
+ },
+ gender: formData.gender,
+ location: {
+ street: {
+ number: parseInt(formData.streetNumber) || 0,
+ name: formData.streetName,
+ },
+ city: formData.city,
+ state: formData.state,
+ country: formData.country,
+ postcode: formData.postcode,
+ },
+ phone: formData.phone,
+ cell: formData.cell,
+ };
+
+ await customersApi.createCustomer(createData);
+ }
+
+ onSave();
+ onClose();
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "An error occurred");
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleClose = () => {
+ if (!loading) {
+ onClose();
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/src/crm/components/CustomerHeaderInfo.tsx b/src/crm/components/CustomerHeaderInfo.tsx
new file mode 100644
index 0000000..e8b6701
--- /dev/null
+++ b/src/crm/components/CustomerHeaderInfo.tsx
@@ -0,0 +1,54 @@
+import * as React from "react";
+import Avatar from "@mui/material/Avatar";
+import Box from "@mui/material/Box";
+import Typography from "@mui/material/Typography";
+import Chip from "@mui/material/Chip";
+import type { Customer } from "../types/customer";
+
+interface CustomerHeaderInfoProps {
+ customer: Customer;
+}
+
+export default function CustomerHeaderInfo({
+ customer,
+}: CustomerHeaderInfoProps) {
+ const fullName = `${customer.name.title} ${customer.name.first} ${customer.name.last}`;
+ const registeredDate = new Date(
+ customer.registered.date,
+ ).toLocaleDateString();
+
+ return (
+
+
+ {fullName.charAt(0).toUpperCase()}
+
+
+
+ {fullName}
+
+
+
+
+
+
+
+ Customer since {registeredDate} ({customer.registered.age} years)
+
+
+
+ );
+}
diff --git a/src/crm/components/CustomersDataGrid.tsx b/src/crm/components/CustomersDataGrid.tsx
new file mode 100644
index 0000000..c5890f6
--- /dev/null
+++ b/src/crm/components/CustomersDataGrid.tsx
@@ -0,0 +1,277 @@
+import * as React from "react";
+import { useState, useEffect, useCallback } from "react";
+import {
+ DataGridPro,
+ GridColDef,
+ GridRowParams,
+ GridCellParams,
+} from "@mui/x-data-grid-pro";
+import Avatar from "@mui/material/Avatar";
+import Chip from "@mui/material/Chip";
+import IconButton from "@mui/material/IconButton";
+import Box from "@mui/material/Box";
+import Tooltip from "@mui/material/Tooltip";
+import EditIcon from "@mui/icons-material/Edit";
+import DeleteIcon from "@mui/icons-material/Delete";
+import VisibilityIcon from "@mui/icons-material/Visibility";
+import Typography from "@mui/material/Typography";
+import { customersApi } from "../utils/customersApi";
+import type { Customer } from "../types/customer";
+
+interface CustomersDataGridProps {
+ onEditCustomer: (customer: Customer) => void;
+ onViewCustomer: (customer: Customer) => void;
+ onDeleteCustomer: (customer: Customer) => void;
+ searchQuery?: string;
+ refreshTrigger?: number;
+}
+
+function renderAvatar(params: GridCellParams) {
+ const customer = params.row as Customer;
+ const fullName = `${customer.name.first} ${customer.name.last}`;
+
+ return (
+
+ {fullName.charAt(0).toUpperCase()}
+
+ );
+}
+
+function renderName(params: GridCellParams) {
+ const customer = params.row as Customer;
+ return `${customer.name.title} ${customer.name.first} ${customer.name.last}`;
+}
+
+function renderLocation(params: GridCellParams) {
+ const customer = params.row as Customer;
+ return `${customer.location.city}, ${customer.location.state}, ${customer.location.country}`;
+}
+
+function renderGender(params: GridCellParams) {
+ const customer = params.row as Customer;
+ const color = customer.gender === "male" ? "primary" : "secondary";
+ return (
+
+ );
+}
+
+function renderAge(params: GridCellParams) {
+ const customer = params.row as Customer;
+ return customer.dob.age;
+}
+
+export default function CustomersDataGrid({
+ onEditCustomer,
+ onViewCustomer,
+ onDeleteCustomer,
+ searchQuery = "",
+ refreshTrigger = 0,
+}: CustomersDataGridProps) {
+ const [customers, setCustomers] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [paginationModel, setPaginationModel] = useState({
+ page: 0,
+ pageSize: 25,
+ });
+ const [rowCount, setRowCount] = useState(0);
+
+ const loadCustomers = useCallback(async () => {
+ setLoading(true);
+ setError(null);
+
+ try {
+ const response = await customersApi.fetchCustomers({
+ page: paginationModel.page + 1, // API is 1-indexed
+ perPage: paginationModel.pageSize,
+ search: searchQuery || undefined,
+ sortBy: "name.first",
+ });
+
+ setCustomers(response.data);
+ setRowCount(response.total);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "An error occurred");
+ setCustomers([]);
+ setRowCount(0);
+ } finally {
+ setLoading(false);
+ }
+ }, [paginationModel.page, paginationModel.pageSize, searchQuery]);
+
+ useEffect(() => {
+ loadCustomers();
+ }, [loadCustomers, refreshTrigger]);
+
+ const handleActions = (params: GridRowParams) => {
+ const customer = params.row as Customer;
+
+ return (
+
+
+ {
+ e.stopPropagation();
+ onViewCustomer(customer);
+ }}
+ >
+
+
+
+
+ {
+ e.stopPropagation();
+ onEditCustomer(customer);
+ }}
+ >
+
+
+
+
+ {
+ e.stopPropagation();
+ onDeleteCustomer(customer);
+ }}
+ >
+
+
+
+
+ );
+ };
+
+ const columns: GridColDef[] = [
+ {
+ field: "avatar",
+ headerName: "",
+ width: 60,
+ sortable: false,
+ filterable: false,
+ disableColumnMenu: true,
+ renderCell: renderAvatar,
+ },
+ {
+ field: "name",
+ headerName: "Name",
+ flex: 1,
+ minWidth: 200,
+ renderCell: renderName,
+ sortable: false, // API handles sorting
+ },
+ {
+ field: "email",
+ headerName: "Email",
+ flex: 1,
+ minWidth: 200,
+ sortable: false,
+ },
+ {
+ field: "phone",
+ headerName: "Phone",
+ flex: 0.8,
+ minWidth: 140,
+ sortable: false,
+ },
+ {
+ field: "location",
+ headerName: "Location",
+ flex: 1,
+ minWidth: 200,
+ renderCell: renderLocation,
+ sortable: false,
+ },
+ {
+ field: "gender",
+ headerName: "Gender",
+ width: 100,
+ renderCell: renderGender,
+ sortable: false,
+ },
+ {
+ field: "age",
+ headerName: "Age",
+ width: 80,
+ renderCell: renderAge,
+ sortable: false,
+ },
+ {
+ field: "actions",
+ headerName: "Actions",
+ width: 140,
+ sortable: false,
+ filterable: false,
+ disableColumnMenu: true,
+ renderCell: handleActions,
+ },
+ ];
+
+ if (error) {
+ return (
+
+ Error loading customers: {error}
+
+ );
+ }
+
+ return (
+ row.login.uuid}
+ disableRowSelectionOnClick
+ density="comfortable"
+ sx={{
+ "& .MuiDataGrid-row:hover": {
+ cursor: "default",
+ },
+ }}
+ slotProps={{
+ filterPanel: {
+ filterFormProps: {
+ logicOperatorInputProps: {
+ variant: "outlined",
+ size: "small",
+ },
+ columnInputProps: {
+ variant: "outlined",
+ size: "small",
+ sx: { mt: "auto" },
+ },
+ operatorInputProps: {
+ variant: "outlined",
+ size: "small",
+ sx: { mt: "auto" },
+ },
+ valueInputProps: {
+ InputComponentProps: {
+ variant: "outlined",
+ size: "small",
+ },
+ },
+ },
+ },
+ }}
+ />
+ );
+}
diff --git a/src/crm/pages/Customers.tsx b/src/crm/pages/Customers.tsx
index bd63a59..4948b43 100644
--- a/src/crm/pages/Customers.tsx
+++ b/src/crm/pages/Customers.tsx
@@ -1,17 +1,256 @@
import * as React from "react";
+import { useState, useCallback } from "react";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
+import Button from "@mui/material/Button";
+import TextField from "@mui/material/TextField";
+import InputAdornment from "@mui/material/InputAdornment";
+import Dialog from "@mui/material/Dialog";
+import DialogTitle from "@mui/material/DialogTitle";
+import DialogContent from "@mui/material/DialogContent";
+import DialogActions from "@mui/material/DialogActions";
+import DialogContentText from "@mui/material/DialogContentText";
+import Snackbar from "@mui/material/Snackbar";
+import Alert from "@mui/material/Alert";
+import Paper from "@mui/material/Paper";
+import Stack from "@mui/material/Stack";
+import SearchIcon from "@mui/icons-material/Search";
+import AddIcon from "@mui/icons-material/Add";
+import RefreshIcon from "@mui/icons-material/Refresh";
+import CustomersDataGrid from "../components/CustomersDataGrid";
+import CustomerDialog from "../components/CustomerDialog";
+import CustomerDetailsCard from "../components/CustomerDetailsCard";
+import { customersApi } from "../utils/customersApi";
+import type { Customer } from "../types/customer";
export default function Customers() {
+ const [searchQuery, setSearchQuery] = useState("");
+ const [customerDialogOpen, setCustomerDialogOpen] = useState(false);
+ const [customerDetailsOpen, setCustomerDetailsOpen] = useState(false);
+ const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
+ const [selectedCustomer, setSelectedCustomer] = useState(
+ null,
+ );
+ const [refreshTrigger, setRefreshTrigger] = useState(0);
+ const [snackbar, setSnackbar] = useState<{
+ open: boolean;
+ message: string;
+ severity: "success" | "error" | "info";
+ }>({
+ open: false,
+ message: "",
+ severity: "success",
+ });
+
+ const showSnackbar = (
+ message: string,
+ severity: "success" | "error" | "info" = "success",
+ ) => {
+ setSnackbar({ open: true, message, severity });
+ };
+
+ const handleSnackbarClose = () => {
+ setSnackbar((prev) => ({ ...prev, open: false }));
+ };
+
+ const handleRefresh = () => {
+ setRefreshTrigger((prev) => prev + 1);
+ showSnackbar("Customer data refreshed", "info");
+ };
+
+ const handleAddCustomer = () => {
+ setSelectedCustomer(null);
+ setCustomerDialogOpen(true);
+ };
+
+ const handleEditCustomer = useCallback((customer: Customer) => {
+ setSelectedCustomer(customer);
+ setCustomerDialogOpen(true);
+ }, []);
+
+ const handleViewCustomer = useCallback((customer: Customer) => {
+ setSelectedCustomer(customer);
+ setCustomerDetailsOpen(true);
+ }, []);
+
+ const handleDeleteCustomer = useCallback((customer: Customer) => {
+ setSelectedCustomer(customer);
+ setDeleteDialogOpen(true);
+ }, []);
+
+ const handleCustomerDialogClose = () => {
+ setCustomerDialogOpen(false);
+ setSelectedCustomer(null);
+ };
+
+ const handleCustomerDetailsClo = () => {
+ setCustomerDetailsOpen(false);
+ setSelectedCustomer(null);
+ };
+
+ const handleCustomerSave = () => {
+ setRefreshTrigger((prev) => prev + 1);
+ showSnackbar(
+ selectedCustomer
+ ? "Customer updated successfully"
+ : "Customer created successfully",
+ );
+ };
+
+ const handleConfirmDelete = async () => {
+ if (!selectedCustomer) return;
+
+ try {
+ await customersApi.deleteCustomer(selectedCustomer.login.uuid);
+ setDeleteDialogOpen(false);
+ setSelectedCustomer(null);
+ setRefreshTrigger((prev) => prev + 1);
+ showSnackbar("Customer deleted successfully");
+ } catch (error) {
+ showSnackbar(
+ error instanceof Error ? error.message : "Failed to delete customer",
+ "error",
+ );
+ }
+ };
+
+ const handleCancelDelete = () => {
+ setDeleteDialogOpen(false);
+ setSelectedCustomer(null);
+ };
+
+ const handleEditFromDetails = (customer: Customer) => {
+ setCustomerDetailsOpen(false);
+ setSelectedCustomer(customer);
+ setCustomerDialogOpen(true);
+ };
+
+ const handleSearchChange = (event: React.ChangeEvent) => {
+ setSearchQuery(event.target.value);
+ };
+
return (
-
- Customers Page
-
-
- This is the customers management page where you can view and manage your
- customer data.
-
+ {/* Header */}
+
+
+ Customer Management
+
+
+ Manage your customer database with comprehensive tools for viewing,
+ editing, and organizing customer information.
+
+
+
+ {/* Toolbar */}
+
+
+
+
+
+ ),
+ }}
+ />
+
+ }
+ onClick={handleRefresh}
+ >
+ Refresh
+
+ }
+ onClick={handleAddCustomer}
+ >
+ Add Customer
+
+
+
+
+
+ {/* Data Grid */}
+
+
+
+
+ {/* Customer Dialog (Create/Edit) */}
+
+
+ {/* Customer Details Dialog */}
+
+
+ {/* Delete Confirmation Dialog */}
+
+
+ {/* Snackbar for notifications */}
+
+
+ {snackbar.message}
+
+
);
}
diff --git a/src/crm/types/customer.ts b/src/crm/types/customer.ts
new file mode 100644
index 0000000..96253b5
--- /dev/null
+++ b/src/crm/types/customer.ts
@@ -0,0 +1,103 @@
+export interface CustomerName {
+ title: string;
+ first: string;
+ last: string;
+}
+
+export interface CustomerLocation {
+ street: {
+ number: number;
+ name: string;
+ };
+ city: string;
+ state: string;
+ country: string;
+ postcode: string;
+ coordinates?: {
+ latitude: number;
+ longitude: number;
+ };
+ timezone?: {
+ offset: string;
+ description: string;
+ };
+}
+
+export interface CustomerDateOfBirth {
+ date: string;
+ age: number;
+}
+
+export interface CustomerRegistered {
+ date: string;
+ age: number;
+}
+
+export interface CustomerPicture {
+ large: string;
+ medium: string;
+ thumbnail: string;
+}
+
+export interface CustomerLogin {
+ uuid: string;
+ username: string;
+ password?: string;
+}
+
+export interface Customer {
+ login: CustomerLogin;
+ name: CustomerName;
+ gender: string;
+ location: CustomerLocation;
+ email: string;
+ dob: CustomerDateOfBirth;
+ registered: CustomerRegistered;
+ phone: string;
+ cell: string;
+ picture: CustomerPicture;
+ nat: string;
+}
+
+export interface CustomersResponse {
+ page: number;
+ perPage: number;
+ total: number;
+ span: string;
+ effectivePage: number;
+ data: Customer[];
+}
+
+export interface CreateCustomerRequest {
+ email: string;
+ login: {
+ username: string;
+ password: string;
+ };
+ name: {
+ first: string;
+ last: string;
+ title: string;
+ };
+ gender: string;
+ location: {
+ street: {
+ number: number;
+ name: string;
+ };
+ city: string;
+ state: string;
+ country: string;
+ postcode: string;
+ };
+ phone?: string;
+ cell?: string;
+}
+
+export interface UpdateCustomerRequest {
+ name?: Partial;
+ location?: Partial;
+ email?: string;
+ phone?: string;
+ cell?: string;
+}
diff --git a/src/crm/utils/customersApi.ts b/src/crm/utils/customersApi.ts
new file mode 100644
index 0000000..c4d17ce
--- /dev/null
+++ b/src/crm/utils/customersApi.ts
@@ -0,0 +1,131 @@
+import type {
+ Customer,
+ CustomersResponse,
+ CreateCustomerRequest,
+ UpdateCustomerRequest,
+} from "../types/customer";
+
+const API_BASE_URL = "https://user-api.builder-io.workers.dev/api";
+
+export interface FetchCustomersParams {
+ page?: number;
+ perPage?: number;
+ search?: string;
+ sortBy?: string;
+ span?: string;
+}
+
+export const customersApi = {
+ // Fetch paginated customers with optional search and sorting
+ async fetchCustomers(
+ params: FetchCustomersParams = {},
+ ): Promise {
+ const searchParams = new URLSearchParams();
+
+ if (params.page) searchParams.set("page", params.page.toString());
+ if (params.perPage) searchParams.set("perPage", params.perPage.toString());
+ if (params.search) searchParams.set("search", params.search);
+ if (params.sortBy) searchParams.set("sortBy", params.sortBy);
+ if (params.span) searchParams.set("span", params.span);
+
+ const url = `${API_BASE_URL}/users?${searchParams.toString()}`;
+
+ try {
+ const response = await fetch(url);
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error("Error fetching customers:", error);
+ throw error;
+ }
+ },
+
+ // Get a specific customer by ID, username, or email
+ async getCustomer(id: string): Promise {
+ try {
+ const response = await fetch(`${API_BASE_URL}/users/${id}`);
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error("Error fetching customer:", error);
+ throw error;
+ }
+ },
+
+ // Create a new customer
+ async createCustomer(
+ customerData: CreateCustomerRequest,
+ ): Promise<{ success: boolean; uuid: string; message: string }> {
+ try {
+ const response = await fetch(`${API_BASE_URL}/users`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(customerData),
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error("Error creating customer:", error);
+ throw error;
+ }
+ },
+
+ // Update an existing customer
+ async updateCustomer(
+ id: string,
+ customerData: UpdateCustomerRequest,
+ ): Promise<{ success: boolean; message: string }> {
+ try {
+ const response = await fetch(`${API_BASE_URL}/users/${id}`, {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(customerData),
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error("Error updating customer:", error);
+ throw error;
+ }
+ },
+
+ // Delete a customer
+ async deleteCustomer(
+ id: string,
+ ): Promise<{ success: boolean; message: string }> {
+ try {
+ const response = await fetch(`${API_BASE_URL}/users/${id}`, {
+ method: "DELETE",
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error("Error deleting customer:", error);
+ throw error;
+ }
+ },
+};