diff --git a/client/src/components/customer/customer-list-card.tsx b/client/src/components/customer/customer-list-card.tsx
new file mode 100644
index 0000000..dd193aa
--- /dev/null
+++ b/client/src/components/customer/customer-list-card.tsx
@@ -0,0 +1,80 @@
+import { useState } from "react";
+import { format } from "date-fns";
+import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
+import { useCustomer } from "@/context/customer-context";
+import { Customer } from "@shared/schema";
+import { Calendar, Server, Users } from "lucide-react";
+
+interface CustomerListCardProps {
+ customers: Customer[];
+}
+
+export function CustomerListCard({ customers }: CustomerListCardProps) {
+ const { currentCustomer, setCurrentCustomer } = useCustomer();
+
+ if (!customers || customers.length === 0) {
+ return (
+
+
+ Customers
+
+ You don't have any customers yet.
+
+
+
+
+
+ Create a customer in the settings page.
+
+
+
+
+ );
+ }
+
+ return (
+
+
+ Customers
+
+ Select a customer to manage their domains and DNS records
+
+
+
+
+ {customers.map((customer) => (
+
setCurrentCustomer(customer)}
+ >
+
+
+
+ {customer.name}
+ {!customer.isActive && (
+
+ Inactive
+
+ )}
+
+
+
+
+
+ 3 Members
+
+
+
+ Created {format(new Date(customer.createdAt), 'MMM d, yyyy')}
+
+
+
+ ))}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/client/src/components/shared/customer-selector.tsx b/client/src/components/shared/customer-selector.tsx
new file mode 100644
index 0000000..496e42a
--- /dev/null
+++ b/client/src/components/shared/customer-selector.tsx
@@ -0,0 +1,63 @@
+import { Button } from "@/components/ui/button";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import { useCustomer } from "@/context/customer-context";
+import { ChevronDown } from "lucide-react";
+
+export function CustomerSelector() {
+ const { customers, currentCustomer, setCurrentCustomer, isLoading } = useCustomer();
+
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ if (!currentCustomer || customers.length === 0) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ {customers.map((customer) => (
+ setCurrentCustomer(customer)}
+ className={customer.id === currentCustomer.id ? "bg-muted" : ""}
+ >
+
+
+ {customer.name}
+
+
+ ))}
+
+
+ );
+}
\ No newline at end of file
diff --git a/client/src/context/customer-context.tsx b/client/src/context/customer-context.tsx
new file mode 100644
index 0000000..bb5f2e4
--- /dev/null
+++ b/client/src/context/customer-context.tsx
@@ -0,0 +1,153 @@
+import { createContext, ReactNode, useContext, useState, useEffect } from "react";
+import { useQuery, useMutation, UseMutationResult } from "@tanstack/react-query";
+import { Customer, Domain, InsertCustomer } from "@shared/schema";
+import { apiRequest, queryClient } from "@/lib/queryClient";
+import { useToast } from "@/hooks/use-toast";
+import { useAuth } from "@/hooks/use-auth";
+
+type CustomerContextType = {
+ customers: Customer[];
+ currentCustomer: Customer | null;
+ setCurrentCustomer: (customer: Customer) => void;
+ isLoading: boolean;
+ createCustomerMutation: UseMutationResult;
+ updateCustomerMutation: UseMutationResult }>;
+ deleteCustomerMutation: UseMutationResult;
+ domainsLoading: boolean;
+ domainsByCustomer: Record;
+};
+
+const CustomerContext = createContext(undefined);
+
+export function CustomerProvider({ children }: { children: ReactNode }) {
+ const { user } = useAuth();
+ const { toast } = useToast();
+ const [currentCustomer, setCurrentCustomer] = useState(null);
+ const [domainsByCustomer, setDomainsByCustomer] = useState>({});
+
+ // Get customers
+ const { data: customers = [], isLoading } = useQuery({
+ queryKey: ["/api/customers"],
+ staleTime: 1000 * 60 * 5, // 5 minutes
+ });
+
+ // Load domains for current customer
+ const { isLoading: domainsLoading } = useQuery({
+ queryKey: ["/api/domains", { customerId: currentCustomer?.id }],
+ enabled: !!currentCustomer,
+ staleTime: 1000 * 60 * 5, // 5 minutes
+ onSuccess: (domains) => {
+ if (currentCustomer) {
+ setDomainsByCustomer(prev => ({
+ ...prev,
+ [currentCustomer.id]: domains
+ }));
+ }
+ }
+ });
+
+ // Customer mutations
+ const createCustomerMutation = useMutation({
+ mutationFn: async (customer: InsertCustomer) => {
+ const res = await apiRequest("POST", "/api/customers", customer);
+ return await res.json();
+ },
+ onSuccess: (data) => {
+ queryClient.invalidateQueries({ queryKey: ["/api/customers"] });
+ toast({
+ title: "Customer created",
+ description: "The customer has been successfully created.",
+ });
+ },
+ onError: (error) => {
+ toast({
+ title: "Failed to create customer",
+ description: error.message,
+ variant: "destructive",
+ });
+ },
+ });
+
+ const updateCustomerMutation = useMutation({
+ mutationFn: async ({ id, data }: { id: string; data: Partial }) => {
+ const res = await apiRequest("PUT", `/api/customers/${id}`, data);
+ return await res.json();
+ },
+ onSuccess: (data) => {
+ queryClient.invalidateQueries({ queryKey: ["/api/customers"] });
+ toast({
+ title: "Customer updated",
+ description: "The customer has been successfully updated.",
+ });
+ },
+ onError: (error) => {
+ toast({
+ title: "Failed to update customer",
+ description: error.message,
+ variant: "destructive",
+ });
+ },
+ });
+
+ const deleteCustomerMutation = useMutation({
+ mutationFn: async (id: string) => {
+ await apiRequest("DELETE", `/api/customers/${id}`);
+ return true;
+ },
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ["/api/customers"] });
+ toast({
+ title: "Customer deleted",
+ description: "The customer has been successfully deleted.",
+ });
+
+ // Reset current customer if it was deleted
+ if (currentCustomer && customers.length > 0 && !customers.find(c => c.id === currentCustomer.id)) {
+ setCurrentCustomer(customers[0]);
+ }
+ },
+ onError: (error) => {
+ toast({
+ title: "Failed to delete customer",
+ description: error.message,
+ variant: "destructive",
+ });
+ },
+ });
+
+ // Set default customer when customers are loaded
+ useEffect(() => {
+ if (customers.length > 0 && !currentCustomer) {
+ // Otherwise, use the first customer
+ setCurrentCustomer(customers[0]);
+ }
+ }, [customers, currentCustomer]);
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useCustomer() {
+ const context = useContext(CustomerContext);
+
+ if (context === undefined) {
+ throw new Error("useCustomer must be used within a CustomerProvider");
+ }
+
+ return context;
+}
\ No newline at end of file