import { useMemo } from 'react'; import { Button, Paper, Text, Menu, ActionIcon, Pagination, NativeSelect, Group, useMantineTheme, Alert, ThemeIcon, Table, Indicator, Tooltip, TextInput } from '@mantine/core'; import { json, LoaderArgs, MetaFunction, redirect } from '@remix-run/node'; import { Form, Link, Outlet, useCatch, useLoaderData, useSearchParams } from '@remix-run/react'; import { AlertTriangle, Edit3, Key, Plus, Search, Settings, Trash, User as UserIcon, X, XCircle } from 'react-feather'; import { requireUser } from '~/session.server'; import { getUsers, User } from '~/models/user.server'; export const meta: MetaFunction = () => { return { title: 'Users | WorkTimer', description: 'Manage your users. You must be logged in as admin to do this.' }; }; export async function loader({ request }: LoaderArgs) { const user = await requireUser(request); if (!user || !user.admin) return redirect('/login'); const url = new URL(request.url); const page = url.searchParams.get('page') ? parseInt(url.searchParams.get('page')!, 10) : 1; const size = url.searchParams.get('size') ? parseInt(url.searchParams.get('size')!, 10) : 25; const orderBy = url.searchParams.get('orderBy') || 'createdAt'; const order = url.searchParams.get('order') || 'desc'; const search = url.searchParams.get('search') || undefined; return json({ user, ...(await getUsers({ search, page, size, orderBy, order: order === 'asc' ? 'asc' : 'desc' })) }); } export default function Users() { const data = useLoaderData(); const [searchParams, setSearchParams] = useSearchParams(); const theme = useMantineTheme(); const pageSize = useMemo(() => { return parseInt(searchParams.get('size') || '25', 10); }, [searchParams]); const page = useMemo(() => { return parseInt(searchParams.get('page') || '1', 10); }, [searchParams]); return (

Users

{ setSearchParams({ page: page.toString(), size: event.currentTarget.value }); }} /> {data.total / pageSize > 1 && ( { setSearchParams({ page: page.toString(), size: pageSize.toString() }); }} /> )}
} rightSection={ setSearchParams((sp) => ({ ...sp, search: '' }))} > } value={searchParams.get('search') || ''} onChange={(event) => { setSearchParams({ search: event.currentTarget.value }); }} />
{data.total} users {data.total !== data.filteredTotal && ( <> | {data.filteredTotal} matches )} {data.users.map((user) => ( ))}
Email Created Actions
{user.id === data.user.id ? ( {user.admin ? ( ) : ( )} ) : ( {user.admin ? ( ) : ( )} )} {user.email} {Intl.DateTimeFormat(data.user.dateFormat, { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }).format(new Date(user.createdAt))}
); } export function ErrorBoundary({ error }: { error: Error }) { console.error(error); return ( } title="Error" color="red"> An unexpected error occurred: {error.message} ); } export function CatchBoundary() { const caught = useCatch(); if (caught.status === 404) { return ( } title="Error" color="red"> Not found ); } throw new Error(`Unexpected caught response with status: ${caught.status}`); }