import { Head, Link, useForm } from '@inertiajs/react'; import { PageTitle, Panel, FormErrors } from '@/components/procurement'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { date } from '@/types/procurement'; type Option = { id: number; name: string }; type RequestOption = { id: number; number: string; department_id: number }; type Item = { id: number; title: string; nature: string; department: string; department_id: number; canLink: boolean; responsible: string; responsible_id: number; version: number; canRecord: boolean; requisition: { id: number; number: string } | null; }; type Milestone = { id: number; purchase_plan_item_id: number; label: string; planned_on: string; actual_on: string | null; version: number; }; type Audit = { id: number; action: string; created_at: string; actor: string; before: Record | null; after: { reason?: string; milestone?: { planned_on: string; actual_on: string | null }; }; }; type Props = { items: { data: Item[]; prev_page_url: string | null; next_page_url: string | null; }; milestones: Milestone[]; history: Audit[]; users: Option[]; departments: Option[]; requisitions: RequestOption[]; canManage: boolean; today: string; }; const natureLabels: Record = { goods: 'Bens', works: 'Obras', services: 'Serviços', consulting: 'Consultoria', }; const selectClass = 'block h-10 w-full rounded-md border bg-background px-3 text-sm'; function CreateItem({ users, departments, requisitions, }: Pick) { const form = useForm({ title: '', nature: '', department_id: '', responsible_id: '', requisition_id: '', }); return (
{ event.preventDefault(); form.post('/planeamento', { preserveScroll: true, onSuccess: () => form.reset(), }); }} className="grid gap-4 sm:grid-cols-2" > ); } function LinkRequest({ item, requisitions, }: { item: Item; requisitions: RequestOption[]; }) { const form = useForm({ requisition_id: '', version: item.version, reason: '', }); return (
{ event.preventDefault(); form.post(`/planeamento/${item.id}/requisicao`, { preserveScroll: true, }); }} className="mt-4 grid gap-3" > ); } function AddMilestone({ id }: { id: number }) { const form = useForm({ label: '', planned_on: '' }); return (
{ event.preventDefault(); form.post(`/planeamento/${id}/marcos`, { preserveScroll: true, onSuccess: () => form.reset(), }); }} className="mt-4 grid gap-3 sm:grid-cols-2" > ); } function Assign({ item, users }: { item: Item; users: Option[] }) { const form = useForm({ responsible_id: String(item.responsible_id), version: item.version, reason: '', }); return (
{ event.preventDefault(); form.post(`/planeamento/${item.id}/responsavel`, { preserveScroll: true, }); }} className="mt-4 grid gap-3" > ); } function MilestoneRow({ milestone, canManage, canRecord, today, }: { milestone: Milestone; canManage: boolean; canRecord: boolean; today: string; }) { const form = useForm<{ version: number; actual_on: string; reason: string; planned_on?: string; }>({ version: milestone.version, actual_on: milestone.actual_on ?? '', reason: '', ...(canManage ? { planned_on: milestone.planned_on } : {}), }); const delta = milestone.actual_on ? Math.round( (Date.parse(`${milestone.actual_on}T00:00:00Z`) - Date.parse(`${milestone.planned_on}T00:00:00Z`)) / 86400000, ) : null; const status = delta !== null ? delta > 0 ? `Realizado com ${delta} dias de atraso` : 'Realizado dentro do prazo' : milestone.planned_on < today ? 'Data prevista ultrapassada' : milestone.planned_on === today ? 'Previsto para hoje' : 'Previsto'; return (
  • {milestone.label}

    {status}

    Previsto: {date(milestone.planned_on)} · Realizado:{' '} {milestone.actual_on ? date(milestone.actual_on) : 'Não registado'}

    {canRecord && (
    Actualizar datas
    { event.preventDefault(); form.patch(`/planeamento/marcos/${milestone.id}`, { preserveScroll: true, }); }} className="mt-3 grid gap-3 sm:grid-cols-2" > {canManage && ( )}

    Deixar a data realizada vazia remove essa indicação, conservando o valor anterior no histórico.

    )}
  • ); } export default function Planning({ items, milestones, history, users, departments, requisitions, canManage, today, }: Props) { return ( <>

    Os marcos são definidos para cada compra. Registar uma data realizada não aprova a compra, valida o orçamento, emite documentos ou confirma entregas e pagamentos.

    {canManage && ( )} {items.data.map((item) => (
    {item.requisition ? ( Processo {item.requisition.number} ) : (

    Sem requisição visível associada

    )} {canManage && (
    Alterar responsável
    )}
    {item.canLink && (
    Ligar uma requisição criada posteriormente
    )}
      {milestones .filter( (milestone) => milestone.purchase_plan_item_id === item.id, ) .map((milestone) => ( ))}
    {!milestones.some( (milestone) => milestone.purchase_plan_item_id === item.id, ) && (

    Sem marcos definidos.

    )} {canManage && (
    Adicionar marco
    )}
    ))} {!items.data.length && (

    Ainda não existem itens disponíveis para o seu perfil.

    )} {canManage && ( {history.length ? (
    {history.map((event) => (
    {event.action === 'planning.created' ? 'Compra adicionada' : event.action === 'planning.assigned' ? 'Responsável alterado' : event.action === 'planning.milestone_created' ? 'Marco criado' : 'Datas actualizadas'}{' '} · {date(event.created_at)}

    Por {event.actor}

    {typeof event.after.reason === 'string' ? event.after.reason : 'Registo inicial'}

    {event.before && (

    Antes: previsto{' '} {String( event.before.planned_on ?? '—', )} , realizado{' '} {String( event.before.actual_on ?? '—', )} , responsável{' '} {String( event.before.responsible_id ? (users.find( (user) => user.id === event.before ?.responsible_id, )?.name ?? 'Indisponível') : '—', )} {event.after.milestone && (

    Depois: previsto{' '} {date( event.after .milestone .planned_on, )} , realizado{' '} {event.after.milestone .actual_on ? date( event.after .milestone .actual_on, ) : 'Não registado'}

    )}

    )}
    ))}
    ) : (

    Sem alterações registadas.

    )}
    )}
    ); } Planning.layout = { breadcrumbs: [ { title: 'Compras', href: '/requisicoes' }, { title: 'Planeamento', href: '/planeamento' }, ], };