import { Alert, Box, Button, ColorSwatch, Flex, Group, NumberInput, Paper, Table, Title } from '@mantine/core'; import { MetaFunction, LoaderArgs, redirect, json } from '@remix-run/node'; import { Link, useFetcher, useLoaderData, useNavigate } from '@remix-run/react'; import { getTimeEntriesByDateAndProject, updateDuration } from '~/models/timeEntry.server'; import { getProjects, Project } from '~/models/project.server'; import { requireUser } from '~/session.server'; import { DateRangePicker, DateRangePickerValue } from '@mantine/dates'; import { useEffect, useState } from 'react'; import dayjs from 'dayjs'; import { Calendar } from 'react-feather'; import { useMediaQuery } from '@mantine/hooks'; import 'dayjs/locale/it'; export const meta: MetaFunction = () => { return { title: 'Reports | WorkTimer', description: 'Generate reports of your time entries. You must be logged in to do this.' }; }; export async function loader({ request }: LoaderArgs) { const user = await requireUser(request); if (!user) return redirect('/login'); const url = new URL(request.url); const dateFrom = url.searchParams.get('dateFrom') ? dayjs(url.searchParams.get('dateFrom')).startOf('day').toDate() : dayjs().startOf('month').startOf('day').toDate(); const dateTo = url.searchParams.get('dateTo') ? dayjs(url.searchParams.get('dateTo')).endOf('day').toDate() : dayjs().endOf('month').endOf('day').toDate(); await updateDuration(user.id); return json({ user, timeByProject: await getTimeEntriesByDateAndProject({ userId: user.id, dateFrom, dateTo }), projects: await getProjects({ userId: user.id }) }); } export default function ReportPage() { const data = useLoaderData(); const reports = useFetcher(); const [dateRange, setDateRange] = useState([ dayjs().startOf('month').startOf('day').toDate(), dayjs().endOf('month').endOf('day').toDate() ]); useEffect(() => { if (dateRange[0] && dateRange[1]) { reports.load( `/reports?dateFrom=${dayjs(dateRange[0]).format( 'YYYY-MM-DD' )}&dateTo=${dayjs(dateRange[1]).format('YYYY-MM-DD')}` ); } }, [dateRange]); const [hourlyRate, setHourlyRate] = useState( data.user.defaultHourlyRate || undefined ); const mobile = useMediaQuery('(max-width: 600px)'); return ( <>

Reports

} mb="md" /> Time per project {reports.data && ( {hourlyRate && } {( Object.values(reports.data.timeByProject ?? {}) as { projectId: string; _sum: { duration: number }; }[] ).map((projectData) => ( {hourlyRate && ( )} ))}
Project TimeBilling
p.id === projectData.projectId )?.color ?? '#000' } /> {reports.data?.projects?.projects?.find( (p) => p.id === projectData.projectId )?.name ?? 'No project'} {(projectData._sum.duration / 1000 / 60 / 60).toFixed(2)} h {( (projectData._sum.duration * hourlyRate) / 1000 / 60 / 60 ).toFixed(2)}{' '} {reports.data?.user?.currency ?? '€'}
)} ); }