import { Drawer, TextInput, useMantineTheme, Group, Button, Textarea, ColorInput, Alert, ActionIcon } 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, RefreshCcw, Save } from 'react-feather'; import { COLORS_MAP, randomColor } from '~/utils'; import { createProject, getProjectByName } from '~/models/project.server'; import { requireUserId } from '~/session.server'; export const meta: MetaFunction = () => { return { title: 'New Project | WorkTimer', description: 'Create a new project. You must be logged in to do this.' }; }; export async function loader({ request }: LoaderArgs) { const userId = await requireUserId(request); if (!userId) return redirect('/projects'); return json({}); } export async function action({ request }: ActionArgs) { const userId = await requireUserId(request); const formData = await request.formData(); const name = formData.get('name'); const description = formData.get('description'); const color = formData.get('color'); if (typeof name !== 'string' || name.length === 0) { return json( { errors: { name: 'name is required', description: null, color: null } }, { status: 400 } ); } if (description && typeof description !== 'string') { return json( { errors: { name: null, description: 'Description is invalid', color: null } }, { status: 422 } ); } if (typeof color !== 'string' || color.length === 0) { return json( { errors: { name: null, description: null, color: 'color is required' } }, { status: 400 } ); } const projectWithSameName = await getProjectByName({ name, userId }); if (projectWithSameName) { return json( { errors: { name: 'A project with this name already exists', description: null, color: null } }, { status: 409 } ); } const project = await createProject({ name, description, color, userId }); return redirect(`/projects`); } const LayoutWrapper = ({ children }: React.PropsWithChildren<{}>) => { const theme = useMantineTheme(); const navigate = useNavigate(); return ( { navigate('/projects'); }} > {children} ); }; export default function NewProjectPage() { const data = useLoaderData(); const actionData = useActionData(); const theme = useMantineTheme(); const nameRef = React.useRef(null); const descriptionRef = React.useRef(null); const colorRef = React.useRef(null); const [color, setColor] = React.useState<{ name: string; hex: string; }>(randomColor()); React.useEffect(() => { if (actionData?.errors?.name) { nameRef.current?.focus(); } else if (actionData?.errors?.description) { descriptionRef.current?.focus(); } else if (actionData?.errors?.color) { colorRef.current?.focus(); } }, [actionData]); return (