mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 09:13:11 +00:00
feat(work-orders): open Extra Docs through authorized content
This commit is contained in:
parent
edf4a51d4c
commit
47f78fec89
3 changed files with 64 additions and 7 deletions
|
|
@ -11,6 +11,7 @@ import {
|
|||
} from "lucide-react";
|
||||
import { toast } from "react-toastify";
|
||||
import { Text } from "@/components/ui/text";
|
||||
import { workOrdersApi } from "@/domain/work-orders/api/work-orders-api";
|
||||
import type { UploadableMediaCategory } from "@/domain/work-orders/mappers/work-order-board-detail-mapper";
|
||||
import type { FileCategory, MediaFile } from "@/domain/work-orders/types/work-order-media";
|
||||
import {
|
||||
|
|
@ -43,21 +44,36 @@ type SlideOverExtrasTabProps = {
|
|||
isReadOnly: boolean;
|
||||
pending?: boolean;
|
||||
avetaRequired?: boolean;
|
||||
workOrderId?: string | number;
|
||||
onUpload: (files: File[]) => void;
|
||||
onRemove: (id: string) => void;
|
||||
onCategorize?: (id: string, category: UploadableMediaCategory) => void;
|
||||
};
|
||||
|
||||
let lastExtraDocObjectUrl: string | null = null;
|
||||
|
||||
async function openExtraDocContent(workOrderId: string | number, mediaId: string): Promise<void> {
|
||||
const blob = await workOrdersApi.getMediaContent(workOrderId, mediaId);
|
||||
if (lastExtraDocObjectUrl) {
|
||||
URL.revokeObjectURL(lastExtraDocObjectUrl);
|
||||
}
|
||||
const objectUrl = URL.createObjectURL(blob);
|
||||
lastExtraDocObjectUrl = objectUrl;
|
||||
window.open(objectUrl, "_blank", "noopener,noreferrer");
|
||||
}
|
||||
|
||||
function ExtraDocRow({
|
||||
file,
|
||||
isReadOnly,
|
||||
pending,
|
||||
workOrderId,
|
||||
onRemove,
|
||||
onCategorize,
|
||||
}: {
|
||||
file: MediaFile;
|
||||
isReadOnly: boolean;
|
||||
pending: boolean;
|
||||
workOrderId?: string | number;
|
||||
onRemove: (id: string) => void;
|
||||
onCategorize?: (id: string, category: UploadableMediaCategory) => void;
|
||||
}) {
|
||||
|
|
@ -75,15 +91,19 @@ function ExtraDocRow({
|
|||
className="shrink-0"
|
||||
style={{ color: isAveta ? "var(--warning)" : "var(--color-text-muted)" }}
|
||||
/>
|
||||
<a
|
||||
href={file.url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="min-w-0 flex-1 truncate hover:underline"
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (workOrderId == null) return;
|
||||
void openExtraDocContent(workOrderId, file.id).catch(() => {
|
||||
toast.error("Unable to open this document.");
|
||||
});
|
||||
}}
|
||||
className="min-w-0 flex-1 truncate text-left hover:underline"
|
||||
style={{ fontSize: 12, color: "var(--foreground)" }}
|
||||
>
|
||||
{file.name}
|
||||
</a>
|
||||
</button>
|
||||
{showCategory ? (
|
||||
<select
|
||||
aria-label={`Category for ${file.name}`}
|
||||
|
|
@ -134,6 +154,7 @@ export function SlideOverExtrasTab({
|
|||
isReadOnly,
|
||||
pending = false,
|
||||
avetaRequired = false,
|
||||
workOrderId,
|
||||
onUpload,
|
||||
onRemove,
|
||||
onCategorize,
|
||||
|
|
@ -238,6 +259,7 @@ export function SlideOverExtrasTab({
|
|||
file={file}
|
||||
isReadOnly={isReadOnly}
|
||||
pending={pending}
|
||||
workOrderId={workOrderId}
|
||||
onRemove={onRemove}
|
||||
onCategorize={onCategorize}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ export function WorkOrderSlideOverTabs({
|
|||
>
|
||||
<SlideOverExtrasTab
|
||||
uploads={state.extrasUploads}
|
||||
workOrderId={infoSource?.id}
|
||||
avetaRequired={resolveSlideOverAvetaRequired(
|
||||
state.editing,
|
||||
infoSource?.avetaRequired,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { fireEvent, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { SlideOverExtrasTab } from "@/app/(protected)/workorders/_components/detail/tabs/slide-over-extras-tab";
|
||||
import { workOrdersApi } from "@/domain/work-orders/api/work-orders-api";
|
||||
import type { MediaFile } from "@/domain/work-orders/types/work-order-media";
|
||||
import { renderWithProviders } from "@/test/test-utils";
|
||||
|
||||
|
|
@ -21,6 +22,10 @@ const avetaDoc: MediaFile = {
|
|||
};
|
||||
|
||||
describe("SlideOverExtrasTab Aveta", () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
it("does not label the first extra document as Aveta when none is explicitly Aveta", () => {
|
||||
renderWithProviders(
|
||||
<SlideOverExtrasTab
|
||||
|
|
@ -72,6 +77,35 @@ describe("SlideOverExtrasTab Aveta", () => {
|
|||
expect(screen.queryByText("safety-form.pdf")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("opens Extra Docs through authenticated content instead of a static href", async () => {
|
||||
const blob = new Blob(["pdf"], { type: "application/pdf" });
|
||||
const getMediaContent = vi.spyOn(workOrdersApi, "getMediaContent").mockResolvedValue(blob);
|
||||
const createObjectURL = vi.fn(() => "blob:authorized");
|
||||
vi.stubGlobal("URL", { ...URL, createObjectURL, revokeObjectURL: vi.fn() });
|
||||
const open = vi.fn();
|
||||
vi.stubGlobal("open", open);
|
||||
|
||||
renderWithProviders(
|
||||
<SlideOverExtrasTab
|
||||
uploads={[{ ...photo, url: "https://cdn.example.com/Assets/Documents/secret.pdf" }]}
|
||||
workOrderId={10}
|
||||
avetaRequired
|
||||
isReadOnly
|
||||
onUpload={vi.fn()}
|
||||
onRemove={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByRole("link")).not.toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole("button", { name: "safety-form.pdf" }));
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(getMediaContent).toHaveBeenCalledWith(10, "m1");
|
||||
});
|
||||
expect(createObjectURL).toHaveBeenCalledWith(blob);
|
||||
expect(open).toHaveBeenCalledWith("blob:authorized", "_blank", "noopener,noreferrer");
|
||||
});
|
||||
|
||||
it("shows Extra Docs accept copy with PDF DOC and 50MB", () => {
|
||||
renderWithProviders(
|
||||
<SlideOverExtrasTab uploads={[]} isReadOnly={false} onUpload={vi.fn()} onRemove={vi.fn()} />,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue