Files
reservas/resources/js/pages/reservations/create.tsx

276 lines
13 KiB
TypeScript

import { Head, Link, useForm } from '@inertiajs/react';
import {
ArrowLeft,
CalendarClock,
Check,
MapPin,
PackageCheck,
} from 'lucide-react';
import type { FormEvent } from 'react';
import InputError from '@/components/input-error';
import { PageHeading } from '@/components/page-heading';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Checkbox } from '@/components/ui/checkbox';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import type { Asset } from '@/types';
export default function CreateReservation({
assets,
selectedAssetId,
}: {
assets: Asset[];
selectedAssetId: number;
}) {
const form = useForm({
purpose: '',
usage_location: '',
starts_at: '',
ends_at: '',
asset_ids: selectedAssetId ? [selectedAssetId] : ([] as number[]),
terms_accepted: false,
});
function submit(event: FormEvent) {
event.preventDefault();
form.post('/requisicoes');
}
function toggleAsset(id: number, checked: boolean) {
form.setData(
'asset_ids',
checked
? [...form.data.asset_ids, id]
: form.data.asset_ids.filter((item) => item !== id),
);
}
return (
<>
<Head title="Nova requisição" />
<main className="mx-auto flex w-full max-w-5xl flex-1 flex-col gap-7 p-5 sm:p-8">
<PageHeading
eyebrow="Novo pedido"
title="Criar requisição"
description="Selecione o período, os equipamentos e indique como serão utilizados."
action={
<Button asChild variant="ghost">
<Link href="/equipamentos">
<ArrowLeft /> Voltar ao catálogo
</Link>
</Button>
}
/>
<form
onSubmit={submit}
className="grid gap-6 lg:grid-cols-[1fr_320px]"
>
<div className="space-y-6">
<Card className="border-slate-200 shadow-none dark:border-slate-800">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<CalendarClock className="size-5 text-primary" />{' '}
Período e utilização
</CardTitle>
</CardHeader>
<CardContent className="grid gap-5 sm:grid-cols-2">
<Field
label="Início"
error={form.errors.starts_at}
>
<Input
type="datetime-local"
value={form.data.starts_at}
onChange={(e) =>
form.setData(
'starts_at',
e.target.value,
)
}
/>
</Field>
<Field label="Fim" error={form.errors.ends_at}>
<Input
type="datetime-local"
value={form.data.ends_at}
onChange={(e) =>
form.setData(
'ends_at',
e.target.value,
)
}
/>
</Field>
<div className="sm:col-span-2">
<Field
label="Local de utilização"
error={form.errors.usage_location}
>
<Input
value={form.data.usage_location}
onChange={(e) =>
form.setData(
'usage_location',
e.target.value,
)
}
placeholder="Ex.: Auditório principal"
/>
</Field>
</div>
<div className="sm:col-span-2">
<Label htmlFor="purpose">Finalidade</Label>
<textarea
id="purpose"
value={form.data.purpose}
onChange={(e) =>
form.setData(
'purpose',
e.target.value,
)
}
rows={4}
placeholder="Descreva brevemente a atividade e a necessidade do equipamento…"
className="mt-2 w-full rounded-md border bg-transparent px-3 py-2 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring"
/>
<InputError message={form.errors.purpose} />
</div>
</CardContent>
</Card>
<Card className="border-slate-200 shadow-none dark:border-slate-800">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<PackageCheck className="size-5 text-primary" />{' '}
Equipamentos
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
{assets.map((asset) => {
const checked =
form.data.asset_ids.includes(asset.id);
return (
<label
key={asset.id}
className={`flex cursor-pointer items-start gap-3 rounded-lg border p-4 transition ${checked ? 'border-primary bg-primary/5' : 'hover:bg-slate-50 dark:hover:bg-slate-900'}`}
>
<Checkbox
checked={checked}
onCheckedChange={(value) =>
toggleAsset(
asset.id,
Boolean(value),
)
}
/>
<div className="min-w-0 flex-1">
<div className="flex justify-between gap-3">
<span className="font-medium">
{asset.name}
</span>
<span className="font-mono text-xs text-muted-foreground">
{asset.asset_tag}
</span>
</div>
<p className="mt-1 text-xs text-muted-foreground">
{asset.category_name} ·{' '}
{asset.model_name}
</p>
{asset.location_name && (
<p className="mt-2 flex items-center gap-1 text-xs text-muted-foreground">
<MapPin className="size-3" />
{asset.location_name}
</p>
)}
</div>
</label>
);
})}
<InputError message={form.errors.asset_ids} />
</CardContent>
</Card>
</div>
<aside>
<Card className="sticky top-6 border-slate-200 shadow-none dark:border-slate-800">
<CardHeader>
<CardTitle>Resumo</CardTitle>
</CardHeader>
<CardContent className="space-y-5">
<div>
<p className="text-xs text-muted-foreground">
Equipamentos selecionados
</p>
<p className="mt-1 text-2xl font-semibold">
{form.data.asset_ids.length}
</p>
</div>
<div className="flex items-start gap-3 border-t pt-5">
<Checkbox
id="terms"
checked={form.data.terms_accepted}
onCheckedChange={(value) =>
form.setData(
'terms_accepted',
Boolean(value),
)
}
/>
<Label
htmlFor="terms"
className="text-xs leading-5 font-normal"
>
Confirmo que conheço as condições de
utilização e que entregarei os
equipamentos no estado e prazo
acordados.
</Label>
</div>
<InputError
message={form.errors.terms_accepted}
/>
<Button
type="submit"
className="w-full"
disabled={form.processing}
>
<Check />{' '}
{form.processing
? 'A submeter…'
: 'Submeter para aprovação'}
</Button>
<p className="text-center text-xs text-muted-foreground">
O equipamento fica pré-reservado até à
decisão.
</p>
</CardContent>
</Card>
</aside>
</form>
</main>
</>
);
}
function Field({
label,
error,
children,
}: {
label: string;
error?: string;
children: React.ReactNode;
}) {
return (
<div>
<Label>{label}</Label>
<div className="mt-2">{children}</div>
<InputError message={error} />
</div>
);
}
CreateReservation.layout = {
breadcrumbs: [
{ title: 'Requisições', href: '/requisicoes' },
{ title: 'Nova', href: '/requisicoes/nova' },
],
};