fix(work-orders): wire slide-over media retry and cancel [SH-116]

This commit is contained in:
arthur.bassi 2026-08-06 10:03:37 -03:00
parent 118d2042e6
commit 31dd9d5b72
5 changed files with 46 additions and 19 deletions

View file

@ -13,6 +13,8 @@ type SlideOverCompletionMediaProps = {
onUploadMedia?: (requests: MediaUploadRequest[]) => void;
onCategorizeMedia?: (id: string, category: UploadableMediaCategory) => void;
onRemoveMedia?: (id: string) => void;
onRetryMedia?: (localId: string) => void;
onCancelMedia?: (localId: string) => void;
};
export function SlideOverCompletionMedia({
@ -22,6 +24,8 @@ export function SlideOverCompletionMedia({
onUploadMedia,
onCategorizeMedia,
onRemoveMedia,
onRetryMedia,
onCancelMedia,
}: SlideOverCompletionMediaProps) {
return (
<div className="space-y-3">
@ -37,6 +41,8 @@ export function SlideOverCompletionMedia({
onUpload={onUploadMedia}
onCategorize={onCategorizeMedia}
onRemove={onRemoveMedia}
onRetry={onRetryMedia}
onCancel={onCancelMedia}
readOnly={isReadOnly}
pending={mediaPending}
/>

View file

@ -30,6 +30,8 @@ type SlideOverCompletionTabProps = {
onUploadMedia?: (requests: MediaUploadRequest[]) => void;
onCategorizeMedia?: (id: string, category: UploadableMediaCategory) => void;
onRemoveMedia?: (id: string) => void;
onRetryMedia?: (localId: string) => void;
onCancelMedia?: (localId: string) => void;
onUploadCompletionPdf?: (file: File) => void;
onOpenCompletionDoc: () => void;
};
@ -44,6 +46,8 @@ export function SlideOverCompletionTab({
onUploadMedia,
onCategorizeMedia,
onRemoveMedia,
onRetryMedia,
onCancelMedia,
onUploadCompletionPdf,
onOpenCompletionDoc,
}: SlideOverCompletionTabProps) {
@ -61,6 +65,8 @@ export function SlideOverCompletionTab({
onUploadMedia={onUploadMedia}
onCategorizeMedia={onCategorizeMedia}
onRemoveMedia={onRemoveMedia}
onRetryMedia={onRetryMedia}
onCancelMedia={onCancelMedia}
/>
);
}

View file

@ -1,5 +1,6 @@
import { useMemo, useState } from "react";
import type { MediaUploadRequest } from "@/app/(protected)/workorders/_components/media/media-uploader";
import { useMediaUploadSession } from "@/app/(protected)/workorders/_hooks/use-media-upload-session";
import type { UploadableMediaCategory } from "@/domain/work-orders/mappers/work-order-board-detail-mapper";
import { boardMediaItemsToMediaFiles } from "@/domain/work-orders/mappers/work-order-board-detail-mapper";
import type { WorkOrderBoardDetail } from "@/domain/work-orders/types/work-order-board-detail";
@ -25,15 +26,18 @@ export function useSlideOverMediaState({
categorizeMediaMutation,
}: UseSlideOverMediaStateArgs) {
const [mediaBatchPending, setMediaBatchPending] = useState(false);
const { uploads, startUploads, retryUpload, cancelUpload, removeLocal, isLocal } =
useMediaUploadSession(detailForRow, (input) => uploadMediaMutation.mutateAsync(input));
const completionMedia = useMemo(
() =>
boardMediaItemsToMediaFiles(
(detailForRow?.media ?? []).filter((item) =>
COMPLETION_MEDIA_CATEGORIES.has(item.category),
),
uploads.filter(
(item) =>
COMPLETION_MEDIA_CATEGORIES.has(item.category) ||
item.uploadStatus === "uploading" ||
item.uploadStatus === "failed",
),
[detailForRow?.media],
[uploads],
);
const extrasUploads = useMemo(
@ -44,24 +48,16 @@ export function useSlideOverMediaState({
[detailForRow?.media],
);
const hasLocalUploading = uploads.some((item) => item.uploadStatus === "uploading");
const mediaPending =
mediaBatchPending ||
hasLocalUploading ||
uploadMediaMutation.isPending ||
deleteMediaMutation.isPending ||
categorizeMediaMutation.isPending;
const uploadMediaRequests = async (requests: MediaUploadRequest[]) => {
setMediaBatchPending(true);
try {
for (const request of requests) {
await uploadMediaMutation.mutateAsync({
file: request.file,
onProgress: request.onProgress,
});
}
} finally {
setMediaBatchPending(false);
}
const uploadMediaRequests = (requests: MediaUploadRequest[]) => {
startUploads(requests);
};
const uploadExtraFiles = async (files: File[]) => {
@ -79,6 +75,14 @@ export function useSlideOverMediaState({
categorizeMediaMutation.mutate({ mediaId, category });
};
const removeMedia = (id: string) => {
if (isLocal(id)) {
removeLocal(id);
return;
}
deleteMediaMutation.mutate(id);
};
return {
completionMedia,
extrasUploads,
@ -86,5 +90,8 @@ export function useSlideOverMediaState({
uploadMediaRequests,
uploadExtraFiles,
categorizeMedia,
retryUpload,
cancelUpload,
removeMedia,
};
}

View file

@ -68,6 +68,9 @@ export function useWorkOrderSlideOver({
uploadMediaRequests,
uploadExtraFiles,
categorizeMedia,
retryUpload,
cancelUpload,
removeMedia,
} = useSlideOverMediaState({
detailForRow,
uploadMediaMutation,
@ -123,6 +126,9 @@ export function useWorkOrderSlideOver({
uploadMediaRequests,
uploadExtraFiles,
categorizeMedia,
retryUpload,
cancelUpload,
removeMedia,
deleteMediaMutation,
uploadCompletionPdf,
showDetailSkeleton,

View file

@ -119,10 +119,12 @@ export function WorkOrderSlideOverTabs({
mediaPending={state.mediaPending}
completionPending={state.completionPending}
onUploadMedia={(requests) => {
void state.uploadMediaRequests(requests);
state.uploadMediaRequests(requests);
}}
onCategorizeMedia={state.categorizeMedia}
onRemoveMedia={(id) => state.deleteMediaMutation.mutate(id)}
onRemoveMedia={state.removeMedia}
onRetryMedia={state.retryUpload}
onCancelMedia={state.cancelUpload}
onUploadCompletionPdf={state.uploadCompletionPdf}
onOpenCompletionDoc={onOpenCompletionDoc}
/>