diff --git a/client/src/context/organization-context.tsx b/client/src/context/organization-context.tsx index d1ddf67..ba1bbc5 100644 --- a/client/src/context/organization-context.tsx +++ b/client/src/context/organization-context.tsx @@ -1,6 +1,6 @@ import { createContext, useContext, useState, useEffect, ReactNode } from "react"; import { useQuery, useMutation, UseMutationResult } from "@tanstack/react-query"; -import { Organization, InsertOrganization } from "@shared/schema"; +import { Organization, InsertOrganization, Domain } from "@shared/schema"; import { useAuth } from "@/hooks/use-auth"; import { apiRequest, queryClient } from "@/lib/queryClient"; import { useToast } from "@/hooks/use-toast"; @@ -11,6 +11,10 @@ type OrganizationContextType = { setCurrentOrganization: (organization: Organization) => void; isLoading: boolean; createOrganizationMutation: UseMutationResult; + updateOrganizationMutation: UseMutationResult }>; + deleteOrganizationMutation: UseMutationResult; + domainsLoading: boolean; + domainsByOrganization: Record; }; const OrganizationContext = createContext(undefined); @@ -19,6 +23,7 @@ export function OrganizationProvider({ children }: { children: ReactNode }) { const { user } = useAuth(); const { toast } = useToast(); const [currentOrganization, setCurrentOrganization] = useState(null); + const [domainsByOrganization, setDomainsByOrganization] = useState>({}); const { data: organizations = [], isLoading } = useQuery({ queryKey: ["/api/organizations"], @@ -26,6 +31,32 @@ export function OrganizationProvider({ children }: { children: ReactNode }) { enabled: !!user, }); + // Load domains for each organization + const { isLoading: domainsLoading } = useQuery({ + queryKey: ["/api/domains"], + enabled: !!currentOrganization + }); + + // Use the domains data to group by organization + const { data: domains = [] } = useQuery({ + queryKey: ["/api/domains"], + enabled: !!currentOrganization + }); + + // Group domains by organization when domains change + useEffect(() => { + if (domains.length > 0) { + const domainsByOrg: Record = {}; + domains.forEach(domain => { + if (!domainsByOrg[domain.organizationId]) { + domainsByOrg[domain.organizationId] = []; + } + domainsByOrg[domain.organizationId].push(domain); + }); + setDomainsByOrganization(domainsByOrg); + } + }, [domains]); + const createOrganizationMutation = useMutation({ mutationFn: async (orgData: InsertOrganization) => { const res = await apiRequest("POST", "/api/organizations", orgData); @@ -47,6 +78,64 @@ export function OrganizationProvider({ children }: { children: ReactNode }) { }); }, }); + + const updateOrganizationMutation = useMutation({ + mutationFn: async ({ id, data }: { id: string; data: Partial }) => { + const res = await apiRequest("PATCH", `/api/organizations/${id}`, data); + return await res.json(); + }, + onSuccess: (updatedOrg: Organization) => { + queryClient.invalidateQueries({ queryKey: ["/api/organizations"] }); + toast({ + title: "Organization updated", + description: `Organization '${updatedOrg.name}' has been updated successfully.`, + }); + + // If the current organization was updated, update it in the state + if (currentOrganization?.id === updatedOrg.id) { + setCurrentOrganization(updatedOrg); + } + }, + onError: (error: Error) => { + toast({ + title: "Failed to update organization", + description: error.message, + variant: "destructive", + }); + }, + }); + + const deleteOrganizationMutation = useMutation({ + mutationFn: async (id: string) => { + const res = await apiRequest("DELETE", `/api/organizations/${id}`); + return res.ok; + }, + onSuccess: (_, id) => { + queryClient.invalidateQueries({ queryKey: ["/api/organizations"] }); + + // If the current organization was deleted, set another one as current + if (currentOrganization?.id === id) { + const remainingOrgs = organizations.filter(org => org.id !== id); + if (remainingOrgs.length > 0) { + setCurrentOrganization(remainingOrgs[0]); + } else { + setCurrentOrganization(null); + } + } + + toast({ + title: "Organization deleted", + description: "Organization has been deleted successfully.", + }); + }, + onError: (error: Error) => { + toast({ + title: "Failed to delete organization", + description: error.message, + variant: "destructive", + }); + }, + }); // Set default organization when organizations are loaded useEffect(() => { @@ -72,7 +161,11 @@ export function OrganizationProvider({ children }: { children: ReactNode }) { currentOrganization, setCurrentOrganization, isLoading, - createOrganizationMutation + domainsLoading, + domainsByOrganization, + createOrganizationMutation, + updateOrganizationMutation, + deleteOrganizationMutation }} > {children} diff --git a/client/src/pages/organizations.tsx b/client/src/pages/organizations.tsx index b5f0605..4b09509 100644 --- a/client/src/pages/organizations.tsx +++ b/client/src/pages/organizations.tsx @@ -11,9 +11,19 @@ import { Organization } from '@shared/schema'; import { useOrganization } from '@/context/organization-context'; import { useToast } from '@/hooks/use-toast'; import { useAuth } from '@/hooks/use-auth'; -import { Users, Plus, Edit, Trash2, Building } from 'lucide-react'; +import { Users, Plus, Edit, Trash2, Building, Calendar, Clock, Info, AlertCircle } from 'lucide-react'; import { apiRequest, queryClient } from '@/lib/queryClient'; import { cn } from '@/lib/utils'; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle +} from '@/components/ui/alert-dialog'; export default function OrganizationsPage() { const { user } = useAuth(); @@ -22,6 +32,12 @@ export default function OrganizationsPage() { const [newOrgName, setNewOrgName] = useState(''); const [newOrgActive, setNewOrgActive] = useState(true); const [isDialogOpen, setIsDialogOpen] = useState(false); + const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); + const [isViewDialogOpen, setIsViewDialogOpen] = useState(false); + const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); + const [selectedOrg, setSelectedOrg] = useState(null); + const [editOrgName, setEditOrgName] = useState(''); + const [editOrgActive, setEditOrgActive] = useState(true); const isAdmin = user?.role === 'admin';