import React, { useState, useEffect } from "react"; import { Eye, EyeOff } from "lucide-react"; import { useQuery, useMutation } from "@tanstack/react-query"; import { useParams } from "wouter"; import { StaffTable } from "@/components/staffs/staff-table"; import { useToast } from "@/hooks/use-toast"; import { Card, CardContent } from "@/components/ui/card"; import { apiRequest, queryClient } from "@/lib/queryClient"; import { StaffForm } from "@/components/staffs/staff-form"; import { DeleteConfirmationDialog } from "@/components/ui/deleteDialog"; import { CredentialTable } from "@/components/settings/insuranceCredTable"; import { useAuth } from "@/hooks/use-auth"; import { Staff } from "@repo/db/types"; import { NpiProviderTable } from "@/components/settings/npiProviderTable"; import { ProgramBridgeTable } from "@/components/settings/program-bridge-table"; import { TwilioSettingsCard } from "@/components/settings/twilio-settings-card"; import { AiSettingsCard } from "@/components/settings/ai-settings-card"; type SectionId = | "staff" | "users" | "account" | "credentials" | "npi" | "programs" | "twilio" | "ai"; export default function SettingsPage() { const { toast } = useToast(); const { user: currentUser } = useAuth(); const isAdmin = currentUser?.username === "admin"; const params = useParams<{ section?: string }>(); const section = (params.section as SectionId | undefined) ?? "staff"; // Staff state const [modalOpen, setModalOpen] = useState(false); const [editingStaff, setEditingStaff] = useState(null); const [isDeleteStaffOpen, setIsDeleteStaffOpen] = useState(false); const [currentStaff, setCurrentStaff] = useState(undefined); // User management state const [newUsername, setNewUsername] = useState(""); const [newPassword, setNewPassword] = useState(""); const [showNewUserPassword, setShowNewUserPassword] = useState(false); const [showAdminPassword, setShowAdminPassword] = useState(false); const [usernameUser, setUsernameUser] = useState(""); useEffect(() => { if (currentUser?.username) setUsernameUser(currentUser.username); }, [currentUser]); // ── Staff ────────────────────────────────────────────────────── const { data: staff = [], isLoading: staffLoading, isError: staffError, error: staffErrorMsg } = useQuery({ queryKey: ["/api/staffs/"], queryFn: async () => { const res = await apiRequest("GET", "/api/staffs/"); if (!res.ok) throw new Error("Failed to fetch staff"); return res.json(); }, staleTime: 1000 * 60 * 5, }); const addStaffMutate = useMutation>({ mutationFn: async (newStaff) => { const res = await apiRequest("POST", "/api/staffs/", newStaff); if (!res.ok) { const e = await res.json().catch(() => null); throw new Error(e?.message || "Failed to add staff"); } return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/staffs/"] }); toast({ title: "Staff Added" }); }, onError: (e: any) => toast({ title: "Error", description: e?.message, variant: "destructive" }), }); const updateStaffMutate = useMutation }>({ mutationFn: async ({ id, updatedFields }) => { const res = await apiRequest("PUT", `/api/staffs/${id}`, updatedFields); if (!res.ok) { const e = await res.json().catch(() => null); throw new Error(e?.message || "Failed to update staff"); } return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/staffs/"] }); toast({ title: "Staff Updated" }); }, onError: (e: any) => toast({ title: "Error", description: e?.message, variant: "destructive" }), }); const deleteStaffMutation = useMutation({ mutationFn: async (id) => { const res = await apiRequest("DELETE", `/api/staffs/${id}`); if (!res.ok) { const e = await res.json().catch(() => null); throw new Error(e?.message || "Failed to delete staff"); } return id; }, onSuccess: () => { setIsDeleteStaffOpen(false); queryClient.invalidateQueries({ queryKey: ["/api/staffs/"] }); toast({ title: "Staff Removed" }); }, onError: (e: any) => toast({ title: "Error", description: e?.message, variant: "destructive" }), }); useEffect(() => { if (addStaffMutate.status === "success" || updateStaffMutate.status === "success") setModalOpen(false); }, [addStaffMutate.status, updateStaffMutate.status]); // ── Users ────────────────────────────────────────────────────── const { data: allUsers = [], refetch: refetchUsers } = useQuery<{ id: number; username: string }[]>({ queryKey: ["/api/users/list"], queryFn: async () => { const res = await apiRequest("GET", "/api/users/list"); if (!res.ok) return []; return res.json(); }, enabled: false, }); useEffect(() => { if (isAdmin) refetchUsers(); }, [isAdmin]); const addUserMutation = useMutation({ mutationFn: async (data: { username: string; password: string }) => { const res = await apiRequest("POST", "/api/users/", data); if (!res.ok) { const e = await res.json().catch(() => null); throw new Error(e?.error || "Failed to create user"); } return res.json(); }, onSuccess: () => { setNewUsername(""); setNewPassword(""); refetchUsers(); toast({ title: "User Created" }); }, onError: (e: any) => toast({ title: "Error", description: e?.message, variant: "destructive" }), }); const deleteUserMutation = useMutation({ mutationFn: async (id: number) => { const res = await apiRequest("DELETE", `/api/users/${id}`); if (!res.ok) throw new Error("Failed to delete user"); }, onSuccess: () => { refetchUsers(); toast({ title: "User Deleted" }); }, onError: (e: any) => toast({ title: "Error", description: e?.message, variant: "destructive" }), }); const updateUserMutate = useMutation({ mutationFn: async (updates: Partial<{ username: string; password: string }>) => { if (!currentUser?.id) throw new Error("User not loaded"); const res = await apiRequest("PUT", `/api/users/${currentUser.id}`, updates); if (!res.ok) { const e = await res.json().catch(() => null); throw new Error(e?.error || "Failed to update user"); } return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/users/"] }); toast({ title: "Updated", description: "Your profile has been updated." }); }, onError: (e: any) => toast({ title: "Error", description: e?.message, variant: "destructive" }), }); // ── Section renderer ─────────────────────────────────────────── const renderSection = () => { switch (section) { case "staff": return (
{ setEditingStaff(null); setModalOpen(true); }} onEdit={(s) => { setEditingStaff(s); setModalOpen(true); }} onDelete={(s) => { setCurrentStaff(s); setIsDeleteStaffOpen(true); }} onView={(s) => alert(`Viewing staff member:\n${s.name} (${s.email || "No email"})`)} /> {staffError &&

{(staffErrorMsg as Error)?.message || "Failed to load staff data."}

} { if (currentStaff?.id) deleteStaffMutation.mutate(currentStaff.id); }} onCancel={() => setIsDeleteStaffOpen(false)} entityName={currentStaff?.name} />
); case "users": return (

Manage Users

{allUsers.length === 0 &&

No users found.

} {allUsers.map((u) => (
{u.username} {u.username !== "admin" && ( )}
))}
{ e.preventDefault(); if (!newUsername.trim() || !newPassword.trim()) return; addUserMutation.mutate({ username: newUsername.trim(), password: newPassword.trim() }); }}>

Add New User

setNewUsername(e.target.value)} className="mt-1 p-2 border rounded w-full" placeholder="Enter username" required />
setNewPassword(e.target.value)} className="p-2 border rounded w-full pr-10" placeholder="••••••••" required />
); case "account": return (

Account Settings

{ e.preventDefault(); const formData = new FormData(e.currentTarget); const password = formData.get("password")?.toString().trim() || undefined; updateUserMutate.mutate({ ...(isAdmin ? {} : { username: usernameUser?.trim() || undefined }), password: password || undefined }); }}>
setUsernameUser(e.target.value)} className="mt-1 p-2 border rounded w-full disabled:bg-gray-100 disabled:cursor-not-allowed disabled:text-gray-500" disabled={isAdmin} /> {isAdmin &&

Admin username cannot be changed.

}

Leave blank to keep current password.

); case "credentials": return ; case "npi": return ; case "programs": return ; case "twilio": return ; case "ai": return ; default: return null; } }; return ( <> {renderSection()} {/* Staff add/edit modal */} {modalOpen && (

{editingStaff ? "Edit Staff" : "Add Staff"}

{ if (editingStaff) { if (!editingStaff.id) return; updateStaffMutate.mutate({ id: editingStaff.id, updatedFields: formData }); } else { addStaffMutate.mutate(formData); } }} onCancel={() => setModalOpen(false)} isLoading={addStaffMutate.status === "pending" || updateStaffMutate.status === "pending"} />
)} ); }