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}
/>