diff --git a/client/src/components/shared/theme-toggle.tsx b/client/src/components/shared/theme-toggle.tsx index 78e641e..f3d40c1 100644 --- a/client/src/components/shared/theme-toggle.tsx +++ b/client/src/components/shared/theme-toggle.tsx @@ -1,24 +1,39 @@ import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { useTheme } from "@/hooks/use-theme"; -import { Moon, Sun } from "lucide-react"; +import { Moon, Sun, Laptop } from "lucide-react"; export function ThemeToggle() { const { theme, setTheme } = useTheme(); - const toggleTheme = () => { - setTheme(theme === "dark" ? "light" : "dark"); - }; - return ( - - - - Toggle theme - + + + + + + Toggle theme + + + + setTheme("light")}> + + Light + + setTheme("dark")}> + + Dark + + setTheme("system")}> + + System + + + ); } diff --git a/client/src/context/organization-context.tsx b/client/src/context/organization-context.tsx index 27d5d46..d1ddf67 100644 --- a/client/src/context/organization-context.tsx +++ b/client/src/context/organization-context.tsx @@ -1,19 +1,23 @@ import { createContext, useContext, useState, useEffect, ReactNode } from "react"; -import { useQuery } from "@tanstack/react-query"; -import { Organization } from "@shared/schema"; +import { useQuery, useMutation, UseMutationResult } from "@tanstack/react-query"; +import { Organization, InsertOrganization } from "@shared/schema"; import { useAuth } from "@/hooks/use-auth"; +import { apiRequest, queryClient } from "@/lib/queryClient"; +import { useToast } from "@/hooks/use-toast"; type OrganizationContextType = { organizations: Organization[]; currentOrganization: Organization | null; setCurrentOrganization: (organization: Organization) => void; isLoading: boolean; + createOrganizationMutation: UseMutationResult; }; const OrganizationContext = createContext(undefined); export function OrganizationProvider({ children }: { children: ReactNode }) { const { user } = useAuth(); + const { toast } = useToast(); const [currentOrganization, setCurrentOrganization] = useState(null); const { data: organizations = [], isLoading } = useQuery({ @@ -21,6 +25,28 @@ export function OrganizationProvider({ children }: { children: ReactNode }) { // Only fetch if user is logged in enabled: !!user, }); + + const createOrganizationMutation = useMutation({ + mutationFn: async (orgData: InsertOrganization) => { + const res = await apiRequest("POST", "/api/organizations", orgData); + return await res.json(); + }, + onSuccess: (newOrg: Organization) => { + queryClient.invalidateQueries({ queryKey: ["/api/organizations"] }); + toast({ + title: "Organization created", + description: `Organization '${newOrg.name}' has been created successfully.`, + }); + setCurrentOrganization(newOrg); + }, + onError: (error: Error) => { + toast({ + title: "Failed to create organization", + description: error.message, + variant: "destructive", + }); + }, + }); // Set default organization when organizations are loaded useEffect(() => { @@ -41,7 +67,13 @@ export function OrganizationProvider({ children }: { children: ReactNode }) { return ( {children} diff --git a/client/src/pages/settings.tsx b/client/src/pages/settings.tsx index 017c21c..2385db2 100644 --- a/client/src/pages/settings.tsx +++ b/client/src/pages/settings.tsx @@ -53,7 +53,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; import { Separator } from "@/components/ui/separator"; -import { Loader2, Moon, Sun, Lock } from "lucide-react"; +import { Loader2, Moon, Sun, Lock, Laptop } from "lucide-react"; // Organization form schema const organizationFormSchema = z.object({ @@ -61,6 +61,12 @@ const organizationFormSchema = z.object({ isActive: z.boolean().default(true), }); +// Create organization form schema +const createOrganizationFormSchema = z.object({ + name: z.string().min(1, "Organization name is required"), + isActive: z.boolean().default(true), +}); + // Account form schema const accountFormSchema = z.object({ username: z.string().min(3, "Username must be at least 3 characters"), @@ -82,10 +88,19 @@ export default function SettingsPage() { const { toast } = useToast(); const { user } = useAuth(); const { theme, setTheme } = useTheme(); - const { currentOrganization, setCurrentOrganization } = useOrganization(); + const { currentOrganization, setCurrentOrganization, createOrganizationMutation } = useOrganization(); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); + // Create organization form + const createOrganizationForm = useForm>({ + resolver: zodResolver(createOrganizationFormSchema), + defaultValues: { + name: "", + isActive: true, + }, + }); + // Fetch organization data const { data: organization } = useQuery({ queryKey: ["/api/organizations", currentOrganization?.id], @@ -434,6 +449,77 @@ export default function SettingsPage() { ) : ( <> + {user?.role === "admin" && ( + + + Create New Organization + + Add a new organization to your account + + + + + { + createOrganizationMutation.mutate(data); + createOrganizationForm.reset(); + })} + className="space-y-4" + > + ( + + Organization Name + + + + + + )} + /> + + ( + + + Active Status + + Enable this organization upon creation + + + + + + + )} + /> + + + {createOrganizationMutation.isPending ? ( + <> + + Creating... + > + ) : ( + "Create Organization" + )} + + + + + + )} + Organization Details @@ -546,7 +632,7 @@ export default function SettingsPage() { Select your preferred theme appearance - + Dark background with light text + setTheme("system")} + > + + + System + Follow your system preference + + diff --git a/theme.json b/theme.json index 4db1e19..295ea89 100644 --- a/theme.json +++ b/theme.json @@ -1,6 +1,6 @@ { "variant": "professional", "primary": "hsl(221.2 83.2% 53.3%)", - "appearance": "light", + "appearance": "system", "radius": 0.5 }
Select your preferred theme appearance
Follow your system preference