feat: complete administration and reporting

This commit is contained in:
2026-08-04 17:40:52 +01:00
parent d829f34580
commit 7a4ac48aaf
42 changed files with 2595 additions and 76 deletions

View File

@@ -0,0 +1,112 @@
import { Head, useForm } from '@inertiajs/react';
import { Clock3, Save, ShieldCheck } 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 { Input } from '@/components/ui/input';
type Definition = {
label: string;
description: string;
default: number;
min: number;
max: number;
unit: string;
};
export default function ReservationPolicyPage({
definitions,
values,
}: {
definitions: Record<string, Definition>;
values: Record<string, number>;
}) {
const form = useForm(values);
function submit(event: FormEvent) {
event.preventDefault();
form.put('/administracao/regras', { preserveScroll: true });
}
return (
<>
<Head title="Regras de reserva" />
<main className="mx-auto flex w-full max-w-5xl flex-1 flex-col gap-7 p-5 sm:p-8">
<PageHeading
eyebrow="Administração"
title="Regras de reserva"
description="Parâmetros aplicados centralmente a novos pedidos, conflitos temporais e prazos de aprovação."
/>
<div className="flex gap-3 rounded-xl border border-sky-200 bg-sky-50 p-4 text-sm text-sky-900 dark:bg-sky-950/30 dark:text-sky-100">
<ShieldCheck className="mt-0.5 size-5 shrink-0" />
<p>
As margens já gravadas permanecem associadas às reservas
existentes. Alterações entram em vigor imediatamente
para novas requisições.
</p>
</div>
<form onSubmit={submit} className="space-y-5">
<div className="grid gap-4 md:grid-cols-2">
{Object.entries(definitions).map(
([key, definition]) => (
<Card key={key} className="gap-4 shadow-none">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-base">
<Clock3 className="size-4 text-primary" />
{definition.label}
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
<p className="min-h-10 text-sm text-muted-foreground">
{definition.description}
</p>
<div className="flex items-center gap-3">
<Input
type="number"
min={definition.min}
max={definition.max}
value={form.data[key]}
onChange={(event) =>
form.setData(
key,
Number(
event.target.value,
),
)
}
/>
<span className="min-w-24 text-sm text-muted-foreground">
{definition.unit}
</span>
</div>
<InputError
message={form.errors[key]}
/>
<p className="text-xs text-muted-foreground">
Intervalo permitido:{' '}
{definition.min}–{definition.max}
</p>
</CardContent>
</Card>
),
)}
</div>
<Button type="submit" disabled={form.processing}>
<Save /> Guardar e aplicar regras
</Button>
</form>
</main>
</>
);
}
ReservationPolicyPage.layout = {
breadcrumbs: [
{ title: 'Administração', href: '/administracao/regras' },
{ title: 'Regras de reserva', href: '/administracao/regras' },
],
};