import { useEffect, useMemo, useState, useDeferredValue } from "react"; import { View, Text, ScrollView, GestureResponderEvent } from "react-native"; import { LoadingSpinner } from "@/components/ui/LoadingSpinner"; import { NoData } from "@/components/ui/NoData"; import { useGetUsersQuery, useCreateUserMutation, useUpdateUserMutation, useDeleteUserMutation, useResendInvitationMutation, } from "@/store/api/usersApi"; import { AdminAccountsTableControls } from "@/components/features/admins/AdminAccountsTableControls"; import { DesktopAdminAccountsTable } from "@/components/features/admins/DesktopAdminAccountsTable"; import { MobileAdminAccountsTable } from "@/components/features/admins/MobileAdminAccountsTable"; import { TablePagination } from "@/components/shared/TablePagination"; import { useAppSelector } from "@/store/hooks"; import { selectIsMobile } from "@/store/screen/screenSizeSlice"; import { TableSortDirection } from "@/constants/tableSortDirectionEnum"; import { AdminModal } from "@/components/features/admins/AdminModal"; import { OperationMode } from "@/constants/operationModeEnum"; import { DeleteUserModal } from "@/components/features/users/DeleteUserModal"; import { User } from "@/store/models/User.model"; import { AppSnackbar } from "@/components/ui/AppSnackbar"; interface AdminAccountsTableProps { initialSearchQuery?: string; } export const AdminAccountsTable = ({ initialSearchQuery = "", }: AdminAccountsTableProps) => { const [page, setPage] = useState(0); const [itemsPerPage, setItemsPerPage] = useState(25); const [searchQuery, setSearchQuery] = useState(""); const [sortColumn, setSortColumn] = useState(null); const [sortDirection, setSortDirection] = useState( TableSortDirection.ASCENDING ); const deferredSearch = useDeferredValue(searchQuery); const [debouncedSearch, setDebouncedSearch] = useState(""); const queryArgs = useMemo(() => { const allowedSorts = ["name", "email", "title", "role", "risk"] as const; const sortBy = (allowedSorts as readonly string[]).includes( sortColumn || "" ) ? (sortColumn as (typeof allowedSorts)[number]) : undefined; const sortDir = sortDirection; const searchParam = debouncedSearch && debouncedSearch.length >= 2 ? debouncedSearch : undefined; return { roleType: "admin" as const, page: page + 1, limit: itemsPerPage, search: searchParam, sortBy, sortDir, }; }, [page, itemsPerPage, debouncedSearch, sortColumn, sortDirection]); const { data: adminsPageData, isLoading, error: fetchError, refetch, isFetching, } = useGetUsersQuery(queryArgs, { refetchOnMountOrArgChange: true }); const adminUsers = adminsPageData?.items ?? []; const [createAdmin, { isLoading: isCreating }] = useCreateUserMutation(); const [updateAdmin, { isLoading: isUpdating }] = useUpdateUserMutation(); const [deleteAdmin, { isLoading: isDeleting }] = useDeleteUserMutation(); const [resendInvitation, { isLoading: isResending }] = useResendInvitationMutation(); const [menuVisible, setMenuVisible] = useState(false); const [menuAnchor, setMenuAnchor] = useState({ x: 0, y: 0 }); const [addAdminModalVisible, setAddAdminModalVisible] = useState(false); const [editModalVisible, setEditModalVisible] = useState(false); const [deleteModalVisible, setDeleteModalVisible] = useState(false); const [currentAdmin, setCurrentAdmin] = useState(null); const [snackbarMessage, setSnackbarMessage] = useState(""); const [snackbarVisible, setSnackbarVisible] = useState(false); const showSnackbar = (message: string) => { setSnackbarMessage(message); setSnackbarVisible(true); }; const totalItems = adminsPageData?.total ?? 0; const from = adminUsers.length ? page * itemsPerPage + 1 : 0; const to = adminUsers.length ? Math.min(from + adminUsers.length - 1, totalItems) : 0; const isMobile = useAppSelector(selectIsMobile); const handleSearch = (query: string) => { setSearchQuery(query); setPage(0); }; const handleSort = (column: string) => { setSortColumn(column); setSortDirection( sortDirection === TableSortDirection.ASCENDING ? TableSortDirection.DESCENDING : TableSortDirection.ASCENDING ); setPage(0); }; const handleItemsPerPageChange = (count: number) => { setItemsPerPage(count); }; const handleCreateAdmin = async (admin: Partial) => { try { await createAdmin(admin).unwrap(); showSnackbar( `${admin.firstName} ${admin.lastName} created. Invitation email sent if no password was provided.` ); setAddAdminModalVisible(false); } catch { showSnackbar("Error creating admin"); } }; const handleUpdateAdmin = async (admin: Partial) => { try { await updateAdmin(admin).unwrap(); showSnackbar(`${admin.firstName} ${admin.lastName} updated successfully`); setEditModalVisible(false); } catch { showSnackbar("Error updating admin"); } }; const handleDeleteAdmin = async (admin: User) => { try { await deleteAdmin(admin.id).unwrap(); showSnackbar(`${admin.firstName} ${admin.lastName} deleted successfully`); setDeleteModalVisible(false); } catch { showSnackbar("Error deleting admin"); } }; const handleResendInvite = async (admin: User) => { try { await resendInvitation(admin.id).unwrap(); showSnackbar(`Invitation resent to ${admin.email}`); } catch { showSnackbar("Error resending invitation"); } }; const handleEdit = (admin: User) => { setCurrentAdmin(admin); setEditModalVisible(true); }; const openDeleteModal = (admin: User) => { setCurrentAdmin(admin); setDeleteModalVisible(true); }; useEffect(() => { const t = setTimeout(() => setDebouncedSearch(deferredSearch.trim()), 450); return () => clearTimeout(t); }, [deferredSearch]); useEffect(() => { const q = (initialSearchQuery || "").trim(); if (q && q !== searchQuery) { setSearchQuery(q); setPage(0); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [initialSearchQuery]); useEffect(() => { setPage(0); }, [itemsPerPage]); return ( <> Admins manager {isLoading ? ( Loading admins... ) : fetchError ? ( ) : ( setMenuVisible(false)} onItemsPerPageChange={handleItemsPerPageChange} onAddAdmin={() => setAddAdminModalVisible(true)} /> setAddAdminModalVisible(false)} onSubmit={handleCreateAdmin} isLoading={isCreating} mode={OperationMode.ADD} /> setEditModalVisible(false)} onSubmit={handleUpdateAdmin} isLoading={isUpdating} mode={OperationMode.EDIT} /> setDeleteModalVisible(false)} onDelete={handleDeleteAdmin} isLoading={isDeleting} /> {searchQuery && adminUsers.length === 0 ? ( No admins match your search for "{searchQuery}" ) : searchQuery.length === 0 && adminUsers.length === 0 ? ( No admins found ) : isMobile ? ( ) : ( )} { const { pageX, pageY } = event.nativeEvent; setMenuAnchor({ x: pageX, y: pageY }); setMenuVisible(true); }} /> )} setSnackbarVisible(false)} action={{ label: "Close", onPress: () => setSnackbarVisible(false) }} > {snackbarMessage} ); };