fix(work-orders): require explicit Aveta Extra Docs category

This commit is contained in:
Arthur Bassi 2026-08-25 12:03:36 -03:00
parent 7a30335ab8
commit 5dfb2fb1c1
5 changed files with 47 additions and 44 deletions

View file

@ -40,22 +40,16 @@ type SlideOverExtrasTabProps = {
function ExtraDocRow({ function ExtraDocRow({
file, file,
index,
uploads,
avetaRequired,
isReadOnly, isReadOnly,
pending, pending,
onRemove, onRemove,
}: { }: {
file: MediaFile; file: MediaFile;
index: number;
uploads: MediaFile[];
avetaRequired: boolean;
isReadOnly: boolean; isReadOnly: boolean;
pending: boolean; pending: boolean;
onRemove: (id: string) => void; onRemove: (id: string) => void;
}) { }) {
const category = resolveExtraDocCategory(file, index, avetaRequired, uploads); const category = resolveExtraDocCategory(file);
const isAveta = category === "Aveta"; const isAveta = category === "Aveta";
const ItemIcon = isAveta ? ShieldCheck : FILETYPE_ICON[file.fileType]; const ItemIcon = isAveta ? ShieldCheck : FILETYPE_ICON[file.fileType];
const showDelete = !isReadOnly && canDeleteWorkOrderMedia(file); const showDelete = !isReadOnly && canDeleteWorkOrderMedia(file);
@ -192,13 +186,10 @@ export function SlideOverExtrasTab({
</Text> </Text>
) : ( ) : (
<div className="space-y-1"> <div className="space-y-1">
{uploads.map((file, index) => ( {uploads.map((file) => (
<ExtraDocRow <ExtraDocRow
key={file.id} key={file.id}
file={file} file={file}
index={index}
uploads={uploads}
avetaRequired={avetaRequired}
isReadOnly={isReadOnly} isReadOnly={isReadOnly}
pending={pending} pending={pending}
onRemove={onRemove} onRemove={onRemove}

View file

@ -65,7 +65,7 @@ export function useSlideOverMediaState({
setMediaBatchPending(true); setMediaBatchPending(true);
try { try {
for (const file of files) { for (const file of files) {
// Board purpose is Extra only; Aveta is a UI overlay when avetaRequired. // Board purpose is Extra only; Aveta overlay requires an explicit Extra Docs category.
await uploadMediaMutation.mutateAsync({ file, category: "Extra" }); await uploadMediaMutation.mutateAsync({ file, category: "Extra" });
} }
} finally { } finally {

View file

@ -1,26 +1,14 @@
import type { MediaCategory, MediaFile } from "@/domain/work-orders/types/work-order-media"; import type { MediaCategory, MediaFile } from "@/domain/work-orders/types/work-order-media";
/** First Extra Docs file defaults to Aveta when the WO is flagged and none is Aveta yet. /**
* Board media purpose stays Extra (API enum Before/After/Extra/Completion). */ * Extra Docs overlay category. Board purpose stays Extra
export function resolveExtraDocCategory( * (API enum Before/After/Extra/Completion). Aveta is never inferred from
file: MediaFile, * upload order — only an explicit Aveta category counts.
index: number, */
avetaRequired: boolean, export function resolveExtraDocCategory(file: MediaFile): MediaCategory {
files: MediaFile[],
): MediaCategory {
if (file.category === "Aveta") {
return "Aveta";
}
const hasExplicitAveta = files.some((item) => item.category === "Aveta");
if (avetaRequired && index === 0 && !hasExplicitAveta) {
return "Aveta";
}
return file.category; return file.category;
} }
export function shouldDefaultFirstExtraToAveta( export function hasExplicitAvetaFile(files: MediaFile[]): boolean {
avetaRequired: boolean, return files.some((item) => item.category === "Aveta");
existing: MediaFile[],
): boolean {
return avetaRequired && !existing.some((item) => item.category === "Aveta");
} }

View file

@ -12,8 +12,16 @@ const photo: MediaFile = {
category: "Report", category: "Report",
}; };
const avetaDoc: MediaFile = {
id: "m2",
name: "aveta-clearance.pdf",
fileType: "pdf",
url: "blob:2",
category: "Aveta",
};
describe("SlideOverExtrasTab Aveta", () => { describe("SlideOverExtrasTab Aveta", () => {
it("labels the first extra document as Aveta when the WO is flagged", () => { it("does not label the first extra document as Aveta when none is explicitly Aveta", () => {
renderWithProviders( renderWithProviders(
<SlideOverExtrasTab <SlideOverExtrasTab
uploads={[photo]} uploads={[photo]}
@ -24,7 +32,23 @@ describe("SlideOverExtrasTab Aveta", () => {
/>, />,
); );
expect(screen.getAllByText("Aveta").length).toBeGreaterThan(0);
expect(screen.getByText("safety-form.pdf")).toBeInTheDocument(); expect(screen.getByText("safety-form.pdf")).toBeInTheDocument();
expect(screen.getAllByText("Aveta")).toHaveLength(1);
});
it("labels only an explicitly Aveta-categorized extra document", () => {
renderWithProviders(
<SlideOverExtrasTab
uploads={[photo, avetaDoc]}
avetaRequired
isReadOnly
onUpload={vi.fn()}
onRemove={vi.fn()}
/>,
);
expect(screen.getByText("safety-form.pdf")).toBeInTheDocument();
expect(screen.getByText("aveta-clearance.pdf")).toBeInTheDocument();
expect(screen.getAllByText("Aveta").length).toBeGreaterThanOrEqual(2);
}); });
}); });

View file

@ -1,8 +1,8 @@
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import type { MediaFile } from "@/domain/work-orders/types/work-order-media"; import type { MediaFile } from "@/domain/work-orders/types/work-order-media";
import { import {
hasExplicitAvetaFile,
resolveExtraDocCategory, resolveExtraDocCategory,
shouldDefaultFirstExtraToAveta,
} from "@/domain/work-orders/utils/extra-docs-aveta"; } from "@/domain/work-orders/utils/extra-docs-aveta";
function file(category: MediaFile["category"]): MediaFile { function file(category: MediaFile["category"]): MediaFile {
@ -16,23 +16,23 @@ function file(category: MediaFile["category"]): MediaFile {
} }
describe("extra-docs-aveta", () => { describe("extra-docs-aveta", () => {
it("defaults the first extra file to Aveta when required and none is Aveta", () => { it("does not label the first extra file as Aveta when the WO is flagged and none is Aveta", () => {
const files = [file("Photo"), file("Report")]; const files = [file("Photo"), file("Report")];
expect(resolveExtraDocCategory(files[0], 0, true, files)).toBe("Aveta"); expect(resolveExtraDocCategory(files[0])).toBe("Photo");
expect(resolveExtraDocCategory(files[1], 1, true, files)).toBe("Report"); expect(resolveExtraDocCategory(files[1])).toBe("Report");
expect(shouldDefaultFirstExtraToAveta(true, files)).toBe(true); expect(hasExplicitAvetaFile(files)).toBe(false);
}); });
it("keeps an explicit Aveta file and does not relabel others", () => { it("keeps an explicit Aveta file and does not relabel others", () => {
const files = [file("Photo"), file("Aveta")]; const files = [file("Photo"), file("Aveta")];
expect(resolveExtraDocCategory(files[0], 0, true, files)).toBe("Photo"); expect(resolveExtraDocCategory(files[0])).toBe("Photo");
expect(resolveExtraDocCategory(files[1], 1, true, files)).toBe("Aveta"); expect(resolveExtraDocCategory(files[1])).toBe("Aveta");
expect(shouldDefaultFirstExtraToAveta(true, files)).toBe(false); expect(hasExplicitAvetaFile(files)).toBe(true);
}); });
it("does not label extras as Aveta when the WO is not flagged", () => { it("does not label extras as Aveta when the WO is not flagged", () => {
const files = [file("Photo")]; const files = [file("Photo")];
expect(resolveExtraDocCategory(files[0], 0, false, files)).toBe("Photo"); expect(resolveExtraDocCategory(files[0])).toBe("Photo");
expect(shouldDefaultFirstExtraToAveta(false, files)).toBe(false); expect(hasExplicitAvetaFile(files)).toBe(false);
}); });
}); });