Inicio do Projeto Piloto
ci / verify (push) Has been cancelled

This commit is contained in:
camilafagundes committed 2026-10-02 23:59:36 -03:00
1 parent 6e23f1533b
commit 419325db12
52 files changed
+1329 -110

No files matched your search

+17 -11
View File
@@ -49,7 +49,7 @@ export function Operation({ ctx }: { ctx: RequestContext }) {
const client = useQueryClient();
const [jobId, setJobId] = useState<string | null>(null);
const [step, setStep] = useState(0);
const [message, setMessage] = useState<string | null>(null);
const [message, setMessage] = useState<{ text: string; action?: string; reference?: string } | null>(null);
const [criteria, setCriteria] = useState(false);
const [sheetDialog, setSheetDialog] = useState<{ sourceId: string; sheets: string[] } | null>(null);
const [mappings, setMappings] = useState<{ sourceColumn: string; modelColumn: string }[]>([]);
@@ -94,6 +94,10 @@ export function Operation({ ctx }: { ctx: RequestContext }) {
});
}
function fail(error?: { message?: string; action?: string; code?: string }, fallback = "Não foi possível continuar.") {
setMessage({ text: error?.message ?? fallback, action: error?.action, reference: error?.code });
}
async function start(modelId: string) {
setMessage(null);
const response = await fetch("/api/spreadsheet-adapter/jobs", {
@@ -101,9 +105,9 @@ export function Operation({ ctx }: { ctx: RequestContext }) {
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ modelId, idempotencyKey: crypto.randomUUID() }),
});
const data = await readJson<{ job?: JobDto; error?: { message: string } }>(response);
const data = await readJson<{ job?: JobDto; error?: { message: string; action?: string; code?: string } }>(response);
if (!response.ok || !data.job) {
setMessage(data.error?.message ?? "Não foi possível abrir a operação");
fail(data.error, "Não foi possível abrir a operação");
return;
}
setJobId(data.job.id);
@@ -117,12 +121,12 @@ export function Operation({ ctx }: { ctx: RequestContext }) {
form.set("file", file);
form.set("jobId", jobId);
const response = await fetch("/api/spreadsheet-adapter/sources/validate", { method: "POST", body: form });
const data = await readJson<{ job: JobDto; code?: string; message?: string; error?: { message: string } }>(response);
const data = await readJson<{ job: JobDto; code?: string; message?: string; error?: { message: string; action?: string; code?: string } }>(response);
if (data.error) {
setMessage(data.error.message);
fail(data.error);
return;
}
setMessage(data.message ?? null);
setMessage(data.message ? { text: data.message } : null);
const created = data.job.sources.at(-1);
if (created && data.code === "SHEET_REQUIRED") setSheetDialog({ sourceId: created.id, sheets: created.sheets });
await job.refetch();
@@ -147,7 +151,7 @@ export function Operation({ ctx }: { ctx: RequestContext }) {
});
const data = await readJson<PreviewContract & { error?: { message: string } }>(response);
if (!response.ok) {
setMessage(data.error?.message ?? "Prévia indisponível");
fail(data.error, "Prévia indisponível");
return;
}
setPreview(data);
@@ -163,8 +167,8 @@ export function Operation({ ctx }: { ctx: RequestContext }) {
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sourceId: source.id, idempotencyKey: crypto.randomUUID() }),
});
const data = await readJson<{ error?: { message: string } }>(response);
if (!response.ok) setMessage(data.error?.message ?? "Processamento não iniciado");
const data = await readJson<{ error?: { message: string; action?: string; code?: string } }>(response);
if (!response.ok) fail(data.error, "Processamento não iniciado");
await job.refetch();
setStep(5);
}
@@ -176,7 +180,7 @@ export function Operation({ ctx }: { ctx: RequestContext }) {
<p className="mt-2 text-sm text-ink/75">Uma transferência por vez. A prévia é obrigatória antes de gerar o arquivo.</p>
</header>
<JourneyTrail steps={["Modelo", "Fontes", "Mapeamento", "Revisão", "Processamento", "Resultado"]} current={step} />
{message ? <ErrorState message={message} /> : null}
{message ? <ErrorState message={message.text} action={message.action} reference={message.reference} /> : null}
{step === 0 ? (
<Card>
{models.isLoading ? <LoadingBlock /> : null}
@@ -284,7 +288,9 @@ export function Operation({ ctx }: { ctx: RequestContext }) {
<Card>
<h2 className="text-lg font-semibold">{job.data.progress?.label ?? "Processando"}</h2>
<p className="mt-2 text-sm">{job.data.progress?.percent == null ? "Progresso ainda não é estimável." : `${job.data.progress.percent}%`}</p>
{job.data.status === "failed" ? <ErrorState message={job.data.errorCode ?? "Falha no processamento"} onRetry={() => void processCurrent()} /> : null}
{job.data.status === "failed" ? (
<ErrorState message="O processamento não foi concluído." action="Tente novamente." reference={job.data.errorCode ?? undefined} onRetry={() => void processCurrent()} />
) : null}
{source?.hasOutput ? (
<div className="mt-4 flex flex-wrap gap-2">
{(["xlsx", "csv", "pdf"] as const).map((format) => (
+13 -1
View File
@@ -19,11 +19,23 @@ export function EmptyState({ title, description, action }: { title: string; desc
);
}
export function ErrorState({ message, onRetry }: { message: string; onRetry?: () => void }) {
export function ErrorState({
message,
action,
reference,
onRetry,
}: {
message: string;
action?: string;
reference?: string;
onRetry?: () => void;
}) {
return (
<div role="alert" className="rounded-[14px] border border-danger bg-white p-4">
<p className="font-semibold text-danger">Não foi possível continuar</p>
<p className="mt-1 text-sm">{message}</p>
{action ? <p className="mt-1 text-sm">{action}</p> : null}
{reference ? <p className="mt-2 text-xs">Referência: {reference}</p> : null}
{onRetry ? (
<button type="button" className="mt-3 text-sm font-semibold underline" onClick={onRetry}>
Tentar novamente