diff --git a/src/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-media.tsx b/src/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-media.tsx index 62e805c2..24e468d1 100644 --- a/src/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-media.tsx +++ b/src/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-media.tsx @@ -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 (
@@ -37,6 +41,8 @@ export function SlideOverCompletionMedia({ onUpload={onUploadMedia} onCategorize={onCategorizeMedia} onRemove={onRemoveMedia} + onRetry={onRetryMedia} + onCancel={onCancelMedia} readOnly={isReadOnly} pending={mediaPending} /> diff --git a/src/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-tab.tsx b/src/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-tab.tsx index cb444296..290ec603 100644 --- a/src/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-tab.tsx +++ b/src/app/(protected)/workorders/_components/detail/tabs/slide-over-completion-tab.tsx @@ -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} /> ); } diff --git a/src/app/(protected)/workorders/_components/detail/use-slide-over-media-state.ts b/src/app/(protected)/workorders/_components/detail/use-slide-over-media-state.ts index 0220f747..821ff322 100644 --- a/src/app/(protected)/workorders/_components/detail/use-slide-over-media-state.ts +++ b/src/app/(protected)/workorders/_components/detail/use-slide-over-media-state.ts @@ -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, }; } diff --git a/src/app/(protected)/workorders/_components/detail/use-work-order-slide-over.ts b/src/app/(protected)/workorders/_components/detail/use-work-order-slide-over.ts index 7d6505c6..771cfb85 100644 --- a/src/app/(protected)/workorders/_components/detail/use-work-order-slide-over.ts +++ b/src/app/(protected)/workorders/_components/detail/use-work-order-slide-over.ts @@ -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, diff --git a/src/app/(protected)/workorders/_components/detail/work-order-slide-over-tabs.tsx b/src/app/(protected)/workorders/_components/detail/work-order-slide-over-tabs.tsx index 3f91d5f2..de9267fe 100644 --- a/src/app/(protected)/workorders/_components/detail/work-order-slide-over-tabs.tsx +++ b/src/app/(protected)/workorders/_components/detail/work-order-slide-over-tabs.tsx @@ -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} />