import { Alert, Drawer, TextInput, Text, useMantineTheme, Group, Button, Textarea, Stack, Select, ColorSwatch } from '@mantine/core'; import type { ActionArgs, LoaderArgs, MetaFunction } from '@remix-run/node'; import { json, redirect } from '@remix-run/node'; import { Form, useActionData, useCatch, useLoaderData, useNavigate } from '@remix-run/react'; import * as React from 'react'; import { AlertTriangle, Play, Save } from 'react-feather'; import { getProjects } from '~/models/project.server'; import { createTimeEntry, stopAllTimeEntries } from '~/models/timeEntry.server'; import { requireUserId } from '~/session.server'; import { DatePicker, TimeInput } from '@mantine/dates'; import { forwardRef } from 'react'; import 'dayjs/locale/it'; export const meta: MetaFunction = () => { return { title: 'New Time Entry | WorkTimer', description: 'Create a new time entry. You must be logged in to do this.' }; }; export async function loader({ request }: LoaderArgs) { const userId = await requireUserId(request); return json({ ...(await getProjects({ userId })) }); } export async function action({ request }: ActionArgs) { const userId = await requireUserId(request); const formData = await request.formData(); const description = (formData.get('description') || undefined) as | string | undefined; const projectId = (formData.get('projectId') || undefined) as | string | undefined; let startTime = (formData.get('startTime') || undefined) as | string | undefined; let endTime = (formData.get('endTime') || undefined) as string | undefined; if (typeof description !== 'string' || description.length === 0) { return json( { errors: { projectId: null, description: 'Description is required', startTime: null, endTime: null } }, { status: 400 } ); } if (typeof projectId !== 'string' || projectId.length === 0) { return json( { errors: { projectId: 'projectId is required', description: null, startTime: null, endTime: null } }, { status: 400 } ); } if (typeof startTime !== 'string' || startTime.length === 0) { return json( { errors: { projectId: null, description: null, startTime: 'startTime is required', endTime: null } }, { status: 400 } ); } if ( startTime && typeof startTime === 'string' && !(startTime === 'now' || !Number.isNaN(Date.parse(startTime))) ) { return json( { errors: { projectId: null, description: null, startTime: 'startTime is invalid', endTime: null } }, { status: 422 } ); } if ( endTime && typeof endTime === 'string' && !(endTime === 'now' || !Number.isNaN(Date.parse(endTime))) ) { return json( { errors: { projectId: null, description: null, startTime: null, endTime: 'endTime is invalid' } }, { status: 422 } ); } let startDate = new Date(startTime === 'now' ? Date.now() : startTime); let endDate = endTime ? new Date(endTime === 'now' ? Date.now() : endTime) : undefined; if ( startDate && endDate && typeof startTime === 'string' && typeof endTime === 'string' && startDate > endDate ) { return json( { errors: { projectId: null, description: null, startTime: 'startTime must be before endTime', endTime: 'startTime must be before endTime' } }, { status: 422 } ); } await stopAllTimeEntries(userId); const timeEntry = await createTimeEntry({ description, startTime: startDate, endTime: endDate || null, duration: endDate ? endDate.getTime() - startDate.getTime() : null, userId, projectId }); return redirect(`/time-entries`); } interface ItemProps extends React.ComponentPropsWithoutRef<'div'> { id: string; label: string; color: string; } const SelectItem = forwardRef( ({ label, color, id, ...others }: ItemProps, ref) => (
{label}
) ); const LayoutWrapper = ({ children }: React.PropsWithChildren<{}>) => { const theme = useMantineTheme(); const navigate = useNavigate(); return ( { navigate('/time-entries'); }} > {children} ); }; export default function NewTimeEntryPage() { const actionData = useActionData(); const data = useLoaderData(); const theme = useMantineTheme(); const descriptionRef = React.useRef(null); const startDateRef = React.useRef(null); const endDateRef = React.useRef(null); const projectRef = React.useRef(null); const [start, setStart] = React.useState(new Date(Date.now())); const [end, setEnd] = React.useState(); React.useEffect(() => { if (actionData?.errors?.description) { descriptionRef.current?.focus(); } else if (actionData?.errors?.startTime) { startDateRef.current?.focus(); } else if (actionData?.errors?.endTime) { endDateRef.current?.focus(); } else if (actionData?.errors?.projectId) { projectRef.current?.focus(); } }, [actionData]); return (