shoc-frontend-new/src/domain/work-orders/utils/screen-work-order-uploads.ts

65 lines
2.3 KiB
TypeScript
Raw Normal View History

import {
MAX_PHOTOS_PER_WORK_ORDER,
MAX_VIDEOS_PER_WORK_ORDER,
MAX_VIDEO_DURATION_SECONDS,
resolveWorkOrderUploadKind,
workOrderFileSizeRejection,
type WorkOrderMediaKindCounts,
} from "@/domain/work-orders/utils/work-order-media-limits";
import { probeVideoDurationSeconds } from "@/lib/probe-video-duration";
export type WorkOrderUploadScreen = {
/** Photos and videos already on the work order, across every surface. */
existing: WorkOrderMediaKindCounts;
isTypeAllowed: (file: File) => boolean;
unsupportedTypeMessage: string;
};
export type WorkOrderUploadScreenResult = { accepted: File[]; rejections: string[] };
/**
* SH-116 browser pre-check shared by every dispatcher upload surface: type, size,
* per-work-order photo/video count (including earlier files in the same selection)
* and video duration. The server enforces the same contract; a video whose duration
* the browser cannot read is never blocked here.
*/
export async function screenWorkOrderUploads(
files: readonly File[],
{ existing, isTypeAllowed, unsupportedTypeMessage }: WorkOrderUploadScreen,
): Promise<WorkOrderUploadScreenResult> {
const counts = { ...existing };
const accepted: File[] = [];
const rejections: string[] = [];
for (const file of files) {
if (!isTypeAllowed(file)) {
rejections.push(unsupportedTypeMessage);
continue;
}
const sizeRejection = workOrderFileSizeRejection(file);
if (sizeRejection) {
rejections.push(sizeRejection);
continue;
}
const kind = resolveWorkOrderUploadKind(file);
if (kind === "photo" && counts.photos >= MAX_PHOTOS_PER_WORK_ORDER) {
rejections.push(`A work order can have at most ${MAX_PHOTOS_PER_WORK_ORDER} photos.`);
continue;
}
if (kind === "video" && counts.videos >= MAX_VIDEOS_PER_WORK_ORDER) {
rejections.push(`A work order can have at most ${MAX_VIDEOS_PER_WORK_ORDER} videos.`);
continue;
}
if (kind === "video") {
const duration = await probeVideoDurationSeconds(file);
if (duration != null && duration > MAX_VIDEO_DURATION_SECONDS) {
rejections.push(`Videos must be ${MAX_VIDEO_DURATION_SECONDS} seconds or shorter.`);
continue;
}
}
if (kind === "photo") counts.photos += 1;
if (kind === "video") counts.videos += 1;
accepted.push(file);
}
return { accepted, rejections };
}