import React, { useState, useEffect } from "react"; import { useQuery, useMutation } from "@tanstack/react-query"; 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"; export default function SettingsPage() { const { toast } = useToast(); const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); // Modal and editing staff state const [modalOpen, setModalOpen] = useState(false); const [credentialModalOpen, setCredentialModalOpen] = useState(false); const [editingStaff, setEditingStaff] = useState(null); const toggleMobileMenu = () => setIsMobileMenuOpen((prev) => !prev); // Fetch staff data const { data: staff = [], isLoading, isError, error, } = 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, // 5 minutes cache }); // Add Staff mutation const addStaffMutate = useMutation< Staff, // Return type Error, // Error type Omit // Variables >({ mutationFn: async (newStaff: Omit) => { const res = await apiRequest("POST", "/api/staffs/", newStaff); if (!res.ok) { const errorData = await res.json().catch(() => null); throw new Error(errorData?.message || "Failed to add staff"); } return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/staffs/"] }); toast({ title: "Staff Added", description: "Staff member added successfully.", variant: "default", }); }, onError: (error: any) => { toast({ title: "Error", description: error?.message || "Failed to add staff", variant: "destructive", }); }, }); // Update Staff mutation const updateStaffMutate = useMutation< Staff, Error, { id: number; updatedFields: Partial } >({ mutationFn: async ({ id, updatedFields, }: { id: number; updatedFields: Partial; }) => { const res = await apiRequest("PUT", `/api/staffs/${id}`, updatedFields); if (!res.ok) { const errorData = await res.json().catch(() => null); throw new Error(errorData?.message || "Failed to update staff"); } return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/staffs/"] }); toast({ title: "Staff Updated", description: "Staff member updated successfully.", variant: "default", }); }, onError: (error: any) => { toast({ title: "Error", description: error?.message || "Failed to update staff", variant: "destructive", }); }, }); // Delete Staff mutation const deleteStaffMutation = useMutation({ mutationFn: async (id: number) => { const res = await apiRequest("DELETE", `/api/staffs/${id}`); if (!res.ok) { const errorData = await res.json().catch(() => null); throw new Error(errorData?.message || "Failed to delete staff"); } return id; }, onSuccess: () => { setIsDeleteStaffOpen(false); queryClient.invalidateQueries({ queryKey: ["/api/staffs/"] }); toast({ title: "Staff Removed", description: "Staff member deleted.", variant: "default", }); }, onError: (error: any) => { toast({ title: "Error", description: error?.message || "Failed to delete staff", variant: "destructive", }); }, }); // Extract mutation states for modal control and loading const isAdding = addStaffMutate.status === "pending"; const isAddSuccess = addStaffMutate.status === "success"; const isUpdating = updateStaffMutate.status === "pending"; const isUpdateSuccess = updateStaffMutate.status === "success"; // Open Add modal const openAddStaffModal = () => { setEditingStaff(null); setModalOpen(true); }; // Open Edit modal const openEditStaffModal = (staff: Staff) => { setEditingStaff(staff); setModalOpen(true); }; // Handle form submit for Add or Edit const handleFormSubmit = (formData: Omit) => { if (editingStaff) { // Editing existing staff if (editingStaff.id === undefined) { toast({ title: "Error", description: "Staff ID is missing", variant: "destructive", }); return; } updateStaffMutate.mutate({ id: editingStaff.id, updatedFields: formData, }); } else { addStaffMutate.mutate(formData); } }; const handleModalCancel = () => { setModalOpen(false); }; // Close modal on successful add/update useEffect(() => { if (isAddSuccess || isUpdateSuccess) { setModalOpen(false); } }, [isAddSuccess, isUpdateSuccess]); const [isDeleteStaffOpen, setIsDeleteStaffOpen] = useState(false); const [currentStaff, setCurrentStaff] = useState( undefined, ); const handleDeleteStaff = (staff: Staff) => { setCurrentStaff(staff); setIsDeleteStaffOpen(true); }; const handleConfirmDeleteStaff = async () => { if (currentStaff?.id) { deleteStaffMutation.mutate(currentStaff.id); } else { toast({ title: "Error", description: "No Staff selected for deletion.", variant: "destructive", }); } }; const handleViewStaff = (staff: Staff) => alert( `Viewing staff member:\n${staff.name} (${staff.email || "No email"})`, ); // MANAGE USERS (admin only) const [newUsername, setNewUsername] = useState(""); const [newPassword, setNewPassword] = useState(""); 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, // loaded lazily below }); const { user: currentUser } = useAuth(); const isAdmin = currentUser?.username === "admin"; 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 err = await res.json().catch(() => null); throw new Error(err?.error || "Failed to create user"); } return res.json(); }, onSuccess: () => { setNewUsername(""); setNewPassword(""); refetchUsers(); toast({ title: "User Created", description: "New user added successfully." }); }, onError: (err: any) => { toast({ title: "Error", description: err?.message || "Failed to create user", 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", description: "User removed successfully." }); }, onError: (err: any) => { toast({ title: "Error", description: err?.message || "Failed to delete user", variant: "destructive" }); }, }); // MANAGE USER (own account) const [usernameUser, setUsernameUser] = useState(""); useEffect(() => { if (currentUser?.username) { setUsernameUser(currentUser.username); } }, [currentUser]); //update user mutation 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 errorData = await res.json().catch(() => null); throw new Error(errorData?.error || "Failed to update user"); } return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/users/"] }); toast({ title: "Updated", description: "Your profile has been updated.", variant: "default", }); }, onError: (err: any) => { toast({ title: "Error", description: err?.message || "Failed to update user", variant: "destructive", }); }, }); return (
{isError && (

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

)} setIsDeleteStaffOpen(false)} entityName={currentStaff?.name} />
{/* Modal Overlay */} {modalOpen && (

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

)} {/* Manage Users section (admin only) */} {isAdmin && (

Manage Users

{/* Existing users list */}
{allUsers.length === 0 && (

No users found.

)} {allUsers.map((u) => (
{u.username} {u.username !== "admin" && ( )}
))}
{/* Add new user form */}
{ 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="mt-1 p-2 border rounded w-full" placeholder="••••••••" required />
)} {/* User Setting section */}

User Settings

{ e.preventDefault(); const formData = new FormData(e.currentTarget); const password = formData.get("password")?.toString().trim() || undefined; updateUserMutate.mutate({ username: usernameUser?.trim() || undefined, password: password || undefined, }); }} >
setUsernameUser(e.target.value)} className="mt-1 p-2 border rounded w-full" />

Leave blank to keep current password.

{/* Credential Section */}
{/* NpiProvider Section */}
); }