commit21
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { Head, Link, router } from '@inertiajs/react';
|
||||
import { PageTitle, Panel } from '@/components/procurement';
|
||||
import { Head, Link, router, useForm } from '@inertiajs/react';
|
||||
import { PageTitle, Panel, controlClass } from '@/components/procurement';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { roleLabels, statusLabels } from '@/types/procurement';
|
||||
|
||||
@@ -7,7 +7,9 @@ type Props = {
|
||||
ready: boolean;
|
||||
unread: number;
|
||||
view: string;
|
||||
search: string;
|
||||
notices: {
|
||||
current_page: number;
|
||||
total: number;
|
||||
data: {
|
||||
id: number;
|
||||
@@ -24,7 +26,14 @@ type Props = {
|
||||
next_page_url: string | null;
|
||||
};
|
||||
};
|
||||
export default function Notices({ ready, unread, view, notices }: Props) {
|
||||
export default function Notices({
|
||||
ready,
|
||||
unread,
|
||||
view,
|
||||
search,
|
||||
notices,
|
||||
}: Props) {
|
||||
const form = useForm({ view, search });
|
||||
return (
|
||||
<>
|
||||
<Head title="Avisos de aprovação" />
|
||||
@@ -61,6 +70,56 @@ export default function Notices({ ready, unread, view, notices }: Props) {
|
||||
}
|
||||
description="Intervenções concluídas deixam a fila pendente. Resultados registados mantêm-se visíveis ao requisitante com acesso actual. Não são criados avisos retroactivos para decisões antigas."
|
||||
>
|
||||
<details
|
||||
className="mb-4 rounded border p-3"
|
||||
open={Boolean(search)}
|
||||
>
|
||||
<summary className="cursor-pointer text-sm">
|
||||
Localizar por referência da requisição
|
||||
</summary>
|
||||
<form
|
||||
className="mt-3 flex flex-wrap items-end gap-3"
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault();
|
||||
form.transform((data) => ({
|
||||
...data,
|
||||
view,
|
||||
}));
|
||||
form.get('/requisicoes/avisos', {
|
||||
preserveState: true,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<label className="text-sm">
|
||||
Referência
|
||||
<input
|
||||
className={controlClass}
|
||||
maxLength={100}
|
||||
value={form.data.search}
|
||||
onChange={(event) =>
|
||||
form.setData(
|
||||
'search',
|
||||
event.target.value,
|
||||
)
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<Button disabled={form.processing}>Procurar</Button>
|
||||
<Link
|
||||
className="text-sm underline"
|
||||
href={`/requisicoes/avisos?view=${view}`}
|
||||
>
|
||||
Limpar pesquisa
|
||||
</Link>
|
||||
{form.errors.search && (
|
||||
<p role="alert">{form.errors.search}</p>
|
||||
)}
|
||||
</form>
|
||||
</details>
|
||||
<p className="mb-3 text-sm text-muted-foreground">
|
||||
{notices.total} aviso(s) nesta consulta · Página{' '}
|
||||
{notices.current_page}
|
||||
</p>
|
||||
{!ready ? (
|
||||
<p>Instalação dos avisos internos pendente.</p>
|
||||
) : notices.data.length === 0 ? (
|
||||
@@ -102,6 +161,11 @@ export default function Notices({ ready, unread, view, notices }: Props) {
|
||||
onClick={() =>
|
||||
router.post(
|
||||
`/requisicoes/avisos/${notice.id}/ler`,
|
||||
{
|
||||
view,
|
||||
search,
|
||||
page: notices.current_page,
|
||||
},
|
||||
)
|
||||
}
|
||||
>
|
||||
|
||||
@@ -8,7 +8,9 @@ type Props = {
|
||||
modules: Record<string, string>;
|
||||
counts: Record<string, number>;
|
||||
checkedAt: string;
|
||||
filters: { module: string; search: string };
|
||||
asOf: string;
|
||||
deadlineCounts: Record<string, number>;
|
||||
filters: { module: string; search: string; due: string };
|
||||
tasks: {
|
||||
total: number;
|
||||
data: {
|
||||
@@ -19,6 +21,7 @@ type Props = {
|
||||
blocked: boolean;
|
||||
created_at: string | null;
|
||||
due_on: string | null;
|
||||
due_status: string;
|
||||
href: string;
|
||||
}[];
|
||||
prev_page_url: string | null;
|
||||
@@ -31,11 +34,20 @@ export default function Tasks({
|
||||
modules,
|
||||
counts,
|
||||
checkedAt,
|
||||
asOf,
|
||||
deadlineCounts,
|
||||
filters,
|
||||
tasks,
|
||||
}: Props) {
|
||||
const [search, setSearch] = useState(filters.search);
|
||||
const [module, setModule] = useState(filters.module);
|
||||
const [due, setDue] = useState(filters.due);
|
||||
const deadlines: Record<string, string> = {
|
||||
overdue: 'Prazo ultrapassado',
|
||||
today: 'Vence hoje',
|
||||
upcoming: 'Prazo futuro',
|
||||
undated: 'Sem prazo registado',
|
||||
};
|
||||
const path =
|
||||
area === 'people' ? '/pessoas/tarefas' : '/requisicoes/tarefas';
|
||||
return (
|
||||
@@ -81,7 +93,7 @@ export default function Tasks({
|
||||
event.preventDefault();
|
||||
router.get(
|
||||
path,
|
||||
{ module, search },
|
||||
{ module, search, due },
|
||||
{ preserveState: true },
|
||||
);
|
||||
}}
|
||||
@@ -114,6 +126,23 @@ export default function Tasks({
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Prazo
|
||||
<select
|
||||
className={controlClass}
|
||||
value={due}
|
||||
onChange={(event) => setDue(event.target.value)}
|
||||
>
|
||||
<option value="all">Todos</option>
|
||||
{Object.entries(deadlines).map(
|
||||
([value, label]) => (
|
||||
<option key={value} value={value}>
|
||||
{label}
|
||||
</option>
|
||||
),
|
||||
)}
|
||||
</select>
|
||||
</label>
|
||||
<Button type="submit" variant="outline">
|
||||
Filtrar
|
||||
</Button>
|
||||
@@ -121,6 +150,16 @@ export default function Tasks({
|
||||
Limpar filtros
|
||||
</Link>
|
||||
</form>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
No módulo e pesquisa escolhidos, em {asOf}:{' '}
|
||||
{Object.entries(deadlines)
|
||||
.map(
|
||||
([value, label]) =>
|
||||
`${label}: ${deadlineCounts[value] ?? 0}`,
|
||||
)
|
||||
.join(' · ')}
|
||||
.
|
||||
</p>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
{tasks.total} tarefa(s) no filtro. Documentos mais
|
||||
antigos aparecem primeiro; esta ordem não define
|
||||
@@ -162,6 +201,8 @@ export default function Tasks({
|
||||
<p className="mt-2 text-sm">
|
||||
Prazo registado no processo:{' '}
|
||||
{task.due_on}
|
||||
{' · '}
|
||||
{deadlines[task.due_status]}
|
||||
</p>
|
||||
)}
|
||||
</article>
|
||||
|
||||
Reference in New Issue
Block a user