151 lines
6.2 KiB
TypeScript
151 lines
6.2 KiB
TypeScript
import { Management, Pagination, type Paginated } from '../management/shared';
|
|
import type { OpportunityTechnicalIncident } from '@/components/opportunity-technical-incidents';
|
|
import { Link } from '@inertiajs/react';
|
|
import {
|
|
handlingLabels,
|
|
incidentOperationLabel,
|
|
incidentErrorLabel,
|
|
} from '@/lib/technical-incident-labels';
|
|
import type { HandlingState } from '@/lib/technical-incident-handling';
|
|
import TechnicalIncidentNotice from '@/components/technical-incident-notice';
|
|
export default function TechnicalSupport({
|
|
incidents,
|
|
actorId,
|
|
filters = { status: 'all' },
|
|
}: {
|
|
actorId?: number;
|
|
incidents: Paginated<
|
|
OpportunityTechnicalIncident & {
|
|
handling?: HandlingState | null;
|
|
details_url?: string;
|
|
unread?: boolean;
|
|
}
|
|
>;
|
|
filters?: { status: string; unread?: boolean };
|
|
}) {
|
|
function href(status: string, unread = !!filters.unread) {
|
|
const query = new URLSearchParams({
|
|
...(status !== 'all' ? { status } : {}),
|
|
...(unread ? { unread: '1' } : {}),
|
|
}).toString();
|
|
return '/suporte-tecnico/incidentes' + (query ? '?' + query : '');
|
|
}
|
|
return (
|
|
<Management title="Suporte técnico — incidentes">
|
|
<p>
|
|
Incidentes de submissão, publicação, uploads e preparação de
|
|
imagens nas organizações e tipos abrangidos pela sua atribuição
|
|
actual. A fila apresenta metadados técnicos; o perfil de suporte
|
|
não concede acesso aos processos ou documentos.
|
|
</p>
|
|
<nav
|
|
aria-label="Filtrar por estado técnico"
|
|
className="flex flex-wrap gap-4"
|
|
>
|
|
{Object.entries({ all: 'Todos', ...handlingLabels }).map(
|
|
([value, label]) => (
|
|
<Link
|
|
key={value}
|
|
className="underline"
|
|
href={href(value)}
|
|
aria-current={
|
|
filters.status === value ? 'page' : undefined
|
|
}
|
|
>
|
|
{label}
|
|
</Link>
|
|
),
|
|
)}
|
|
</nav>
|
|
<nav
|
|
aria-label="Filtrar avisos de suporte"
|
|
className="flex flex-wrap gap-4"
|
|
>
|
|
<Link
|
|
className="underline"
|
|
href={href(filters.status, false)}
|
|
aria-current={!filters.unread ? 'page' : undefined}
|
|
>
|
|
Todos os avisos
|
|
</Link>
|
|
<Link
|
|
className="underline"
|
|
href={href(filters.status, true)}
|
|
aria-current={filters.unread ? 'page' : undefined}
|
|
>
|
|
Por consultar
|
|
</Link>
|
|
</nav>
|
|
{!incidents.data.length && (
|
|
<p>Não existem incidentes disponíveis neste âmbito.</p>
|
|
)}
|
|
{incidents.data.map((incident) => (
|
|
<article
|
|
key={incident.reference}
|
|
className="space-y-2 rounded border p-4"
|
|
>
|
|
<h2 className="font-semibold">
|
|
{incidentOperationLabel(incident.operation)} · versão
|
|
observada {incident.version}
|
|
</h2>
|
|
<p>{incident.occurred_at}</p>
|
|
<p>{incidentErrorLabel(incident.error_code)}</p>
|
|
<p className="break-all">
|
|
Referência: {incident.reference}
|
|
</p>
|
|
{actorId && typeof incident.unread === 'boolean' && (
|
|
<TechnicalIncidentNotice
|
|
key={`notice:${actorId}:${incident.reference}`}
|
|
actorId={actorId}
|
|
reference={incident.reference}
|
|
unread={incident.unread}
|
|
/>
|
|
)}
|
|
{incident.handling ? (
|
|
<p>
|
|
Estado técnico:{' '}
|
|
{handlingLabels[incident.handling.status]} ·{' '}
|
|
{incident.handling.assignee_name || 'Por assumir'}
|
|
</p>
|
|
) : (
|
|
<p>
|
|
Estado técnico indisponível; registos preservados.
|
|
</p>
|
|
)}
|
|
{incident.details_url && (
|
|
<Link
|
|
className="underline"
|
|
href={
|
|
incident.details_url +
|
|
(incidents.current_page > 1 ||
|
|
filters.status !== 'all' ||
|
|
filters.unread
|
|
? '?' +
|
|
new URLSearchParams({
|
|
...(incidents.current_page > 1
|
|
? {
|
|
page: String(
|
|
incidents.current_page,
|
|
),
|
|
}
|
|
: {}),
|
|
...(filters.status !== 'all'
|
|
? { status: filters.status }
|
|
: {}),
|
|
...(filters.unread
|
|
? { unread: '1' }
|
|
: {}),
|
|
}).toString()
|
|
: '')
|
|
}
|
|
>
|
|
Consultar e tratar incidente
|
|
</Link>
|
|
)}
|
|
</article>
|
|
))}
|
|
<Pagination page={incidents} />
|
|
</Management>
|
|
);
|
|
}
|