326 lines
13 KiB
TypeScript
326 lines
13 KiB
TypeScript
import { Link, router } from '@inertiajs/react';
|
|
import { Field, Management, button, control } from './shared';
|
|
|
|
import { states } from '@/lib/editorial-states';
|
|
|
|
type Item = {
|
|
id: number;
|
|
title: string;
|
|
revision_no: number;
|
|
state: string;
|
|
responsible: string | null;
|
|
published: boolean;
|
|
legal_required: boolean;
|
|
};
|
|
const eventTitles: Record<string, string> = {
|
|
submitted: 'Submissão',
|
|
pending_review: 'Submissão',
|
|
review_started: 'Início da revisão',
|
|
in_review: 'Início da revisão',
|
|
returned: 'Devolução',
|
|
rejected: 'Rejeição',
|
|
approved: 'Aprovação',
|
|
published: 'Publicação',
|
|
archived: 'Arquivo',
|
|
withdrawn: 'Retirada da publicação',
|
|
publication_withdrawn: 'Retirada da publicação',
|
|
suspended: 'Suspensão da publicação',
|
|
submit: 'Submissão',
|
|
start_review: 'Início da revisão',
|
|
return: 'Devolução',
|
|
reject: 'Rejeição',
|
|
legal_validate: 'Validação jurídica',
|
|
approve: 'Aprovação',
|
|
publish: 'Publicação',
|
|
archive: 'Arquivo',
|
|
};
|
|
export default function EditorialWorkflow({
|
|
items,
|
|
notices,
|
|
filters = {},
|
|
resourceAccess,
|
|
opportunitiesUrl,
|
|
offersUrl = null,
|
|
offerOperationsUrl = null,
|
|
opportunityPending,
|
|
clustersUrl,
|
|
portfolioUrl,
|
|
portfolioPending = 0,
|
|
demographyUrl,
|
|
demographyPending = 0,
|
|
territorialUrl,
|
|
nativePending = { clusters: 0, territories: 0 },
|
|
subjectsUrl,
|
|
subjectsPending = 0,
|
|
zonesUrl,
|
|
}: {
|
|
resourceAccess: boolean;
|
|
opportunitiesUrl: string | null;
|
|
offersUrl?: string | null;
|
|
offerOperationsUrl?: string | null;
|
|
opportunityPending: number;
|
|
clustersUrl?: string | null;
|
|
portfolioUrl?: string | null;
|
|
portfolioPending?: number;
|
|
demographyUrl?: string | null;
|
|
demographyPending?: number;
|
|
territorialUrl?: string | null;
|
|
nativePending?: { clusters: number; territories: number; zones?: number };
|
|
zonesUrl?: string | null;
|
|
subjectsUrl?: string | null;
|
|
subjectsPending?: number;
|
|
filters?: { state?: string; kind?: string };
|
|
items: { data: Item[]; current_page: number; last_page: number };
|
|
notices: {
|
|
id: number;
|
|
action: string;
|
|
read_at: string | null;
|
|
href: string;
|
|
reference: string;
|
|
}[];
|
|
}) {
|
|
const pageUrl = (page: number) =>
|
|
`/management/editorial?${new URLSearchParams({ ...filters, page: String(page) })}`;
|
|
const areas = [
|
|
{
|
|
title: 'Conteúdos e oportunidades',
|
|
entries: [
|
|
{
|
|
label: 'Artigos',
|
|
href: resourceAccess
|
|
? '/management/editorial?kind=article'
|
|
: null,
|
|
},
|
|
{
|
|
label: 'Legislação',
|
|
href: resourceAccess
|
|
? '/management/editorial?kind=legislation'
|
|
: null,
|
|
},
|
|
{
|
|
label: 'Documentos',
|
|
href: resourceAccess
|
|
? '/management/editorial?kind=document'
|
|
: null,
|
|
},
|
|
{
|
|
label: 'Oportunidades',
|
|
href: opportunitiesUrl,
|
|
pending: opportunityPending,
|
|
},
|
|
{
|
|
label: 'Ofertas — revisão privada',
|
|
href: offersUrl,
|
|
},
|
|
{ label: 'Ofertas — acompanhamento', href: offerOperationsUrl },
|
|
{
|
|
label: 'Biblioteca e editores',
|
|
href: resourceAccess ? '/management/portal-services' : null,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
title: 'Investimentos e competitividade',
|
|
entries: [
|
|
{
|
|
label: 'Carteira',
|
|
href: portfolioUrl,
|
|
pending: portfolioPending,
|
|
},
|
|
{
|
|
label: 'Competitividade dos clusters',
|
|
href: clustersUrl,
|
|
pending: nativePending.clusters,
|
|
},
|
|
{
|
|
label: 'Estruturas e relações',
|
|
href: subjectsUrl,
|
|
pending: subjectsPending,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
title: 'Território e dados',
|
|
entries: [
|
|
{
|
|
label: 'Conteúdos territoriais',
|
|
href: territorialUrl,
|
|
pending: nativePending.territories,
|
|
},
|
|
{
|
|
label: 'Zonas e indicadores',
|
|
href: zonesUrl,
|
|
pending: nativePending.zones ?? 0,
|
|
},
|
|
{
|
|
label: 'Demografia',
|
|
href: demographyUrl,
|
|
pending: demographyPending,
|
|
},
|
|
],
|
|
},
|
|
].filter((area) => area.entries.some((item) => item.href));
|
|
return (
|
|
<Management title="Circuito editorial">
|
|
<p className="max-w-3xl text-sm text-slate-600">
|
|
Acompanhe a preparação, revisão e publicação numa única área de
|
|
trabalho. Seleccione uma actividade para continuar nas tarefas
|
|
da sua equipa.
|
|
</p>
|
|
<section
|
|
aria-label="Actividades do dashboard"
|
|
className="grid gap-4 md:grid-cols-2 xl:grid-cols-3"
|
|
>
|
|
{areas.map((area) => (
|
|
<section
|
|
key={area.title}
|
|
className="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm"
|
|
>
|
|
<h2 className="border-b border-slate-100 bg-slate-50 px-5 py-4 text-sm font-semibold text-slate-900">
|
|
{area.title}
|
|
</h2>
|
|
<ul className="divide-y divide-slate-100">
|
|
{area.entries.map((item) =>
|
|
item.href ? (
|
|
<li key={item.label}>
|
|
<Link
|
|
href={item.href}
|
|
className="flex min-h-14 items-center justify-between gap-3 px-5 py-3 text-sm font-medium text-slate-700 transition hover:bg-slate-50 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-red-700"
|
|
>
|
|
<span>{item.label}</span>
|
|
{'pending' in item && (
|
|
<span className="shrink-0 rounded-full bg-slate-100 px-2.5 py-1 text-xs text-slate-600">
|
|
{item.pending} pendentes
|
|
</span>
|
|
)}
|
|
</Link>
|
|
</li>
|
|
) : null,
|
|
)}
|
|
</ul>
|
|
</section>
|
|
))}
|
|
</section>
|
|
{resourceAccess && (
|
|
<>
|
|
<h2 className="text-lg font-semibold text-slate-900">
|
|
Fila de conteúdos
|
|
</h2>
|
|
<form
|
|
action="/management/editorial"
|
|
className="my-4 flex flex-wrap items-end gap-3"
|
|
>
|
|
<Field label="Conteúdo">
|
|
<select
|
|
className={control}
|
|
name="kind"
|
|
defaultValue={filters.kind ?? ''}
|
|
>
|
|
<option value="">Todos</option>
|
|
<option value="article">Artigos</option>
|
|
<option value="legislation">Legislação</option>
|
|
<option value="document">Documentos</option>
|
|
</select>
|
|
</Field>
|
|
<Field label="Estado da edição">
|
|
<select
|
|
className={control}
|
|
name="state"
|
|
defaultValue={filters.state ?? ''}
|
|
>
|
|
<option value="">Todos</option>
|
|
{Object.entries(states).map(
|
|
([state, label]) => (
|
|
<option key={state} value={state}>
|
|
{label}
|
|
</option>
|
|
),
|
|
)}
|
|
</select>
|
|
</Field>
|
|
<button className={button}>Filtrar fila</button>
|
|
</form>
|
|
<section aria-label="Fila editorial" className="space-y-3">
|
|
{items.data.map((item) => (
|
|
<article
|
|
key={item.id}
|
|
className="rounded border p-4"
|
|
>
|
|
<Link
|
|
className="font-semibold underline"
|
|
href={`/management/editorial/resources/${item.id}`}
|
|
>
|
|
{item.title}
|
|
</Link>
|
|
<p>
|
|
Edição {item.revision_no} ·{' '}
|
|
{states[item.state] ?? item.state} ·
|
|
Responsável:{' '}
|
|
{item.responsible ?? 'Por atribuir'}
|
|
</p>
|
|
<p>
|
|
{item.published
|
|
? 'Versão pública disponível'
|
|
: 'Sem versão pública'}
|
|
{item.legal_required &&
|
|
' · Exige validação jurídica'}
|
|
</p>
|
|
</article>
|
|
))}
|
|
</section>
|
|
<nav aria-label="Paginação" className="flex gap-4">
|
|
{items.current_page > 1 && (
|
|
<Link href={pageUrl(items.current_page - 1)}>
|
|
Anterior
|
|
</Link>
|
|
)}
|
|
<span>
|
|
Página {items.current_page} de {items.last_page}
|
|
</span>
|
|
{items.current_page < items.last_page && (
|
|
<Link href={pageUrl(items.current_page + 1)}>
|
|
Seguinte
|
|
</Link>
|
|
)}
|
|
</nav>
|
|
</>
|
|
)}
|
|
<section
|
|
id="notificacoes"
|
|
aria-label="Notificações editoriais"
|
|
className="rounded-xl border border-slate-200 bg-white p-5 shadow-sm"
|
|
>
|
|
<h2 className="mb-3 text-lg font-semibold">Notificações</h2>
|
|
{notices.length === 0 && (
|
|
<p className="text-sm text-slate-500">
|
|
Não há notificações neste âmbito.
|
|
</p>
|
|
)}
|
|
{notices.map((n) => (
|
|
<p
|
|
key={n.id}
|
|
className="flex flex-wrap items-center justify-between gap-3 border-t border-slate-100 py-3 text-sm"
|
|
>
|
|
<Link className="underline" href={n.href}>
|
|
{eventTitles[n.action] ?? 'Alteração editorial'} ·{' '}
|
|
{n.reference}
|
|
</Link>
|
|
{!n.read_at && (
|
|
<button
|
|
className="ml-3 underline"
|
|
onClick={() =>
|
|
router.post(
|
|
`/management/editorial/notices/${n.id}/read`,
|
|
)
|
|
}
|
|
>
|
|
Marcar como lida
|
|
</button>
|
|
)}
|
|
</p>
|
|
))}
|
|
</section>
|
|
</Management>
|
|
);
|
|
}
|