137 lines
4.0 KiB
TypeScript
137 lines
4.0 KiB
TypeScript
import {
|
|
useDeferredValue,
|
|
useEffect,
|
|
useMemo,
|
|
useState,
|
|
useCallback,
|
|
} from "react";
|
|
import { View, Text } from "react-native";
|
|
import { useFocusEffect } from "@react-navigation/native";
|
|
|
|
import { LogsTable } from "@/components/features/logs/LogsTable";
|
|
import { LoadingSpinner } from "@/components/ui/LoadingSpinner";
|
|
import { NoData } from "@/components/ui/NoData";
|
|
import { useGetUserLogsQuery } from "@/store/api/usersApi";
|
|
import { TableSortDirection } from "@/constants/tableSortDirectionEnum";
|
|
import { useAppSelector } from "@/store/hooks";
|
|
import { selectIsMobile } from "@/store/screen/screenSizeSlice";
|
|
|
|
export const LogsPage = () => {
|
|
const [page, setPage] = useState(0);
|
|
const [itemsPerPage, setItemsPerPage] = useState(25);
|
|
const [searchQuery, setSearchQuery] = useState("");
|
|
const [sortColumn, setSortColumn] = useState<string | null>(null);
|
|
const [sortDirection, setSortDirection] = useState<TableSortDirection>(
|
|
TableSortDirection.ASCENDING
|
|
);
|
|
const isMobile = useAppSelector(selectIsMobile);
|
|
|
|
const deferredSearch = useDeferredValue(searchQuery);
|
|
const [debouncedSearch, setDebouncedSearch] = useState(deferredSearch);
|
|
useEffect(() => {
|
|
const handler = setTimeout(
|
|
() => setDebouncedSearch(deferredSearch.trim()),
|
|
450
|
|
);
|
|
return () => clearTimeout(handler);
|
|
}, [deferredSearch]);
|
|
|
|
const queryArgs = useMemo(() => {
|
|
const searchParam =
|
|
debouncedSearch && debouncedSearch.length >= 2
|
|
? debouncedSearch
|
|
: undefined;
|
|
return {
|
|
page: page + 1,
|
|
limit: itemsPerPage,
|
|
search: searchParam,
|
|
} as const;
|
|
}, [page, itemsPerPage, debouncedSearch]);
|
|
|
|
const {
|
|
data: logsData,
|
|
isLoading,
|
|
isFetching,
|
|
error,
|
|
refetch,
|
|
} = useGetUserLogsQuery(queryArgs);
|
|
|
|
const logs = logsData?.items ?? [];
|
|
const totalItems = logsData?.total ?? 0;
|
|
const from = logs.length ? page * itemsPerPage + 1 : 0;
|
|
const to = logs.length ? Math.min(from + logs.length - 1, totalItems) : 0;
|
|
|
|
// Refetch whenever the screen/page gains focus
|
|
useFocusEffect(
|
|
useCallback(() => {
|
|
refetch();
|
|
}, [refetch])
|
|
);
|
|
|
|
return (
|
|
<View
|
|
className={`flex-1 p-1 ${isMobile ? "p-1" : "p-5"} bg-primaryPurpleLight`}
|
|
>
|
|
<Text
|
|
className={`text-2xl font-bold ${
|
|
isMobile ? "mb-1" : "mb-4"
|
|
} text-white`}
|
|
>
|
|
Users login activity
|
|
</Text>
|
|
<View
|
|
className={`bg-lightGray rounded-lg ${isMobile ? "p-1" : "p-5"} flex-1`}
|
|
>
|
|
{isLoading ? (
|
|
<View className="flex-1 justify-center items-center py-10">
|
|
<LoadingSpinner />
|
|
<Text className="mt-2">Loading logs...</Text>
|
|
</View>
|
|
) : error ? (
|
|
<View className="flex-1 justify-center items-center py-10">
|
|
<NoData
|
|
message="Error loading logs"
|
|
onRetry={refetch}
|
|
isRetrying={isFetching}
|
|
/>
|
|
</View>
|
|
) : (
|
|
<LogsTable
|
|
logs={logs}
|
|
page={page}
|
|
itemsPerPage={itemsPerPage}
|
|
totalItems={totalItems}
|
|
from={from}
|
|
to={to}
|
|
searchQuery={searchQuery}
|
|
onSearch={(q) => {
|
|
setSearchQuery(q);
|
|
setPage(0);
|
|
}}
|
|
sortColumn={sortColumn}
|
|
sortDirection={sortDirection}
|
|
onSort={(column) => {
|
|
if (sortColumn === column) {
|
|
setSortDirection((d) =>
|
|
d === TableSortDirection.ASCENDING
|
|
? TableSortDirection.DESCENDING
|
|
: TableSortDirection.ASCENDING
|
|
);
|
|
} else {
|
|
setSortColumn(column);
|
|
setSortDirection(TableSortDirection.ASCENDING);
|
|
}
|
|
setPage(0);
|
|
}}
|
|
onItemsPerPageChange={(value) => {
|
|
setItemsPerPage(value);
|
|
setPage(0);
|
|
}}
|
|
onPageChange={(newPage) => setPage(newPage)}
|
|
/>
|
|
)}
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|