@flipdish/portal-library 8.6.5 → 8.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/organisms/AssetManager/hooks/useAssetUploadManager.cjs.js +1 -1
- package/dist/components/organisms/AssetManager/hooks/useAssetUploadManager.cjs.js.map +1 -1
- package/dist/components/organisms/AssetManager/hooks/useAssetUploadManager.d.ts +2 -1
- package/dist/components/organisms/AssetManager/hooks/useAssetUploadManager.js +1 -1
- package/dist/components/organisms/AssetManager/hooks/useAssetUploadManager.js.map +1 -1
- package/dist/components/organisms/AssetManager/hooks/usePresignedUploadAsset.cjs.js +2 -0
- package/dist/components/organisms/AssetManager/hooks/usePresignedUploadAsset.cjs.js.map +1 -0
- package/dist/components/organisms/AssetManager/hooks/usePresignedUploadAsset.d.ts +12 -0
- package/dist/components/organisms/AssetManager/hooks/usePresignedUploadAsset.js +2 -0
- package/dist/components/organisms/AssetManager/hooks/usePresignedUploadAsset.js.map +1 -0
- package/dist/components/organisms/AssetManager/index.cjs.js +1 -1
- package/dist/components/organisms/AssetManager/index.cjs.js.map +1 -1
- package/dist/components/organisms/AssetManager/index.d.ts +2 -0
- package/dist/components/organisms/AssetManager/index.js +1 -1
- package/dist/components/organisms/AssetManager/index.js.map +1 -1
- package/dist/components/organisms/AssetManager/services/asset.service.cjs.js +1 -1
- package/dist/components/organisms/AssetManager/services/asset.service.cjs.js.map +1 -1
- package/dist/components/organisms/AssetManager/services/asset.service.d.ts +45 -2
- package/dist/components/organisms/AssetManager/services/asset.service.js +1 -1
- package/dist/components/organisms/AssetManager/services/asset.service.js.map +1 -1
- package/dist/mocks/msw/handlers.cjs.js +1 -1
- package/dist/mocks/msw/handlers.cjs.js.map +1 -1
- package/dist/mocks/msw/handlers.d.ts +6 -1
- package/dist/mocks/msw/handlers.js +1 -1
- package/dist/mocks/msw/handlers.js.map +1 -1
- package/package.json +12 -12
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var e=require("react"),l=require("./useUploadAsset.cjs.js");exports.useAssetUploadManager=({orgId:a,brandId:
|
|
1
|
+
"use strict";var e=require("react"),l=require("./usePresignedUploadAsset.cjs.js"),s=require("./useUploadAsset.cjs.js");exports.useAssetUploadManager=({orgId:a,brandId:r,onUploadSuccess:o,getErrorMessage:d,useNewUploadFlow:u=!1})=>{const[n,t]=e.useState([]),[c,i]=e.useState(null),p=s.useUploadAsset(),U=l.usePresignedUploadAsset(),g=u?U:p,b=e.useCallback(e=>{if(0===e.length)return;i(null);const l=e.map(e=>({file:e}));t(e=>[...e,...l])},[]),f=e.useCallback(e=>{t(l=>l.filter(l=>l.file!==e))},[]),S=e.useCallback(()=>{i(null),g.mutate({orgId:a,brandId:r,files:n.map(e=>e.file)},{onSuccess:e=>{const l=e.data||[];o&&o(l),t([])},onError:e=>{console.error("Upload failed:",e);const l=d(e);i(l)}})},[a,r,n,g,o,d]),k=e.useCallback(()=>{t([])},[]),A=e.useCallback(()=>{i(null)},[]);return{uploadedFiles:n,uploadError:c,isUploading:g.isPending,handleUpload:b,handleRemove:f,handleSave:S,clearUploadedFiles:k,clearSaveUploadError:A}};
|
|
2
2
|
//# sourceMappingURL=useAssetUploadManager.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAssetUploadManager.cjs.js","sources":["../../../../../src/components/organisms/AssetManager/hooks/useAssetUploadManager.ts"],"sourcesContent":["import { useCallback, useState } from 'react';\n\nimport type { FileUploadState } from '../../FileUpload';\nimport type { Asset } from '../types/assets.type';\nimport { useUploadAsset } from './useUploadAsset';\n\ninterface UseAssetUploadManagerOptions {\n orgId: string;\n brandId?: string;\n onUploadSuccess?: (assets: Asset[]) => void;\n getErrorMessage: (error: Error) => string;\n}\n\ninterface UseAssetUploadManagerReturn {\n uploadedFiles: FileUploadState[];\n uploadError: string | null;\n isUploading: boolean;\n handleUpload: (files: File[]) => void;\n handleRemove: (file: File) => void;\n handleSave: () => void;\n clearUploadedFiles: () => void;\n clearSaveUploadError: () => void;\n}\n\n/**\n * Custom hook to manage asset upload state and logic\n * Handles file management, upload execution, and error handling\n *\n * @param options - Configuration options for upload management\n * @returns Upload state and handlers\n */\nexport const useAssetUploadManager = ({\n orgId,\n brandId,\n onUploadSuccess,\n getErrorMessage,\n}: UseAssetUploadManagerOptions): UseAssetUploadManagerReturn => {\n const [uploadedFiles, setUploadedFiles] = useState<FileUploadState[]>([]);\n const [uploadError, setUploadError] = useState<string | null>(null);\n const
|
|
1
|
+
{"version":3,"file":"useAssetUploadManager.cjs.js","sources":["../../../../../src/components/organisms/AssetManager/hooks/useAssetUploadManager.ts"],"sourcesContent":["import { useCallback, useState } from 'react';\n\nimport type { FileUploadState } from '../../FileUpload';\nimport type { Asset } from '../types/assets.type';\nimport { usePresignedUploadAsset } from './usePresignedUploadAsset';\nimport { useUploadAsset } from './useUploadAsset';\n\ninterface UseAssetUploadManagerOptions {\n orgId: string;\n brandId?: string;\n onUploadSuccess?: (assets: Asset[]) => void;\n getErrorMessage: (error: Error) => string;\n useNewUploadFlow?: boolean;\n}\n\ninterface UseAssetUploadManagerReturn {\n uploadedFiles: FileUploadState[];\n uploadError: string | null;\n isUploading: boolean;\n handleUpload: (files: File[]) => void;\n handleRemove: (file: File) => void;\n handleSave: () => void;\n clearUploadedFiles: () => void;\n clearSaveUploadError: () => void;\n}\n\n/**\n * Custom hook to manage asset upload state and logic\n * Handles file management, upload execution, and error handling\n *\n * @param options - Configuration options for upload management\n * @returns Upload state and handlers\n */\nexport const useAssetUploadManager = ({\n orgId,\n brandId,\n onUploadSuccess,\n getErrorMessage,\n useNewUploadFlow = false,\n}: UseAssetUploadManagerOptions): UseAssetUploadManagerReturn => {\n const [uploadedFiles, setUploadedFiles] = useState<FileUploadState[]>([]);\n const [uploadError, setUploadError] = useState<string | null>(null);\n const legacyMutation = useUploadAsset();\n const presignedMutation = usePresignedUploadAsset();\n const uploadMutation = useNewUploadFlow ? presignedMutation : legacyMutation;\n\n const handleUpload = useCallback((files: File[]): void => {\n if (files.length === 0) {\n return;\n }\n // Clear any upload errors when files are added\n setUploadError(null);\n // Add files with uploading state\n const newFiles: FileUploadState[] = files.map((file) => ({\n file,\n }));\n setUploadedFiles((prev) => [...prev, ...newFiles]);\n }, []);\n\n const handleRemove = useCallback((fileToRemove: File): void => {\n setUploadedFiles((prev) => prev.filter((fileState) => fileState.file !== fileToRemove));\n }, []);\n\n const handleSave = useCallback((): void => {\n // Clear any previous errors\n setUploadError(null);\n\n uploadMutation.mutate(\n {\n orgId,\n brandId,\n files: uploadedFiles.map((file) => file.file),\n },\n {\n onSuccess: (response) => {\n const uploadedAssets = response.data || [];\n // Notify parent component of successful upload\n if (onUploadSuccess) {\n onUploadSuccess(uploadedAssets);\n }\n // Clear uploaded files after successful upload\n setUploadedFiles([]);\n },\n onError: (error) => {\n console.error('Upload failed:', error);\n // Display the error message\n const errorMessage = getErrorMessage(error);\n setUploadError(errorMessage);\n },\n },\n );\n }, [orgId, brandId, uploadedFiles, uploadMutation, onUploadSuccess, getErrorMessage]);\n\n const clearUploadedFiles = useCallback((): void => {\n setUploadedFiles([]);\n }, []);\n\n const clearSaveUploadError = useCallback((): void => {\n setUploadError(null);\n }, []);\n\n return {\n uploadedFiles,\n uploadError,\n isUploading: uploadMutation.isPending,\n handleUpload,\n handleRemove,\n handleSave,\n clearUploadedFiles,\n clearSaveUploadError,\n };\n};\n"],"names":["orgId","brandId","onUploadSuccess","getErrorMessage","useNewUploadFlow","uploadedFiles","setUploadedFiles","useState","uploadError","setUploadError","legacyMutation","useUploadAsset","presignedMutation","usePresignedUploadAsset","uploadMutation","handleUpload","useCallback","files","length","newFiles","map","file","prev","handleRemove","fileToRemove","filter","fileState","handleSave","mutate","onSuccess","response","uploadedAssets","data","onError","error","console","errorMessage","clearUploadedFiles","clearSaveUploadError","isUploading","isPending"],"mappings":"qJAiCqC,EACnCA,QACAC,UACAC,kBACAC,kBACAC,oBAAmB,MAEnB,MAAOC,EAAeC,GAAoBC,EAAAA,SAA4B,KAC/DC,EAAaC,GAAkBF,EAAAA,SAAwB,MACxDG,EAAiBC,EAAAA,iBACjBC,EAAoBC,EAAAA,0BACpBC,EAAiBV,EAAmBQ,EAAoBF,EAExDK,EAAeC,cAAaC,IAChC,GAAqB,IAAjBA,EAAMC,OACR,OAGFT,EAAe,MAEf,MAAMU,EAA8BF,EAAMG,IAAKC,IAAI,CACjDA,UAEFf,EAAkBgB,GAAS,IAAIA,KAASH,KACvC,IAEGI,EAAeP,cAAaQ,IAChClB,EAAkBgB,GAASA,EAAKG,OAAQC,GAAcA,EAAUL,OAASG,KACxE,IAEGG,EAAaX,EAAAA,YAAY,KAE7BP,EAAe,MAEfK,EAAec,OACb,CACE5B,QACAC,UACAgB,MAAOZ,EAAce,IAAKC,GAASA,EAAKA,OAE1C,CACEQ,UAAYC,IACV,MAAMC,EAAiBD,EAASE,MAAQ,GAEpC9B,GACFA,EAAgB6B,GAGlBzB,EAAiB,KAEnB2B,QAAUC,IACRC,QAAQD,MAAM,iBAAkBA,GAEhC,MAAME,EAAejC,EAAgB+B,GACrCzB,EAAe2B,OAIpB,CAACpC,EAAOC,EAASI,EAAeS,EAAgBZ,EAAiBC,IAE9DkC,EAAqBrB,EAAAA,YAAY,KACrCV,EAAiB,KAChB,IAEGgC,EAAuBtB,EAAAA,YAAY,KACvCP,EAAe,OACd,IAEH,MAAO,CACLJ,gBACAG,cACA+B,YAAazB,EAAe0B,UAC5BzB,eACAQ,eACAI,aACAU,qBACAC"}
|
|
@@ -6,6 +6,7 @@ interface UseAssetUploadManagerOptions {
|
|
|
6
6
|
brandId?: string;
|
|
7
7
|
onUploadSuccess?: (assets: Asset[]) => void;
|
|
8
8
|
getErrorMessage: (error: Error) => string;
|
|
9
|
+
useNewUploadFlow?: boolean;
|
|
9
10
|
}
|
|
10
11
|
interface UseAssetUploadManagerReturn {
|
|
11
12
|
uploadedFiles: FileUploadState[];
|
|
@@ -24,6 +25,6 @@ interface UseAssetUploadManagerReturn {
|
|
|
24
25
|
* @param options - Configuration options for upload management
|
|
25
26
|
* @returns Upload state and handlers
|
|
26
27
|
*/
|
|
27
|
-
declare const useAssetUploadManager: ({ orgId, brandId, onUploadSuccess, getErrorMessage, }: UseAssetUploadManagerOptions) => UseAssetUploadManagerReturn;
|
|
28
|
+
declare const useAssetUploadManager: ({ orgId, brandId, onUploadSuccess, getErrorMessage, useNewUploadFlow, }: UseAssetUploadManagerOptions) => UseAssetUploadManagerReturn;
|
|
28
29
|
|
|
29
30
|
export { useAssetUploadManager };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useState as e,useCallback as
|
|
1
|
+
import{useState as e,useCallback as o}from"react";import{usePresignedUploadAsset as l}from"./usePresignedUploadAsset.js";import{useUploadAsset as r}from"./useUploadAsset.js";const a=({orgId:a,brandId:s,onUploadSuccess:d,getErrorMessage:n,useNewUploadFlow:t=!1})=>{const[i,p]=e([]),[u,c]=e(null),f=r(),m=l(),U=t?m:f,g=o(e=>{if(0===e.length)return;c(null);const o=e.map(e=>({file:e}));p(e=>[...e,...o])},[]),h=o(e=>{p(o=>o.filter(o=>o.file!==e))},[]),E=o(()=>{c(null),U.mutate({orgId:a,brandId:s,files:i.map(e=>e.file)},{onSuccess:e=>{const o=e.data||[];d&&d(o),p([])},onError:e=>{console.error("Upload failed:",e);const o=n(e);c(o)}})},[a,s,i,U,d,n]),I=o(()=>{p([])},[]),S=o(()=>{c(null)},[]);return{uploadedFiles:i,uploadError:u,isUploading:U.isPending,handleUpload:g,handleRemove:h,handleSave:E,clearUploadedFiles:I,clearSaveUploadError:S}};export{a as useAssetUploadManager};
|
|
2
2
|
//# sourceMappingURL=useAssetUploadManager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAssetUploadManager.js","sources":["../../../../../src/components/organisms/AssetManager/hooks/useAssetUploadManager.ts"],"sourcesContent":["import { useCallback, useState } from 'react';\n\nimport type { FileUploadState } from '../../FileUpload';\nimport type { Asset } from '../types/assets.type';\nimport { useUploadAsset } from './useUploadAsset';\n\ninterface UseAssetUploadManagerOptions {\n orgId: string;\n brandId?: string;\n onUploadSuccess?: (assets: Asset[]) => void;\n getErrorMessage: (error: Error) => string;\n}\n\ninterface UseAssetUploadManagerReturn {\n uploadedFiles: FileUploadState[];\n uploadError: string | null;\n isUploading: boolean;\n handleUpload: (files: File[]) => void;\n handleRemove: (file: File) => void;\n handleSave: () => void;\n clearUploadedFiles: () => void;\n clearSaveUploadError: () => void;\n}\n\n/**\n * Custom hook to manage asset upload state and logic\n * Handles file management, upload execution, and error handling\n *\n * @param options - Configuration options for upload management\n * @returns Upload state and handlers\n */\nexport const useAssetUploadManager = ({\n orgId,\n brandId,\n onUploadSuccess,\n getErrorMessage,\n}: UseAssetUploadManagerOptions): UseAssetUploadManagerReturn => {\n const [uploadedFiles, setUploadedFiles] = useState<FileUploadState[]>([]);\n const [uploadError, setUploadError] = useState<string | null>(null);\n const
|
|
1
|
+
{"version":3,"file":"useAssetUploadManager.js","sources":["../../../../../src/components/organisms/AssetManager/hooks/useAssetUploadManager.ts"],"sourcesContent":["import { useCallback, useState } from 'react';\n\nimport type { FileUploadState } from '../../FileUpload';\nimport type { Asset } from '../types/assets.type';\nimport { usePresignedUploadAsset } from './usePresignedUploadAsset';\nimport { useUploadAsset } from './useUploadAsset';\n\ninterface UseAssetUploadManagerOptions {\n orgId: string;\n brandId?: string;\n onUploadSuccess?: (assets: Asset[]) => void;\n getErrorMessage: (error: Error) => string;\n useNewUploadFlow?: boolean;\n}\n\ninterface UseAssetUploadManagerReturn {\n uploadedFiles: FileUploadState[];\n uploadError: string | null;\n isUploading: boolean;\n handleUpload: (files: File[]) => void;\n handleRemove: (file: File) => void;\n handleSave: () => void;\n clearUploadedFiles: () => void;\n clearSaveUploadError: () => void;\n}\n\n/**\n * Custom hook to manage asset upload state and logic\n * Handles file management, upload execution, and error handling\n *\n * @param options - Configuration options for upload management\n * @returns Upload state and handlers\n */\nexport const useAssetUploadManager = ({\n orgId,\n brandId,\n onUploadSuccess,\n getErrorMessage,\n useNewUploadFlow = false,\n}: UseAssetUploadManagerOptions): UseAssetUploadManagerReturn => {\n const [uploadedFiles, setUploadedFiles] = useState<FileUploadState[]>([]);\n const [uploadError, setUploadError] = useState<string | null>(null);\n const legacyMutation = useUploadAsset();\n const presignedMutation = usePresignedUploadAsset();\n const uploadMutation = useNewUploadFlow ? presignedMutation : legacyMutation;\n\n const handleUpload = useCallback((files: File[]): void => {\n if (files.length === 0) {\n return;\n }\n // Clear any upload errors when files are added\n setUploadError(null);\n // Add files with uploading state\n const newFiles: FileUploadState[] = files.map((file) => ({\n file,\n }));\n setUploadedFiles((prev) => [...prev, ...newFiles]);\n }, []);\n\n const handleRemove = useCallback((fileToRemove: File): void => {\n setUploadedFiles((prev) => prev.filter((fileState) => fileState.file !== fileToRemove));\n }, []);\n\n const handleSave = useCallback((): void => {\n // Clear any previous errors\n setUploadError(null);\n\n uploadMutation.mutate(\n {\n orgId,\n brandId,\n files: uploadedFiles.map((file) => file.file),\n },\n {\n onSuccess: (response) => {\n const uploadedAssets = response.data || [];\n // Notify parent component of successful upload\n if (onUploadSuccess) {\n onUploadSuccess(uploadedAssets);\n }\n // Clear uploaded files after successful upload\n setUploadedFiles([]);\n },\n onError: (error) => {\n console.error('Upload failed:', error);\n // Display the error message\n const errorMessage = getErrorMessage(error);\n setUploadError(errorMessage);\n },\n },\n );\n }, [orgId, brandId, uploadedFiles, uploadMutation, onUploadSuccess, getErrorMessage]);\n\n const clearUploadedFiles = useCallback((): void => {\n setUploadedFiles([]);\n }, []);\n\n const clearSaveUploadError = useCallback((): void => {\n setUploadError(null);\n }, []);\n\n return {\n uploadedFiles,\n uploadError,\n isUploading: uploadMutation.isPending,\n handleUpload,\n handleRemove,\n handleSave,\n clearUploadedFiles,\n clearSaveUploadError,\n };\n};\n"],"names":["useAssetUploadManager","orgId","brandId","onUploadSuccess","getErrorMessage","useNewUploadFlow","uploadedFiles","setUploadedFiles","useState","uploadError","setUploadError","legacyMutation","useUploadAsset","presignedMutation","usePresignedUploadAsset","uploadMutation","handleUpload","useCallback","files","length","newFiles","map","file","prev","handleRemove","fileToRemove","filter","fileState","handleSave","mutate","onSuccess","response","uploadedAssets","data","onError","error","console","errorMessage","clearUploadedFiles","clearSaveUploadError","isUploading","isPending"],"mappings":"8KAiCO,MAAMA,EAAwB,EACnCC,QACAC,UACAC,kBACAC,kBACAC,oBAAmB,MAEnB,MAAOC,EAAeC,GAAoBC,EAA4B,KAC/DC,EAAaC,GAAkBF,EAAwB,MACxDG,EAAiBC,IACjBC,EAAoBC,IACpBC,EAAiBV,EAAmBQ,EAAoBF,EAExDK,EAAeC,EAAaC,IAChC,GAAqB,IAAjBA,EAAMC,OACR,OAGFT,EAAe,MAEf,MAAMU,EAA8BF,EAAMG,IAAKC,IAAI,CACjDA,UAEFf,EAAkBgB,GAAS,IAAIA,KAASH,KACvC,IAEGI,EAAeP,EAAaQ,IAChClB,EAAkBgB,GAASA,EAAKG,OAAQC,GAAcA,EAAUL,OAASG,KACxE,IAEGG,EAAaX,EAAY,KAE7BP,EAAe,MAEfK,EAAec,OACb,CACE5B,QACAC,UACAgB,MAAOZ,EAAce,IAAKC,GAASA,EAAKA,OAE1C,CACEQ,UAAYC,IACV,MAAMC,EAAiBD,EAASE,MAAQ,GAEpC9B,GACFA,EAAgB6B,GAGlBzB,EAAiB,KAEnB2B,QAAUC,IACRC,QAAQD,MAAM,iBAAkBA,GAEhC,MAAME,EAAejC,EAAgB+B,GACrCzB,EAAe2B,OAIpB,CAACpC,EAAOC,EAASI,EAAeS,EAAgBZ,EAAiBC,IAE9DkC,EAAqBrB,EAAY,KACrCV,EAAiB,KAChB,IAEGgC,EAAuBtB,EAAY,KACvCP,EAAe,OACd,IAEH,MAAO,CACLJ,gBACAG,cACA+B,YAAazB,EAAe0B,UAC5BzB,eACAQ,eACAI,aACAU,qBACAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var s=require("@tanstack/react-query"),e=require("../assets.queryKeys.cjs.js"),r=require("../services/asset.service.cjs.js");exports.usePresignedUploadAsset=()=>{const t=s.useQueryClient();return s.useMutation({mutationFn:r.uploadAssetViaPresignedUrl,onSuccess:(s,r)=>{const a=e.assetsKeys.list({orgId:r.orgId,brandId:r.brandId}),u=s.data||[];t.setQueryData(a,s=>({data:[...u,...s?.data||[]]}))}})};
|
|
2
|
+
//# sourceMappingURL=usePresignedUploadAsset.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePresignedUploadAsset.cjs.js","sources":["../../../../../src/components/organisms/AssetManager/hooks/usePresignedUploadAsset.ts"],"sourcesContent":["import { useMutation, type UseMutationResult, useQueryClient } from '@tanstack/react-query';\n\nimport { assetsKeys } from '../assets.queryKeys';\nimport { type UploadAssetParams, uploadAssetViaPresignedUrl } from '../services/asset.service';\nimport { type ListAssetsResponse, type UploadAssetResponse } from '../types/assets.type';\n\n/**\n * Hook to upload assets via the presigned S3 URL flow (request-upload → S3 PUT → confirm-upload).\n * Supports files up to 20 MB. Automatically updates the assets query cache after successful upload.\n * @returns Mutation result with mutate function, loading state, and error state\n */\nexport const usePresignedUploadAsset = (): UseMutationResult<\n UploadAssetResponse,\n Error,\n UploadAssetParams\n> => {\n const queryClient = useQueryClient();\n\n return useMutation<UploadAssetResponse, Error, UploadAssetParams>({\n mutationFn: uploadAssetViaPresignedUrl,\n onSuccess: (createdAssetsResponse, variables) => {\n const queryKey = assetsKeys.list({ orgId: variables.orgId, brandId: variables.brandId });\n const newAssets = createdAssetsResponse.data || [];\n\n queryClient.setQueryData<ListAssetsResponse>(queryKey, (prev) => ({\n data: [...newAssets, ...(prev?.data || [])],\n }));\n },\n });\n};\n"],"names":["queryClient","useQueryClient","useMutation","mutationFn","uploadAssetViaPresignedUrl","onSuccess","createdAssetsResponse","variables","queryKey","assetsKeys","list","orgId","brandId","newAssets","data","setQueryData","prev"],"mappings":"0KAWuC,KAKrC,MAAMA,EAAcC,EAAAA,iBAEpB,OAAOC,cAA2D,CAChEC,WAAYC,EAAAA,2BACZC,UAAW,CAACC,EAAuBC,KACjC,MAAMC,EAAWC,EAAAA,WAAWC,KAAK,CAAEC,MAAOJ,EAAUI,MAAOC,QAASL,EAAUK,UACxEC,EAAYP,EAAsBQ,MAAQ,GAEhDd,EAAYe,aAAiCP,EAAWQ,IAAI,CAC1DF,KAAM,IAAID,KAAeG,GAAMF,MAAQ"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { UseMutationResult } from '@tanstack/react-query';
|
|
2
|
+
import { UploadAssetParams } from '../services/asset.service.js';
|
|
3
|
+
import { UploadAssetResponse } from '@flipdish/asset-management';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hook to upload assets via the presigned S3 URL flow (request-upload → S3 PUT → confirm-upload).
|
|
7
|
+
* Supports files up to 20 MB. Automatically updates the assets query cache after successful upload.
|
|
8
|
+
* @returns Mutation result with mutate function, loading state, and error state
|
|
9
|
+
*/
|
|
10
|
+
declare const usePresignedUploadAsset: () => UseMutationResult<UploadAssetResponse, Error, UploadAssetParams>;
|
|
11
|
+
|
|
12
|
+
export { usePresignedUploadAsset };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{useQueryClient as t,useMutation as r}from"@tanstack/react-query";import{assetsKeys as s}from"../assets.queryKeys.js";import{uploadAssetViaPresignedUrl as a}from"../services/asset.service.js";const e=()=>{const e=t();return r({mutationFn:a,onSuccess:(t,r)=>{const a=s.list({orgId:r.orgId,brandId:r.brandId}),o=t.data||[];e.setQueryData(a,t=>({data:[...o,...t?.data||[]]}))}})};export{e as usePresignedUploadAsset};
|
|
2
|
+
//# sourceMappingURL=usePresignedUploadAsset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePresignedUploadAsset.js","sources":["../../../../../src/components/organisms/AssetManager/hooks/usePresignedUploadAsset.ts"],"sourcesContent":["import { useMutation, type UseMutationResult, useQueryClient } from '@tanstack/react-query';\n\nimport { assetsKeys } from '../assets.queryKeys';\nimport { type UploadAssetParams, uploadAssetViaPresignedUrl } from '../services/asset.service';\nimport { type ListAssetsResponse, type UploadAssetResponse } from '../types/assets.type';\n\n/**\n * Hook to upload assets via the presigned S3 URL flow (request-upload → S3 PUT → confirm-upload).\n * Supports files up to 20 MB. Automatically updates the assets query cache after successful upload.\n * @returns Mutation result with mutate function, loading state, and error state\n */\nexport const usePresignedUploadAsset = (): UseMutationResult<\n UploadAssetResponse,\n Error,\n UploadAssetParams\n> => {\n const queryClient = useQueryClient();\n\n return useMutation<UploadAssetResponse, Error, UploadAssetParams>({\n mutationFn: uploadAssetViaPresignedUrl,\n onSuccess: (createdAssetsResponse, variables) => {\n const queryKey = assetsKeys.list({ orgId: variables.orgId, brandId: variables.brandId });\n const newAssets = createdAssetsResponse.data || [];\n\n queryClient.setQueryData<ListAssetsResponse>(queryKey, (prev) => ({\n data: [...newAssets, ...(prev?.data || [])],\n }));\n },\n });\n};\n"],"names":["usePresignedUploadAsset","queryClient","useQueryClient","useMutation","mutationFn","uploadAssetViaPresignedUrl","onSuccess","createdAssetsResponse","variables","queryKey","assetsKeys","list","orgId","brandId","newAssets","data","setQueryData","prev"],"mappings":"sMAWO,MAAMA,EAA0B,KAKrC,MAAMC,EAAcC,IAEpB,OAAOC,EAA2D,CAChEC,WAAYC,EACZC,UAAW,CAACC,EAAuBC,KACjC,MAAMC,EAAWC,EAAWC,KAAK,CAAEC,MAAOJ,EAAUI,MAAOC,QAASL,EAAUK,UACxEC,EAAYP,EAAsBQ,MAAQ,GAEhDd,EAAYe,aAAiCP,EAAWQ,IAAI,CAC1DF,KAAM,IAAID,KAAeG,GAAMF,MAAQ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var e=require("react/jsx-runtime"),a=require("react"),s=require("@mui/material/styles"),r=require("@mui/material/Box"),l=require("../../atoms/Tab/index.cjs.js"),i=require("../../molecules/Modal/index.cjs.js"),
|
|
1
|
+
"use strict";var e=require("react/jsx-runtime"),a=require("react"),s=require("@mui/material/styles"),r=require("@mui/material/Box"),l=require("../../atoms/Tab/index.cjs.js"),i=require("../../molecules/Modal/index.cjs.js"),o=require("../../molecules/Tabs/index.cjs.js"),t=require("../../../providers/TranslationProvider.cjs.js"),n=require("@tanstack/react-query"),d=require("./hooks/useAssetSelection.cjs.js"),c=require("./hooks/useAssetUploadManager.cjs.js"),u=require("./hooks/useGetAssets.cjs.js"),p=require("./LibraryTabContent/index.cjs.js"),g=require("./UploadTabContent/index.cjs.js");const m=s.styled(r)(({theme:e})=>({height:"calc(70vh - 200px)",overflowY:"auto",marginTop:e.spacing(2),marginRight:e.spacing(-2),marginLeft:e.spacing(-1),marginBottom:e.spacing(-1),paddingRight:e.spacing(2),paddingLeft:e.spacing(1),paddingBottom:e.spacing(1)})),j=new n.QueryClient({defaultOptions:{queries:{refetchOnWindowFocus:!1,retry:2}}}),b=({open:s,onClose:r,onSelect:n,orgId:j,brandId:b,maxSelect:y=1,useNewUploadFlow:h=!1})=>{const[x,v]=a.useState("library"),{translate:q}=t.useTranslation(),{data:C,isLoading:S,error:U}=u.useGetAssets(j,s,b),{selectedAssetsMap:A,handleAssetClick:f,clearSelection:k,addAssetsToSelection:T}=d.useAssetSelection({maxSelect:y}),{uploadedFiles:w,uploadError:_,isUploading:F,handleUpload:I,handleRemove:L,handleSave:M,clearUploadedFiles:E,clearSaveUploadError:R}=c.useAssetUploadManager({orgId:j,brandId:b,useNewUploadFlow:h,onUploadSuccess:e=>{T(e),v("library")},getErrorMessage:e=>e.message||q("Failed_to_upload_assets_try_again")});a.useEffect(()=>{s||(k(),E(),R())},[s,k,E,R]);const B=a.useCallback(()=>{k(),r()},[r,k]),z=a.useCallback(()=>{if(0===A.size)return void r();const e=Array.from(A.values());n&&e.length>0&&n(e),k(),r()},[n,A,r,k]),G=a.useMemo(()=>{const e={label:q("Cancel"),onClick:B,variant:"secondary",tone:"neutral",id:"asset-manager-cancel"},a={library:[e,{label:q("Select"),onClick:z,variant:"primary",id:"asset-manager-select"}],upload:[e,{label:q("Save"),onClick:M,loading:F,variant:"primary",id:"asset-manager-upload"}]};return a[x]??a.library},[x,F,B,z,M,q]);return e.jsxs(i,{actions:G,onClose:B,open:s,size:"large",title:q("library"===x?"Select_Images":"Upload_Images"),children:[e.jsxs(o,{fdKey:"asset-manager-tabs",onChange:(e,a)=>{v(a),E()},value:x,children:[e.jsx(l,{label:q("Upload"),value:"upload"}),e.jsx(l,{label:q("Library"),value:"library"})]}),e.jsx(m,{children:"library"===x?e.jsx(p,{assets:C?.data,error:U,isLoading:S,onAssetClick:f,selectedAssets:A}):e.jsx(g,{error:_,files:w,onRemove:L,onUpload:I})})]})};module.exports=a=>e.jsx(n.QueryClientProvider,{client:j,children:e.jsx(b,{...a})});
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../../../../src/components/organisms/AssetManager/index.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport { styled } from '@mui/material/styles';\n\nimport Box from '@fd/components/atoms/Box';\nimport Tab from '@fd/components/atoms/Tab';\nimport Modal, { type ModalAction } from '@fd/components/molecules/Modal';\nimport Tabs from '@fd/components/molecules/Tabs';\nimport { useTranslation } from '@fd/providers/TranslationProvider';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\n\nimport { useAssetSelection } from './hooks/useAssetSelection';\nimport { useAssetUploadManager } from './hooks/useAssetUploadManager';\nimport { useGetAssets } from './hooks/useGetAssets';\nimport LibraryTabContent from './LibraryTabContent';\nimport type { Asset } from './types/assets.type';\nimport UploadAssetContent from './UploadTabContent';\n/**\n * Scrollable container for the tab content.\n * Allows the asset list to scroll while keeping the modal title, tabs, and actions visible.\n * Uses fixed height to prevent modal resizing when switching tabs.\n */\nconst StyledScrollableContent = styled(Box)(({ theme }) => ({\n height: 'calc(70vh - 200px)', // Fixed height for consistent modal size across tabs\n overflowY: 'auto',\n marginTop: theme.spacing(2),\n marginRight: theme.spacing(-2), // Extend to edge for scrollbar\n marginLeft: theme.spacing(-1), // Allow space for shadows on left\n marginBottom: theme.spacing(-1), // Allow space for shadows on bottom\n paddingRight: theme.spacing(2), // Compensate for negative margin\n paddingLeft: theme.spacing(1), // Compensate for negative margin\n paddingBottom: theme.spacing(1), // Compensate for negative margin\n}));\n\n/**\n * Props for the AssetManager component.\n * Provides a modal interface for selecting images from a grid display.\n * It supports both single and multiple image selection modes with configurable limits.\n *\n * import AssetManager from '@flipdish/portal-library/components/organisms/AssetManager';\n *\n * ```tsx\n * import AssetManager from '@flipdish/portal-library/components/organisms/AssetManager';\n */\nexport interface AssetManagerProps {\n /** Whether the asset manager modal is open */\n open: boolean;\n /** Callback function when the modal is closed or cancelled */\n onClose: () => void;\n /** Callback function when assets are selected */\n onSelect?: (selectedAssets: Asset[]) => void;\n /**\n * Organization ID for fetching assets\n */\n orgId: string;\n /** Brand ID for fetching assets */\n brandId?: string;\n /** Maximum number of assets that can be selected. Defaults to 1 */\n maxSelect?: number;\n}\n\n// Create a QueryClient instance for the AssetManager component\n// This allows the component to be used without requiring clients to wrap it in QueryClientProvider\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n refetchOnWindowFocus: false,\n retry: 2,\n },\n },\n});\n\n/**\n * Inner component that contains the AssetManager logic.\n * Must be rendered inside QueryClientProvider to use React Query hooks.\n */\nconst AssetManagerContent: React.FC<AssetManagerProps> = ({\n open,\n onClose,\n onSelect,\n orgId,\n brandId,\n maxSelect = 1,\n}) => {\n const [selectedTab, setSelectedTab] = useState<string>('library');\n const { translate } = useTranslation();\n\n // Fetch assets at the parent level to enable future reuse across tabs\n const {\n data: assetResponse,\n isLoading: isLoadingAssets,\n error: assetsError,\n } = useGetAssets(orgId, open, brandId);\n\n // Asset selection hook - manages selected assets from library\n const { selectedAssetsMap, handleAssetClick, clearSelection, addAssetsToSelection } = useAssetSelection({\n maxSelect,\n });\n\n // Asset upload hook - manages file uploads and upload state\n const {\n uploadedFiles,\n uploadError,\n isUploading,\n handleUpload,\n handleRemove,\n handleSave,\n clearUploadedFiles,\n clearSaveUploadError,\n } = useAssetUploadManager({\n orgId,\n brandId,\n onUploadSuccess: (uploadedAssets) => {\n // Add uploaded assets to selection and switch to library tab\n addAssetsToSelection(uploadedAssets);\n setSelectedTab('library');\n },\n getErrorMessage: () => translate('Failed_to_upload_assets_try_again'),\n });\n\n // Reset state when modal opens/closes\n useEffect(() => {\n if (!open) {\n clearSelection();\n clearUploadedFiles();\n clearSaveUploadError();\n }\n }, [open, clearSelection, clearUploadedFiles, clearSaveUploadError]);\n\n const handleCancel = useCallback((): void => {\n clearSelection();\n onClose();\n }, [onClose, clearSelection]);\n\n const handleSelectAssets = useCallback((): void => {\n if (selectedAssetsMap.size === 0) {\n onClose();\n return;\n }\n const selectedAssets = Array.from(selectedAssetsMap.values());\n if (onSelect && selectedAssets.length > 0) {\n onSelect(selectedAssets);\n }\n clearSelection();\n onClose();\n }, [onSelect, selectedAssetsMap, onClose, clearSelection]);\n\n const getTitle = (): string => {\n switch (selectedTab) {\n case 'library':\n return translate('Select_Images');\n default:\n return translate('Upload_Images');\n }\n };\n\n // Tab-specific action button configurations\n const actionButtons = useMemo<ModalAction[]>(() => {\n // Common cancel button configuration\n const cancelButton: ModalAction = {\n label: translate('Cancel'),\n onClick: handleCancel,\n variant: 'secondary',\n tone: 'neutral',\n id: 'asset-manager-cancel',\n };\n\n const tabActions: Record<string, ModalAction[]> = {\n library: [\n cancelButton,\n {\n label: translate('Select'),\n onClick: handleSelectAssets,\n variant: 'primary',\n id: 'asset-manager-select',\n },\n ],\n upload: [\n cancelButton,\n {\n label: translate('Save'),\n onClick: handleSave,\n loading: isUploading,\n variant: 'primary',\n id: 'asset-manager-upload',\n },\n ],\n };\n\n return tabActions[selectedTab] ?? (tabActions['library'] as ModalAction[]);\n }, [selectedTab, isUploading, handleCancel, handleSelectAssets, handleSave, translate]);\n\n return (\n <Modal actions={actionButtons} onClose={handleCancel} open={open} size=\"large\" title={getTitle()}>\n <Tabs\n fdKey=\"asset-manager-tabs\"\n onChange={(_, value: string) => {\n setSelectedTab(value);\n clearUploadedFiles();\n }}\n value={selectedTab}\n >\n <Tab label={translate('Upload')} value=\"upload\" />\n <Tab label={translate('Library')} value=\"library\" />\n </Tabs>\n <StyledScrollableContent>\n {selectedTab === 'library' ? (\n <LibraryTabContent\n assets={assetResponse?.data}\n error={assetsError}\n isLoading={isLoadingAssets}\n onAssetClick={handleAssetClick}\n selectedAssets={selectedAssetsMap}\n />\n ) : (\n <UploadAssetContent\n error={uploadError}\n files={uploadedFiles}\n onRemove={handleRemove}\n onUpload={handleUpload}\n />\n )}\n </StyledScrollableContent>\n </Modal>\n );\n};\n\n/**\n * AssetManager component used to select assets from the library or upload new assets.\n * Can manage assets at a organization or brand level.\n *\n * @example\n * ```tsx\n * <AssetManager\n * open={isOpen}\n * onClose={() => setIsOpen(false)}\n * onSelect={(assets) => console.log(assets)}\n * orgId=\"org123\"\n * brandId=\"brand456\"\n * maxSelect={3}\n * />\n * ```\n */\nconst AssetManager: React.FC<AssetManagerProps> = (props) => {\n return (\n <QueryClientProvider client={queryClient}>\n <AssetManagerContent {...props} />\n </QueryClientProvider>\n );\n};\n\nexport type { Asset, AssetMetadata, AssetMetadataDimensions, AssetType } from './types/assets.type';\nexport default AssetManager;\n"],"names":["StyledScrollableContent","styled","Box","theme","height","overflowY","marginTop","spacing","marginRight","marginLeft","marginBottom","paddingRight","paddingLeft","paddingBottom","queryClient","QueryClient","defaultOptions","queries","refetchOnWindowFocus","retry","AssetManagerContent","open","onClose","onSelect","orgId","brandId","maxSelect","selectedTab","setSelectedTab","useState","translate","useTranslation","data","assetResponse","isLoading","isLoadingAssets","error","assetsError","useGetAssets","selectedAssetsMap","handleAssetClick","clearSelection","addAssetsToSelection","useAssetSelection","uploadedFiles","uploadError","isUploading","handleUpload","handleRemove","handleSave","clearUploadedFiles","clearSaveUploadError","useAssetUploadManager","onUploadSuccess","uploadedAssets","getErrorMessage","useEffect","handleCancel","useCallback","handleSelectAssets","size","selectedAssets","Array","from","values","length","actionButtons","useMemo","cancelButton","label","onClick","variant","tone","id","tabActions","library","upload","loading","_jsxs","Modal","actions","title","Tabs","fdKey","onChange","_","value","_jsx","Tab","children","LibraryTabContent","assets","onAssetClick","UploadAssetContent","files","onRemove","onUpload","props","QueryClientProvider","client"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../../../../src/components/organisms/AssetManager/index.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport { styled } from '@mui/material/styles';\n\nimport Box from '@fd/components/atoms/Box';\nimport Tab from '@fd/components/atoms/Tab';\nimport Modal, { type ModalAction } from '@fd/components/molecules/Modal';\nimport Tabs from '@fd/components/molecules/Tabs';\nimport { useTranslation } from '@fd/providers/TranslationProvider';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\n\nimport { useAssetSelection } from './hooks/useAssetSelection';\nimport { useAssetUploadManager } from './hooks/useAssetUploadManager';\nimport { useGetAssets } from './hooks/useGetAssets';\nimport LibraryTabContent from './LibraryTabContent';\nimport type { Asset } from './types/assets.type';\nimport UploadAssetContent from './UploadTabContent';\n/**\n * Scrollable container for the tab content.\n * Allows the asset list to scroll while keeping the modal title, tabs, and actions visible.\n * Uses fixed height to prevent modal resizing when switching tabs.\n */\nconst StyledScrollableContent = styled(Box)(({ theme }) => ({\n height: 'calc(70vh - 200px)', // Fixed height for consistent modal size across tabs\n overflowY: 'auto',\n marginTop: theme.spacing(2),\n marginRight: theme.spacing(-2), // Extend to edge for scrollbar\n marginLeft: theme.spacing(-1), // Allow space for shadows on left\n marginBottom: theme.spacing(-1), // Allow space for shadows on bottom\n paddingRight: theme.spacing(2), // Compensate for negative margin\n paddingLeft: theme.spacing(1), // Compensate for negative margin\n paddingBottom: theme.spacing(1), // Compensate for negative margin\n}));\n\n/**\n * Props for the AssetManager component.\n * Provides a modal interface for selecting images from a grid display.\n * It supports both single and multiple image selection modes with configurable limits.\n *\n * import AssetManager from '@flipdish/portal-library/components/organisms/AssetManager';\n *\n * ```tsx\n * import AssetManager from '@flipdish/portal-library/components/organisms/AssetManager';\n */\nexport interface AssetManagerProps {\n /** Whether the asset manager modal is open */\n open: boolean;\n /** Callback function when the modal is closed or cancelled */\n onClose: () => void;\n /** Callback function when assets are selected */\n onSelect?: (selectedAssets: Asset[]) => void;\n /**\n * Organization ID for fetching assets\n */\n orgId: string;\n /** Brand ID for fetching assets */\n brandId?: string;\n /** Maximum number of assets that can be selected. Defaults to 1 */\n maxSelect?: number;\n /** When true, uploads via presigned S3 URLs (supports files up to 20 MB). Defaults to false */\n useNewUploadFlow?: boolean;\n}\n\n// Create a QueryClient instance for the AssetManager component\n// This allows the component to be used without requiring clients to wrap it in QueryClientProvider\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n refetchOnWindowFocus: false,\n retry: 2,\n },\n },\n});\n\n/**\n * Inner component that contains the AssetManager logic.\n * Must be rendered inside QueryClientProvider to use React Query hooks.\n */\nconst AssetManagerContent: React.FC<AssetManagerProps> = ({\n open,\n onClose,\n onSelect,\n orgId,\n brandId,\n maxSelect = 1,\n useNewUploadFlow = false,\n}) => {\n const [selectedTab, setSelectedTab] = useState<string>('library');\n const { translate } = useTranslation();\n\n // Fetch assets at the parent level to enable future reuse across tabs\n const {\n data: assetResponse,\n isLoading: isLoadingAssets,\n error: assetsError,\n } = useGetAssets(orgId, open, brandId);\n\n // Asset selection hook - manages selected assets from library\n const { selectedAssetsMap, handleAssetClick, clearSelection, addAssetsToSelection } = useAssetSelection({\n maxSelect,\n });\n\n // Asset upload hook - manages file uploads and upload state\n const {\n uploadedFiles,\n uploadError,\n isUploading,\n handleUpload,\n handleRemove,\n handleSave,\n clearUploadedFiles,\n clearSaveUploadError,\n } = useAssetUploadManager({\n orgId,\n brandId,\n useNewUploadFlow,\n onUploadSuccess: (uploadedAssets) => {\n // Add uploaded assets to selection and switch to library tab\n addAssetsToSelection(uploadedAssets);\n setSelectedTab('library');\n },\n getErrorMessage: (error: Error) => error.message || translate('Failed_to_upload_assets_try_again'),\n });\n\n // Reset state when modal opens/closes\n useEffect(() => {\n if (!open) {\n clearSelection();\n clearUploadedFiles();\n clearSaveUploadError();\n }\n }, [open, clearSelection, clearUploadedFiles, clearSaveUploadError]);\n\n const handleCancel = useCallback((): void => {\n clearSelection();\n onClose();\n }, [onClose, clearSelection]);\n\n const handleSelectAssets = useCallback((): void => {\n if (selectedAssetsMap.size === 0) {\n onClose();\n return;\n }\n const selectedAssets = Array.from(selectedAssetsMap.values());\n if (onSelect && selectedAssets.length > 0) {\n onSelect(selectedAssets);\n }\n clearSelection();\n onClose();\n }, [onSelect, selectedAssetsMap, onClose, clearSelection]);\n\n const getTitle = (): string => {\n switch (selectedTab) {\n case 'library':\n return translate('Select_Images');\n default:\n return translate('Upload_Images');\n }\n };\n\n // Tab-specific action button configurations\n const actionButtons = useMemo<ModalAction[]>(() => {\n // Common cancel button configuration\n const cancelButton: ModalAction = {\n label: translate('Cancel'),\n onClick: handleCancel,\n variant: 'secondary',\n tone: 'neutral',\n id: 'asset-manager-cancel',\n };\n\n const tabActions: Record<string, ModalAction[]> = {\n library: [\n cancelButton,\n {\n label: translate('Select'),\n onClick: handleSelectAssets,\n variant: 'primary',\n id: 'asset-manager-select',\n },\n ],\n upload: [\n cancelButton,\n {\n label: translate('Save'),\n onClick: handleSave,\n loading: isUploading,\n variant: 'primary',\n id: 'asset-manager-upload',\n },\n ],\n };\n\n return tabActions[selectedTab] ?? (tabActions['library'] as ModalAction[]);\n }, [selectedTab, isUploading, handleCancel, handleSelectAssets, handleSave, translate]);\n\n return (\n <Modal actions={actionButtons} onClose={handleCancel} open={open} size=\"large\" title={getTitle()}>\n <Tabs\n fdKey=\"asset-manager-tabs\"\n onChange={(_, value: string) => {\n setSelectedTab(value);\n clearUploadedFiles();\n }}\n value={selectedTab}\n >\n <Tab label={translate('Upload')} value=\"upload\" />\n <Tab label={translate('Library')} value=\"library\" />\n </Tabs>\n <StyledScrollableContent>\n {selectedTab === 'library' ? (\n <LibraryTabContent\n assets={assetResponse?.data}\n error={assetsError}\n isLoading={isLoadingAssets}\n onAssetClick={handleAssetClick}\n selectedAssets={selectedAssetsMap}\n />\n ) : (\n <UploadAssetContent\n error={uploadError}\n files={uploadedFiles}\n onRemove={handleRemove}\n onUpload={handleUpload}\n />\n )}\n </StyledScrollableContent>\n </Modal>\n );\n};\n\n/**\n * AssetManager component used to select assets from the library or upload new assets.\n * Can manage assets at a organization or brand level.\n *\n * @example\n * ```tsx\n * <AssetManager\n * open={isOpen}\n * onClose={() => setIsOpen(false)}\n * onSelect={(assets) => console.log(assets)}\n * orgId=\"org123\"\n * brandId=\"brand456\"\n * maxSelect={3}\n * />\n * ```\n */\nconst AssetManager: React.FC<AssetManagerProps> = (props) => {\n return (\n <QueryClientProvider client={queryClient}>\n <AssetManagerContent {...props} />\n </QueryClientProvider>\n );\n};\n\nexport type { Asset, AssetMetadata, AssetMetadataDimensions, AssetType } from './types/assets.type';\nexport default AssetManager;\n"],"names":["StyledScrollableContent","styled","Box","theme","height","overflowY","marginTop","spacing","marginRight","marginLeft","marginBottom","paddingRight","paddingLeft","paddingBottom","queryClient","QueryClient","defaultOptions","queries","refetchOnWindowFocus","retry","AssetManagerContent","open","onClose","onSelect","orgId","brandId","maxSelect","useNewUploadFlow","selectedTab","setSelectedTab","useState","translate","useTranslation","data","assetResponse","isLoading","isLoadingAssets","error","assetsError","useGetAssets","selectedAssetsMap","handleAssetClick","clearSelection","addAssetsToSelection","useAssetSelection","uploadedFiles","uploadError","isUploading","handleUpload","handleRemove","handleSave","clearUploadedFiles","clearSaveUploadError","useAssetUploadManager","onUploadSuccess","uploadedAssets","getErrorMessage","message","useEffect","handleCancel","useCallback","handleSelectAssets","size","selectedAssets","Array","from","values","length","actionButtons","useMemo","cancelButton","label","onClick","variant","tone","id","tabActions","library","upload","loading","_jsxs","Modal","actions","title","Tabs","fdKey","onChange","_","value","_jsx","Tab","children","LibraryTabContent","assets","onAssetClick","UploadAssetContent","files","onRemove","onUpload","props","QueryClientProvider","client"],"mappings":"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"}
|
|
@@ -27,6 +27,8 @@ interface AssetManagerProps {
|
|
|
27
27
|
brandId?: string;
|
|
28
28
|
/** Maximum number of assets that can be selected. Defaults to 1 */
|
|
29
29
|
maxSelect?: number;
|
|
30
|
+
/** When true, uploads via presigned S3 URLs (supports files up to 20 MB). Defaults to false */
|
|
31
|
+
useNewUploadFlow?: boolean;
|
|
30
32
|
}
|
|
31
33
|
/**
|
|
32
34
|
* AssetManager component used to select assets from the library or upload new assets.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jsx as e,jsxs as a}from"react/jsx-runtime";import{useState as r,useEffect as o,useCallback as s,useMemo as t}from"react";import{styled as
|
|
1
|
+
import{jsx as e,jsxs as a}from"react/jsx-runtime";import{useState as r,useEffect as o,useCallback as s,useMemo as t}from"react";import{styled as l}from"@mui/material/styles";import i from"@mui/material/Box";import n from"../../atoms/Tab/index.js";import d from"../../molecules/Modal/index.js";import m from"../../molecules/Tabs/index.js";import{useTranslation as c}from"../../../providers/TranslationProvider.js";import{QueryClient as p,QueryClientProvider as g}from"@tanstack/react-query";import{useAssetSelection as u}from"./hooks/useAssetSelection.js";import{useAssetUploadManager as f}from"./hooks/useAssetUploadManager.js";import{useGetAssets as b}from"./hooks/useGetAssets.js";import h from"./LibraryTabContent/index.js";import y from"./UploadTabContent/index.js";const v=l(i)(({theme:e})=>({height:"calc(70vh - 200px)",overflowY:"auto",marginTop:e.spacing(2),marginRight:e.spacing(-2),marginLeft:e.spacing(-1),marginBottom:e.spacing(-1),paddingRight:e.spacing(2),paddingLeft:e.spacing(1),paddingBottom:e.spacing(1)})),S=new p({defaultOptions:{queries:{refetchOnWindowFocus:!1,retry:2}}}),U=({open:l,onClose:i,onSelect:p,orgId:g,brandId:S,maxSelect:U=1,useNewUploadFlow:x=!1})=>{const[C,j]=r("library"),{translate:k}=c(),{data:A,isLoading:w,error:T}=b(g,l,S),{selectedAssetsMap:_,handleAssetClick:F,clearSelection:I,addAssetsToSelection:L}=u({maxSelect:U}),{uploadedFiles:M,uploadError:R,isUploading:B,handleUpload:E,handleRemove:q,handleSave:z,clearUploadedFiles:N,clearSaveUploadError:O}=f({orgId:g,brandId:S,useNewUploadFlow:x,onUploadSuccess:e=>{L(e),j("library")},getErrorMessage:e=>e.message||k("Failed_to_upload_assets_try_again")});o(()=>{l||(I(),N(),O())},[l,I,N,O]);const G=s(()=>{I(),i()},[i,I]),K=s(()=>{if(0===_.size)return void i();const e=Array.from(_.values());p&&e.length>0&&p(e),I(),i()},[p,_,i,I]),P=t(()=>{const e={label:k("Cancel"),onClick:G,variant:"secondary",tone:"neutral",id:"asset-manager-cancel"},a={library:[e,{label:k("Select"),onClick:K,variant:"primary",id:"asset-manager-select"}],upload:[e,{label:k("Save"),onClick:z,loading:B,variant:"primary",id:"asset-manager-upload"}]};return a[C]??a.library},[C,B,G,K,z,k]);return a(d,{actions:P,onClose:G,open:l,size:"large",title:k("library"===C?"Select_Images":"Upload_Images"),children:[a(m,{fdKey:"asset-manager-tabs",onChange:(e,a)=>{j(a),N()},value:C,children:[e(n,{label:k("Upload"),value:"upload"}),e(n,{label:k("Library"),value:"library"})]}),e(v,{children:"library"===C?e(h,{assets:A?.data,error:T,isLoading:w,onAssetClick:F,selectedAssets:_}):e(y,{error:R,files:M,onRemove:q,onUpload:E})})]})},x=a=>e(g,{client:S,children:e(U,{...a})});export{x as default};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../src/components/organisms/AssetManager/index.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport { styled } from '@mui/material/styles';\n\nimport Box from '@fd/components/atoms/Box';\nimport Tab from '@fd/components/atoms/Tab';\nimport Modal, { type ModalAction } from '@fd/components/molecules/Modal';\nimport Tabs from '@fd/components/molecules/Tabs';\nimport { useTranslation } from '@fd/providers/TranslationProvider';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\n\nimport { useAssetSelection } from './hooks/useAssetSelection';\nimport { useAssetUploadManager } from './hooks/useAssetUploadManager';\nimport { useGetAssets } from './hooks/useGetAssets';\nimport LibraryTabContent from './LibraryTabContent';\nimport type { Asset } from './types/assets.type';\nimport UploadAssetContent from './UploadTabContent';\n/**\n * Scrollable container for the tab content.\n * Allows the asset list to scroll while keeping the modal title, tabs, and actions visible.\n * Uses fixed height to prevent modal resizing when switching tabs.\n */\nconst StyledScrollableContent = styled(Box)(({ theme }) => ({\n height: 'calc(70vh - 200px)', // Fixed height for consistent modal size across tabs\n overflowY: 'auto',\n marginTop: theme.spacing(2),\n marginRight: theme.spacing(-2), // Extend to edge for scrollbar\n marginLeft: theme.spacing(-1), // Allow space for shadows on left\n marginBottom: theme.spacing(-1), // Allow space for shadows on bottom\n paddingRight: theme.spacing(2), // Compensate for negative margin\n paddingLeft: theme.spacing(1), // Compensate for negative margin\n paddingBottom: theme.spacing(1), // Compensate for negative margin\n}));\n\n/**\n * Props for the AssetManager component.\n * Provides a modal interface for selecting images from a grid display.\n * It supports both single and multiple image selection modes with configurable limits.\n *\n * import AssetManager from '@flipdish/portal-library/components/organisms/AssetManager';\n *\n * ```tsx\n * import AssetManager from '@flipdish/portal-library/components/organisms/AssetManager';\n */\nexport interface AssetManagerProps {\n /** Whether the asset manager modal is open */\n open: boolean;\n /** Callback function when the modal is closed or cancelled */\n onClose: () => void;\n /** Callback function when assets are selected */\n onSelect?: (selectedAssets: Asset[]) => void;\n /**\n * Organization ID for fetching assets\n */\n orgId: string;\n /** Brand ID for fetching assets */\n brandId?: string;\n /** Maximum number of assets that can be selected. Defaults to 1 */\n maxSelect?: number;\n}\n\n// Create a QueryClient instance for the AssetManager component\n// This allows the component to be used without requiring clients to wrap it in QueryClientProvider\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n refetchOnWindowFocus: false,\n retry: 2,\n },\n },\n});\n\n/**\n * Inner component that contains the AssetManager logic.\n * Must be rendered inside QueryClientProvider to use React Query hooks.\n */\nconst AssetManagerContent: React.FC<AssetManagerProps> = ({\n open,\n onClose,\n onSelect,\n orgId,\n brandId,\n maxSelect = 1,\n}) => {\n const [selectedTab, setSelectedTab] = useState<string>('library');\n const { translate } = useTranslation();\n\n // Fetch assets at the parent level to enable future reuse across tabs\n const {\n data: assetResponse,\n isLoading: isLoadingAssets,\n error: assetsError,\n } = useGetAssets(orgId, open, brandId);\n\n // Asset selection hook - manages selected assets from library\n const { selectedAssetsMap, handleAssetClick, clearSelection, addAssetsToSelection } = useAssetSelection({\n maxSelect,\n });\n\n // Asset upload hook - manages file uploads and upload state\n const {\n uploadedFiles,\n uploadError,\n isUploading,\n handleUpload,\n handleRemove,\n handleSave,\n clearUploadedFiles,\n clearSaveUploadError,\n } = useAssetUploadManager({\n orgId,\n brandId,\n onUploadSuccess: (uploadedAssets) => {\n // Add uploaded assets to selection and switch to library tab\n addAssetsToSelection(uploadedAssets);\n setSelectedTab('library');\n },\n getErrorMessage: () => translate('Failed_to_upload_assets_try_again'),\n });\n\n // Reset state when modal opens/closes\n useEffect(() => {\n if (!open) {\n clearSelection();\n clearUploadedFiles();\n clearSaveUploadError();\n }\n }, [open, clearSelection, clearUploadedFiles, clearSaveUploadError]);\n\n const handleCancel = useCallback((): void => {\n clearSelection();\n onClose();\n }, [onClose, clearSelection]);\n\n const handleSelectAssets = useCallback((): void => {\n if (selectedAssetsMap.size === 0) {\n onClose();\n return;\n }\n const selectedAssets = Array.from(selectedAssetsMap.values());\n if (onSelect && selectedAssets.length > 0) {\n onSelect(selectedAssets);\n }\n clearSelection();\n onClose();\n }, [onSelect, selectedAssetsMap, onClose, clearSelection]);\n\n const getTitle = (): string => {\n switch (selectedTab) {\n case 'library':\n return translate('Select_Images');\n default:\n return translate('Upload_Images');\n }\n };\n\n // Tab-specific action button configurations\n const actionButtons = useMemo<ModalAction[]>(() => {\n // Common cancel button configuration\n const cancelButton: ModalAction = {\n label: translate('Cancel'),\n onClick: handleCancel,\n variant: 'secondary',\n tone: 'neutral',\n id: 'asset-manager-cancel',\n };\n\n const tabActions: Record<string, ModalAction[]> = {\n library: [\n cancelButton,\n {\n label: translate('Select'),\n onClick: handleSelectAssets,\n variant: 'primary',\n id: 'asset-manager-select',\n },\n ],\n upload: [\n cancelButton,\n {\n label: translate('Save'),\n onClick: handleSave,\n loading: isUploading,\n variant: 'primary',\n id: 'asset-manager-upload',\n },\n ],\n };\n\n return tabActions[selectedTab] ?? (tabActions['library'] as ModalAction[]);\n }, [selectedTab, isUploading, handleCancel, handleSelectAssets, handleSave, translate]);\n\n return (\n <Modal actions={actionButtons} onClose={handleCancel} open={open} size=\"large\" title={getTitle()}>\n <Tabs\n fdKey=\"asset-manager-tabs\"\n onChange={(_, value: string) => {\n setSelectedTab(value);\n clearUploadedFiles();\n }}\n value={selectedTab}\n >\n <Tab label={translate('Upload')} value=\"upload\" />\n <Tab label={translate('Library')} value=\"library\" />\n </Tabs>\n <StyledScrollableContent>\n {selectedTab === 'library' ? (\n <LibraryTabContent\n assets={assetResponse?.data}\n error={assetsError}\n isLoading={isLoadingAssets}\n onAssetClick={handleAssetClick}\n selectedAssets={selectedAssetsMap}\n />\n ) : (\n <UploadAssetContent\n error={uploadError}\n files={uploadedFiles}\n onRemove={handleRemove}\n onUpload={handleUpload}\n />\n )}\n </StyledScrollableContent>\n </Modal>\n );\n};\n\n/**\n * AssetManager component used to select assets from the library or upload new assets.\n * Can manage assets at a organization or brand level.\n *\n * @example\n * ```tsx\n * <AssetManager\n * open={isOpen}\n * onClose={() => setIsOpen(false)}\n * onSelect={(assets) => console.log(assets)}\n * orgId=\"org123\"\n * brandId=\"brand456\"\n * maxSelect={3}\n * />\n * ```\n */\nconst AssetManager: React.FC<AssetManagerProps> = (props) => {\n return (\n <QueryClientProvider client={queryClient}>\n <AssetManagerContent {...props} />\n </QueryClientProvider>\n );\n};\n\nexport type { Asset, AssetMetadata, AssetMetadataDimensions, AssetType } from './types/assets.type';\nexport default AssetManager;\n"],"names":["StyledScrollableContent","styled","Box","theme","height","overflowY","marginTop","spacing","marginRight","marginLeft","marginBottom","paddingRight","paddingLeft","paddingBottom","queryClient","QueryClient","defaultOptions","queries","refetchOnWindowFocus","retry","AssetManagerContent","open","onClose","onSelect","orgId","brandId","maxSelect","selectedTab","setSelectedTab","useState","translate","useTranslation","data","assetResponse","isLoading","isLoadingAssets","error","assetsError","useGetAssets","selectedAssetsMap","handleAssetClick","clearSelection","addAssetsToSelection","useAssetSelection","uploadedFiles","uploadError","isUploading","handleUpload","handleRemove","handleSave","clearUploadedFiles","clearSaveUploadError","useAssetUploadManager","onUploadSuccess","uploadedAssets","getErrorMessage","useEffect","handleCancel","useCallback","handleSelectAssets","size","selectedAssets","Array","from","values","length","actionButtons","useMemo","cancelButton","label","onClick","variant","tone","id","tabActions","library","upload","loading","_jsxs","Modal","actions","title","Tabs","fdKey","onChange","_","value","_jsx","Tab","children","LibraryTabContent","assets","onAssetClick","UploadAssetContent","files","onRemove","onUpload","AssetManager","props","QueryClientProvider","client"],"mappings":"kwBAsBA,MAAMA,EAA0BC,EAAOC,EAAPD,CAAY,EAAGE,YAAO,CACpDC,OAAQ,qBACRC,UAAW,OACXC,UAAWH,EAAMI,QAAQ,GACzBC,YAAaL,EAAMI,YACnBE,WAAYN,EAAMI,YAClBG,aAAcP,EAAMI,YACpBI,aAAcR,EAAMI,QAAQ,GAC5BK,YAAaT,EAAMI,QAAQ,GAC3BM,cAAeV,EAAMI,QAAQ,MAgCzBO,EAAc,IAAIC,EAAY,CAClCC,eAAgB,CACdC,QAAS,CACPC,sBAAsB,EACtBC,MAAO,MASPC,EAAmD,EACvDC,OACAC,UACAC,WACAC,QACAC,UACAC,YAAY,MAEZ,MAAOC,EAAaC,GAAkBC,EAAiB,YACjDC,UAAEA,GAAcC,KAIpBC,KAAMC,EACNC,UAAWC,EACXC,MAAOC,GACLC,EAAad,EAAOH,EAAMI,IAGxBc,kBAAEA,EAAiBC,iBAAEA,EAAgBC,eAAEA,EAAcC,qBAAEA,GAAyBC,EAAkB,CACtGjB,eAIIkB,cACJA,EAAaC,YACbA,EAAWC,YACXA,EAAWC,aACXA,EAAYC,aACZA,EAAYC,WACZA,EAAUC,mBACVA,EAAkBC,qBAClBA,GACEC,EAAsB,CACxB5B,QACAC,UACA4B,gBAAkBC,IAEhBZ,EAAqBY,GACrB1B,EAAe,YAEjB2B,gBAAiB,IAAMzB,EAAU,uCAInC0B,EAAU,KACHnC,IACHoB,IACAS,IACAC,MAED,CAAC9B,EAAMoB,EAAgBS,EAAoBC,IAE9C,MAAMM,EAAeC,EAAY,KAC/BjB,IACAnB,KACC,CAACA,EAASmB,IAEPkB,EAAqBD,EAAY,KACrC,GAA+B,IAA3BnB,EAAkBqB,KAEpB,YADAtC,IAGF,MAAMuC,EAAiBC,MAAMC,KAAKxB,EAAkByB,UAChDzC,GAAYsC,EAAeI,OAAS,GACtC1C,EAASsC,GAEXpB,IACAnB,KACC,CAACC,EAAUgB,EAAmBjB,EAASmB,IAYpCyB,EAAgBC,EAAuB,KAE3C,MAAMC,EAA4B,CAChCC,MAAOvC,EAAU,UACjBwC,QAASb,EACTc,QAAS,YACTC,KAAM,UACNC,GAAI,wBAGAC,EAA4C,CAChDC,QAAS,CACPP,EACA,CACEC,MAAOvC,EAAU,UACjBwC,QAASX,EACTY,QAAS,UACTE,GAAI,yBAGRG,OAAQ,CACNR,EACA,CACEC,MAAOvC,EAAU,QACjBwC,QAASrB,EACT4B,QAAS/B,EACTyB,QAAS,UACTE,GAAI,0BAKV,OAAOC,EAAW/C,IAAiB+C,EAAoB,SACtD,CAAC/C,EAAamB,EAAaW,EAAcE,EAAoBV,EAAYnB,IAE5E,OACEgD,EAACC,EAAK,CAACC,QAASd,EAAe5C,QAASmC,EAAcpC,KAAMA,EAAMuC,KAAK,QAAQqB,MA3CpEnD,EADJ,YADCH,EAEa,gBAEA,2BA0CnBmD,EAACI,GACCC,MAAM,qBACNC,SAAU,CAACC,EAAGC,KACZ1D,EAAe0D,GACfpC,KAEFoC,MAAO3D,YAEP4D,EAACC,GAAInB,MAAOvC,EAAU,UAAWwD,MAAM,WACvCC,EAACC,EAAG,CAACnB,MAAOvC,EAAU,WAAYwD,MAAM,eAE1CC,EAACvF,EAAuB,CAAAyF,SACL,YAAhB9D,EACC4D,EAACG,EAAiB,CAChBC,OAAQ1D,GAAeD,KACvBI,MAAOC,EACPH,UAAWC,EACXyD,aAAcpD,EACdqB,eAAgBtB,IAGlBgD,EAACM,GACCzD,MAAOS,EACPiD,MAAOlD,EACPmD,SAAU/C,EACVgD,SAAUjD,UAwBhBkD,EAA6CC,GAE/CX,EAACY,EAAmB,CAACC,OAAQtF,EAAW2E,SACtCF,EAACnE,EAAmB,IAAK8E"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/components/organisms/AssetManager/index.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport { styled } from '@mui/material/styles';\n\nimport Box from '@fd/components/atoms/Box';\nimport Tab from '@fd/components/atoms/Tab';\nimport Modal, { type ModalAction } from '@fd/components/molecules/Modal';\nimport Tabs from '@fd/components/molecules/Tabs';\nimport { useTranslation } from '@fd/providers/TranslationProvider';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\n\nimport { useAssetSelection } from './hooks/useAssetSelection';\nimport { useAssetUploadManager } from './hooks/useAssetUploadManager';\nimport { useGetAssets } from './hooks/useGetAssets';\nimport LibraryTabContent from './LibraryTabContent';\nimport type { Asset } from './types/assets.type';\nimport UploadAssetContent from './UploadTabContent';\n/**\n * Scrollable container for the tab content.\n * Allows the asset list to scroll while keeping the modal title, tabs, and actions visible.\n * Uses fixed height to prevent modal resizing when switching tabs.\n */\nconst StyledScrollableContent = styled(Box)(({ theme }) => ({\n height: 'calc(70vh - 200px)', // Fixed height for consistent modal size across tabs\n overflowY: 'auto',\n marginTop: theme.spacing(2),\n marginRight: theme.spacing(-2), // Extend to edge for scrollbar\n marginLeft: theme.spacing(-1), // Allow space for shadows on left\n marginBottom: theme.spacing(-1), // Allow space for shadows on bottom\n paddingRight: theme.spacing(2), // Compensate for negative margin\n paddingLeft: theme.spacing(1), // Compensate for negative margin\n paddingBottom: theme.spacing(1), // Compensate for negative margin\n}));\n\n/**\n * Props for the AssetManager component.\n * Provides a modal interface for selecting images from a grid display.\n * It supports both single and multiple image selection modes with configurable limits.\n *\n * import AssetManager from '@flipdish/portal-library/components/organisms/AssetManager';\n *\n * ```tsx\n * import AssetManager from '@flipdish/portal-library/components/organisms/AssetManager';\n */\nexport interface AssetManagerProps {\n /** Whether the asset manager modal is open */\n open: boolean;\n /** Callback function when the modal is closed or cancelled */\n onClose: () => void;\n /** Callback function when assets are selected */\n onSelect?: (selectedAssets: Asset[]) => void;\n /**\n * Organization ID for fetching assets\n */\n orgId: string;\n /** Brand ID for fetching assets */\n brandId?: string;\n /** Maximum number of assets that can be selected. Defaults to 1 */\n maxSelect?: number;\n /** When true, uploads via presigned S3 URLs (supports files up to 20 MB). Defaults to false */\n useNewUploadFlow?: boolean;\n}\n\n// Create a QueryClient instance for the AssetManager component\n// This allows the component to be used without requiring clients to wrap it in QueryClientProvider\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n refetchOnWindowFocus: false,\n retry: 2,\n },\n },\n});\n\n/**\n * Inner component that contains the AssetManager logic.\n * Must be rendered inside QueryClientProvider to use React Query hooks.\n */\nconst AssetManagerContent: React.FC<AssetManagerProps> = ({\n open,\n onClose,\n onSelect,\n orgId,\n brandId,\n maxSelect = 1,\n useNewUploadFlow = false,\n}) => {\n const [selectedTab, setSelectedTab] = useState<string>('library');\n const { translate } = useTranslation();\n\n // Fetch assets at the parent level to enable future reuse across tabs\n const {\n data: assetResponse,\n isLoading: isLoadingAssets,\n error: assetsError,\n } = useGetAssets(orgId, open, brandId);\n\n // Asset selection hook - manages selected assets from library\n const { selectedAssetsMap, handleAssetClick, clearSelection, addAssetsToSelection } = useAssetSelection({\n maxSelect,\n });\n\n // Asset upload hook - manages file uploads and upload state\n const {\n uploadedFiles,\n uploadError,\n isUploading,\n handleUpload,\n handleRemove,\n handleSave,\n clearUploadedFiles,\n clearSaveUploadError,\n } = useAssetUploadManager({\n orgId,\n brandId,\n useNewUploadFlow,\n onUploadSuccess: (uploadedAssets) => {\n // Add uploaded assets to selection and switch to library tab\n addAssetsToSelection(uploadedAssets);\n setSelectedTab('library');\n },\n getErrorMessage: (error: Error) => error.message || translate('Failed_to_upload_assets_try_again'),\n });\n\n // Reset state when modal opens/closes\n useEffect(() => {\n if (!open) {\n clearSelection();\n clearUploadedFiles();\n clearSaveUploadError();\n }\n }, [open, clearSelection, clearUploadedFiles, clearSaveUploadError]);\n\n const handleCancel = useCallback((): void => {\n clearSelection();\n onClose();\n }, [onClose, clearSelection]);\n\n const handleSelectAssets = useCallback((): void => {\n if (selectedAssetsMap.size === 0) {\n onClose();\n return;\n }\n const selectedAssets = Array.from(selectedAssetsMap.values());\n if (onSelect && selectedAssets.length > 0) {\n onSelect(selectedAssets);\n }\n clearSelection();\n onClose();\n }, [onSelect, selectedAssetsMap, onClose, clearSelection]);\n\n const getTitle = (): string => {\n switch (selectedTab) {\n case 'library':\n return translate('Select_Images');\n default:\n return translate('Upload_Images');\n }\n };\n\n // Tab-specific action button configurations\n const actionButtons = useMemo<ModalAction[]>(() => {\n // Common cancel button configuration\n const cancelButton: ModalAction = {\n label: translate('Cancel'),\n onClick: handleCancel,\n variant: 'secondary',\n tone: 'neutral',\n id: 'asset-manager-cancel',\n };\n\n const tabActions: Record<string, ModalAction[]> = {\n library: [\n cancelButton,\n {\n label: translate('Select'),\n onClick: handleSelectAssets,\n variant: 'primary',\n id: 'asset-manager-select',\n },\n ],\n upload: [\n cancelButton,\n {\n label: translate('Save'),\n onClick: handleSave,\n loading: isUploading,\n variant: 'primary',\n id: 'asset-manager-upload',\n },\n ],\n };\n\n return tabActions[selectedTab] ?? (tabActions['library'] as ModalAction[]);\n }, [selectedTab, isUploading, handleCancel, handleSelectAssets, handleSave, translate]);\n\n return (\n <Modal actions={actionButtons} onClose={handleCancel} open={open} size=\"large\" title={getTitle()}>\n <Tabs\n fdKey=\"asset-manager-tabs\"\n onChange={(_, value: string) => {\n setSelectedTab(value);\n clearUploadedFiles();\n }}\n value={selectedTab}\n >\n <Tab label={translate('Upload')} value=\"upload\" />\n <Tab label={translate('Library')} value=\"library\" />\n </Tabs>\n <StyledScrollableContent>\n {selectedTab === 'library' ? (\n <LibraryTabContent\n assets={assetResponse?.data}\n error={assetsError}\n isLoading={isLoadingAssets}\n onAssetClick={handleAssetClick}\n selectedAssets={selectedAssetsMap}\n />\n ) : (\n <UploadAssetContent\n error={uploadError}\n files={uploadedFiles}\n onRemove={handleRemove}\n onUpload={handleUpload}\n />\n )}\n </StyledScrollableContent>\n </Modal>\n );\n};\n\n/**\n * AssetManager component used to select assets from the library or upload new assets.\n * Can manage assets at a organization or brand level.\n *\n * @example\n * ```tsx\n * <AssetManager\n * open={isOpen}\n * onClose={() => setIsOpen(false)}\n * onSelect={(assets) => console.log(assets)}\n * orgId=\"org123\"\n * brandId=\"brand456\"\n * maxSelect={3}\n * />\n * ```\n */\nconst AssetManager: React.FC<AssetManagerProps> = (props) => {\n return (\n <QueryClientProvider client={queryClient}>\n <AssetManagerContent {...props} />\n </QueryClientProvider>\n );\n};\n\nexport type { Asset, AssetMetadata, AssetMetadataDimensions, AssetType } from './types/assets.type';\nexport default AssetManager;\n"],"names":["StyledScrollableContent","styled","Box","theme","height","overflowY","marginTop","spacing","marginRight","marginLeft","marginBottom","paddingRight","paddingLeft","paddingBottom","queryClient","QueryClient","defaultOptions","queries","refetchOnWindowFocus","retry","AssetManagerContent","open","onClose","onSelect","orgId","brandId","maxSelect","useNewUploadFlow","selectedTab","setSelectedTab","useState","translate","useTranslation","data","assetResponse","isLoading","isLoadingAssets","error","assetsError","useGetAssets","selectedAssetsMap","handleAssetClick","clearSelection","addAssetsToSelection","useAssetSelection","uploadedFiles","uploadError","isUploading","handleUpload","handleRemove","handleSave","clearUploadedFiles","clearSaveUploadError","useAssetUploadManager","onUploadSuccess","uploadedAssets","getErrorMessage","message","useEffect","handleCancel","useCallback","handleSelectAssets","size","selectedAssets","Array","from","values","length","actionButtons","useMemo","cancelButton","label","onClick","variant","tone","id","tabActions","library","upload","loading","_jsxs","Modal","actions","title","Tabs","fdKey","onChange","_","value","_jsx","Tab","children","LibraryTabContent","assets","onAssetClick","UploadAssetContent","files","onRemove","onUpload","AssetManager","props","QueryClientProvider","client"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var e=require("../../../../utilities/apiUtils.cjs.js"),t=require("../../../../utilities/fileUtils.cjs.js");const r=()=>{const e=window.location?.host?.includes("portal.flipdish.com");return e?"https://api.portal.flipdish.com/assets":"https://api-prod-staging.portal.flipdishdev.com/assets"},o=async t=>{const{orgId:o,brandId:s,files:a}=t;if(!o)throw new Error("Organization ID is required to request upload");const n=`${r()}/orgs/${encodeURIComponent(o)}/assets/request-upload`,i={files:a.map(e=>({fileName:e.name,contentType:e.type}))};void 0!==s&&(i.brandId=s);try{const e=await fetch(n,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!e.ok){let t=e.statusText;try{const r=await e.json();t=r?.error?.message??r?.Message??t}catch{}throw new Error(t||"Failed to request upload")}return await e.json()}catch(t){const r=e.getErrorMessage(t,"Failed to request upload");throw new Error(r)}},s=async(e,t)=>{const r=await fetch(e,{method:"PUT",headers:{"Content-Type":t.type},body:t});if(!r.ok)throw new Error(`Failed to upload file to storage: ${r.statusText}`)},a=async t=>{const{orgId:o,brandId:s,assetKeys:a}=t;if(!o)throw new Error("Organization ID is required to confirm upload");const n=`${r()}/orgs/${encodeURIComponent(o)}/assets/confirm-upload`,i={assetKeys:a};void 0!==s&&(i.brandId=s);try{const e=await fetch(n,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!e.ok){let t=e.statusText;try{const r=await e.json();t=r?.error?.message??r?.Message??t}catch{}throw new Error(t||"Failed to confirm upload")}return await e.json()}catch(t){const r=e.getErrorMessage(t,"Failed to confirm upload");throw new Error(r)}};exports.confirmUpload=a,exports.getAssets=async(e,t)=>{const o=((e,t)=>{if(!e)return;const o=t?`?brandId=${t}`:"";return`${r()}/orgs/${e}/assets${o}`})(e,t);if(!o)throw new Error("Organization ID is required to fetch assets");const s=await fetch(o,{method:"GET",credentials:"include"});if(!s.ok)throw new Error(`Failed to fetch assets: ${s.statusText}`);return await s.json()},exports.requestUpload=o,exports.uploadAsset=async o=>{const{orgId:s,brandId:a,files:n}=o;if(!s)throw new Error("Organization ID is required to upload assets");if(!n||0===n.length)throw new Error("At least one file is required to upload");const i=n.map(t.sanitizeFile),d=`${r()}/orgs/${encodeURIComponent(s)}/assets/upload`,c=new FormData;for(const e of i)c.append("files",e);void 0!==a&&c.append("brandId",a);try{const e=await fetch(d,{method:"POST",credentials:"include",body:c});if(!e.ok){let t=e.statusText;try{const r=await e.json();t=r?.error?.message??r?.Message??t}catch{}throw new Error(t||"Failed to upload assets")}return await e.json()}catch(t){const r=e.getErrorMessage(t,"Failed to upload assets");throw new Error(r)}},exports.uploadAssetViaPresignedUrl=async r=>{const{orgId:n,brandId:i,files:d}=r;if(!n)throw new Error("Organization ID is required to upload assets");if(!d||0===d.length)throw new Error("At least one file is required to upload");const c=d.map(t.sanitizeFile);try{const{data:e}=await o({orgId:n,brandId:i,files:c}),t=c.map((t,r)=>{const o=e[r];if(!o)throw new Error(`No presigned URL returned for file: ${t.name}`);const s=o.assetKey.split("/").pop()??t.name;return s===t.name?t:new File([t],s,{type:t.type})});await Promise.all(t.map((t,r)=>s(e[r].uploadUrl,t)));const r=e.map(e=>e.assetKey);return await a({orgId:n,brandId:i,assetKeys:r})}catch(t){const r=e.getErrorMessage(t,"Failed to upload assets");throw new Error(r)}},exports.uploadFileToPresignedUrl=s;
|
|
2
2
|
//# sourceMappingURL=asset.service.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset.service.cjs.js","sources":["../../../../../src/components/organisms/AssetManager/services/asset.service.ts"],"sourcesContent":["import { getErrorMessage } from '@fd/utilities/apiUtils';\n\nimport { sanitizeFile } from '../../../../utilities/fileUtils';\nimport type { ListAssetsResponse, UploadAssetResponse } from '../types/assets.type';\n\nconst PROD_API_URL = 'https://api.portal.flipdish.com/assets';\nconst DEV_STAGING_API_URL = 'https://api-prod-staging.portal.flipdishdev.com/assets';\n\n/**\n * Gets the asset management API base URL based on the current environment\n * @returns The appropriate API URL for the current environment\n */\nconst getAssetManagementApiUrl = (): string => {\n const isProd = window.location?.host?.includes('portal.flipdish.com');\n\n if (isProd) {\n return PROD_API_URL;\n }\n\n return DEV_STAGING_API_URL;\n};\n\nconst buildApiUrl = (orgId: string, brandId?: string): string | undefined => {\n if (!orgId) return undefined;\n const brandIdQueryParam = brandId ? `?brandId=${brandId}` : '';\n const baseUrl = getAssetManagementApiUrl();\n return `${baseUrl}/orgs/${orgId}/assets${brandIdQueryParam}`;\n};\n\n/**\n * Fetches assets from the asset management service API\n * @param orgId - organization ID.\n * @param brandId - brand ID.\n * @returns Promise resolving to an array of assets\n */\nexport const getAssets = async (orgId: string, brandId?: string): Promise<ListAssetsResponse> => {\n const apiUrl = buildApiUrl(orgId, brandId);\n\n if (!apiUrl) {\n throw new Error('Organization ID is required to fetch assets');\n }\n\n const response = await fetch(apiUrl, {\n method: 'GET',\n credentials: 'include',\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch assets: ${response.statusText}`);\n }\n\n return (await response.json()) as ListAssetsResponse;\n};\n\n/**\n * Upload parameters for asset upload\n */\nexport interface UploadAssetParams {\n /** Organization ID */\n orgId: string;\n /** Optional brand ID */\n brandId?: string;\n /** The files to upload */\n files: File[];\n}\n\n/**\n * Uploads multiple assets to the asset management service API\n * Sanitizes filenames by replacing spaces with dashes before uploading\n * @param params - Upload parameters containing orgId, brandId, and files\n * @returns Promise resolving to an array of uploaded assets\n */\nexport const uploadAsset = async (params: UploadAssetParams): Promise<UploadAssetResponse> => {\n const { orgId, brandId, files } = params;\n\n if (!orgId) {\n throw new Error('Organization ID is required to upload assets');\n }\n\n if (!files || files.length === 0) {\n throw new Error('At least one file is required to upload');\n }\n\n // Sanitize filenames by replacing spaces with dashes\n const sanitizedFiles = files.map(sanitizeFile);\n\n const baseUrl = getAssetManagementApiUrl();\n const uploadUrl = `${baseUrl}/orgs/${encodeURIComponent(orgId)}/assets/upload`;\n\n const formData = new FormData();\n for (const file of sanitizedFiles) {\n formData.append('files', file);\n }\n if (brandId !== undefined) {\n formData.append('brandId', brandId);\n }\n\n try {\n const response = await fetch(uploadUrl, {\n method: 'POST',\n credentials: 'include',\n body: formData,\n });\n\n if (!response.ok) {\n let message = response.statusText;\n try {\n const body = (await response.json()) as { error?: { message?: string }; Message?: string };\n message = body?.error?.message ?? body?.Message ?? message;\n } catch {\n // ignore JSON parse failure\n }\n throw new Error(message || 'Failed to upload assets');\n }\n\n return (await response.json()) as UploadAssetResponse;\n } catch (error: unknown) {\n const errorMessage = getErrorMessage(error, 'Failed to upload assets');\n throw new Error(errorMessage);\n }\n};\n"],"names":["getAssetManagementApiUrl","isProd","window","location","host","includes","async","orgId","brandId","apiUrl","brandIdQueryParam","buildApiUrl","Error","response","fetch","method","credentials","ok","statusText","json","params","files","length","sanitizedFiles","map","sanitizeFile","uploadUrl","encodeURIComponent","formData","FormData","file","append","undefined","body","message","error","Message","errorMessage","getErrorMessage"],"mappings":"wHAKA,MAOMA,EAA2B,KAC/B,MAAMC,EAASC,OAAOC,UAAUC,MAAMC,SAAS,uBAE/C,OAAIJ,EAVe,yCACO,4EA6BHK,MAAOC,EAAeC,KAC7C,MAAMC,EAdY,EAACF,EAAeC,KAClC,IAAKD,EAAO,OACZ,MAAMG,EAAoBF,EAAU,YAAYA,IAAY,GAE5D,MAAO,GADSR,YACUO,WAAeG,KAU1BC,CAAYJ,EAAOC,GAElC,IAAKC,EACH,MAAM,IAAIG,MAAM,+CAGlB,MAAMC,QAAiBC,MAAML,EAAQ,CACnCM,OAAQ,MACRC,YAAa,YAGf,IAAKH,EAASI,GACZ,MAAM,IAAIL,MAAM,2BAA2BC,EAASK,cAGtD,aAAcL,EAASM,4BAqBEb,MAAOc,IAChC,MAAMb,MAAEA,EAAKC,QAAEA,EAAOa,MAAEA,GAAUD,EAElC,IAAKb,EACH,MAAM,IAAIK,MAAM,gDAGlB,IAAKS,GAA0B,IAAjBA,EAAMC,OAClB,MAAM,IAAIV,MAAM,2CAIlB,MAAMW,EAAiBF,EAAMG,IAAIC,gBAG3BC,EAAY,GADF1B,YACqB2B,mBAAmBpB,mBAElDqB,EAAW,IAAIC,SACrB,IAAK,MAAMC,KAAQP,EACjBK,EAASG,OAAO,QAASD,QAEXE,IAAZxB,GACFoB,EAASG,OAAO,UAAWvB,GAG7B,IACE,MAAMK,QAAiBC,MAAMY,EAAW,CACtCX,OAAQ,OACRC,YAAa,UACbiB,KAAML,IAGR,IAAKf,EAASI,GAAI,CAChB,IAAIiB,EAAUrB,EAASK,WACvB,IACE,MAAMe,QAAcpB,EAASM,OAC7Be,EAAUD,GAAME,OAAOD,SAAWD,GAAMG,SAAWF,CACrD,CAAE,MAEF,CACA,MAAM,IAAItB,MAAMsB,GAAW,0BAC7B,CAEA,aAAcrB,EAASM,MACzB,CAAE,MAAOgB,GACP,MAAME,EAAeC,EAAAA,gBAAgBH,EAAO,2BAC5C,MAAM,IAAIvB,MAAMyB,EAClB"}
|
|
1
|
+
{"version":3,"file":"asset.service.cjs.js","sources":["../../../../../src/components/organisms/AssetManager/services/asset.service.ts"],"sourcesContent":["import { getErrorMessage } from '@fd/utilities/apiUtils';\n\nimport { sanitizeFile } from '../../../../utilities/fileUtils';\nimport type { ListAssetsResponse, UploadAssetResponse } from '../types/assets.type';\n\ninterface PresignedUploadItem {\n uploadUrl: string;\n assetKey: string;\n expiresIn: number;\n}\n\ninterface RequestUploadResponse {\n data: PresignedUploadItem[];\n}\n\ninterface ConfirmUploadParams {\n orgId: string;\n brandId?: string;\n assetKeys: string[];\n}\n\nconst PROD_API_URL = 'https://api.portal.flipdish.com/assets';\nconst DEV_STAGING_API_URL = 'https://api-prod-staging.portal.flipdishdev.com/assets';\n\n/**\n * Gets the asset management API base URL based on the current environment\n * @returns The appropriate API URL for the current environment\n */\nconst getAssetManagementApiUrl = (): string => {\n const isProd = window.location?.host?.includes('portal.flipdish.com');\n\n if (isProd) {\n return PROD_API_URL;\n }\n\n return DEV_STAGING_API_URL;\n};\n\nconst buildApiUrl = (orgId: string, brandId?: string): string | undefined => {\n if (!orgId) return undefined;\n const brandIdQueryParam = brandId ? `?brandId=${brandId}` : '';\n const baseUrl = getAssetManagementApiUrl();\n return `${baseUrl}/orgs/${orgId}/assets${brandIdQueryParam}`;\n};\n\n/**\n * Fetches assets from the asset management service API\n * @param orgId - organization ID.\n * @param brandId - brand ID.\n * @returns Promise resolving to an array of assets\n */\nexport const getAssets = async (orgId: string, brandId?: string): Promise<ListAssetsResponse> => {\n const apiUrl = buildApiUrl(orgId, brandId);\n\n if (!apiUrl) {\n throw new Error('Organization ID is required to fetch assets');\n }\n\n const response = await fetch(apiUrl, {\n method: 'GET',\n credentials: 'include',\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch assets: ${response.statusText}`);\n }\n\n return (await response.json()) as ListAssetsResponse;\n};\n\n/**\n * Upload parameters for asset upload\n */\nexport interface UploadAssetParams {\n /** Organization ID */\n orgId: string;\n /** Optional brand ID */\n brandId?: string;\n /** The files to upload */\n files: File[];\n}\n\n/**\n * Uploads multiple assets to the asset management service API\n * Sanitizes filenames by replacing spaces with dashes before uploading\n * @param params - Upload parameters containing orgId, brandId, and files\n * @returns Promise resolving to an array of uploaded assets\n */\nexport const uploadAsset = async (params: UploadAssetParams): Promise<UploadAssetResponse> => {\n const { orgId, brandId, files } = params;\n\n if (!orgId) {\n throw new Error('Organization ID is required to upload assets');\n }\n\n if (!files || files.length === 0) {\n throw new Error('At least one file is required to upload');\n }\n\n // Sanitize filenames by replacing spaces with dashes\n const sanitizedFiles = files.map(sanitizeFile);\n\n const baseUrl = getAssetManagementApiUrl();\n const uploadUrl = `${baseUrl}/orgs/${encodeURIComponent(orgId)}/assets/upload`;\n\n const formData = new FormData();\n for (const file of sanitizedFiles) {\n formData.append('files', file);\n }\n if (brandId !== undefined) {\n formData.append('brandId', brandId);\n }\n\n try {\n const response = await fetch(uploadUrl, {\n method: 'POST',\n credentials: 'include',\n body: formData,\n });\n\n if (!response.ok) {\n let message = response.statusText;\n try {\n const body = (await response.json()) as { error?: { message?: string }; Message?: string };\n message = body?.error?.message ?? body?.Message ?? message;\n } catch {\n // ignore JSON parse failure\n }\n throw new Error(message || 'Failed to upload assets');\n }\n\n return (await response.json()) as UploadAssetResponse;\n } catch (error: unknown) {\n const errorMessage = getErrorMessage(error, 'Failed to upload assets');\n throw new Error(errorMessage);\n }\n};\n\n/**\n * Requests presigned S3 PUT URLs for direct client-to-S3 uploads.\n * @param params - orgId, optional brandId, and files to upload\n * @returns Presigned upload items containing uploadUrl and assetKey per file\n */\nexport const requestUpload = async (params: UploadAssetParams): Promise<RequestUploadResponse> => {\n const { orgId, brandId, files } = params;\n\n if (!orgId) {\n throw new Error('Organization ID is required to request upload');\n }\n\n const baseUrl = getAssetManagementApiUrl();\n const url = `${baseUrl}/orgs/${encodeURIComponent(orgId)}/assets/request-upload`;\n\n const body: { files: Array<{ fileName: string; contentType: string }>; brandId?: string } = {\n files: files.map((file) => ({ fileName: file.name, contentType: file.type })),\n };\n if (brandId !== undefined) {\n body.brandId = brandId;\n }\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n let message = response.statusText;\n try {\n const responseBody = (await response.json()) as { error?: { message?: string }; Message?: string };\n message = responseBody?.error?.message ?? responseBody?.Message ?? message;\n } catch {\n // ignore JSON parse failure\n }\n throw new Error(message || 'Failed to request upload');\n }\n\n return (await response.json()) as RequestUploadResponse;\n } catch (error: unknown) {\n const errorMessage = getErrorMessage(error, 'Failed to request upload');\n throw new Error(errorMessage);\n }\n};\n\n/**\n * Uploads a single file directly to S3 using a presigned PUT URL.\n * Does not use credentials — the presigned URL is self-authenticating.\n * @param uploadUrl - The presigned S3 PUT URL\n * @param file - The file to upload\n */\nexport const uploadFileToPresignedUrl = async (uploadUrl: string, file: File): Promise<void> => {\n const response = await fetch(uploadUrl, {\n method: 'PUT',\n headers: { 'Content-Type': file.type },\n body: file,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to upload file to storage: ${response.statusText}`);\n }\n};\n\n/**\n * Confirms that files have been uploaded to S3 and creates asset metadata records.\n * @param params - orgId, optional brandId, and S3 asset keys from request-upload\n * @returns The created asset records\n */\nexport const confirmUpload = async (params: ConfirmUploadParams): Promise<UploadAssetResponse> => {\n const { orgId, brandId, assetKeys } = params;\n\n if (!orgId) {\n throw new Error('Organization ID is required to confirm upload');\n }\n\n const baseUrl = getAssetManagementApiUrl();\n const url = `${baseUrl}/orgs/${encodeURIComponent(orgId)}/assets/confirm-upload`;\n\n const body: { assetKeys: string[]; brandId?: string } = { assetKeys };\n if (brandId !== undefined) {\n body.brandId = brandId;\n }\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n let message = response.statusText;\n try {\n const responseBody = (await response.json()) as { error?: { message?: string }; Message?: string };\n message = responseBody?.error?.message ?? responseBody?.Message ?? message;\n } catch {\n // ignore JSON parse failure\n }\n throw new Error(message || 'Failed to confirm upload');\n }\n\n return (await response.json()) as UploadAssetResponse;\n } catch (error: unknown) {\n const errorMessage = getErrorMessage(error, 'Failed to confirm upload');\n throw new Error(errorMessage);\n }\n};\n\n/**\n * Uploads assets via the presigned S3 URL flow:\n * 1. Request presigned URLs from the service\n * 2. PUT each file directly to its S3 presigned URL (in parallel)\n * 3. Confirm uploads to persist asset metadata\n *\n * Supports files up to 20 MB\n * @param params - Upload parameters containing orgId, brandId, and files\n * @returns Promise resolving to the created asset records\n */\nexport const uploadAssetViaPresignedUrl = async (params: UploadAssetParams): Promise<UploadAssetResponse> => {\n const { orgId, brandId, files } = params;\n\n if (!orgId) {\n throw new Error('Organization ID is required to upload assets');\n }\n\n if (!files || files.length === 0) {\n throw new Error('At least one file is required to upload');\n }\n\n // Sanitize filenames (spaces → dashes) before requesting presigned URLs\n const sanitizedFiles = files.map(sanitizeFile);\n\n try {\n // Step 1: Get presigned S3 PUT URLs\n const { data: presignedItems } = await requestUpload({ orgId, brandId, files: sanitizedFiles });\n\n // Step 2: Align file names with the asset keys returned by the server.\n // The server may have deduplicated filenames (e.g. my-file.jpg → my-file-1.jpg),\n // so rename any file whose name doesn't match the end of its asset key.\n const alignedFiles = sanitizedFiles.map((file, index) => {\n const item = presignedItems[index];\n if (!item) {\n throw new Error(`No presigned URL returned for file: ${file.name}`);\n }\n const keyFileName = item.assetKey.split('/').pop() ?? file.name;\n if (keyFileName === file.name) {\n return file;\n }\n return new File([file], keyFileName, { type: file.type });\n });\n\n // Step 3: Upload all files in parallel to their respective presigned URLs\n await Promise.all(\n alignedFiles.map((file, index) => {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return uploadFileToPresignedUrl(presignedItems[index]!.uploadUrl, file);\n }),\n );\n\n // Step 4: Confirm uploads and get asset metadata\n const assetKeys = presignedItems.map((item) => item.assetKey);\n return await confirmUpload({ orgId, brandId, assetKeys });\n } catch (error: unknown) {\n const errorMessage = getErrorMessage(error, 'Failed to upload assets');\n throw new Error(errorMessage);\n }\n};\n"],"names":["getAssetManagementApiUrl","isProd","window","location","host","includes","requestUpload","async","params","orgId","brandId","files","Error","url","encodeURIComponent","body","map","file","fileName","name","contentType","type","undefined","response","fetch","method","credentials","headers","JSON","stringify","ok","message","statusText","responseBody","json","error","Message","errorMessage","getErrorMessage","uploadFileToPresignedUrl","uploadUrl","confirmUpload","assetKeys","apiUrl","brandIdQueryParam","buildApiUrl","length","sanitizedFiles","sanitizeFile","formData","FormData","append","data","presignedItems","alignedFiles","index","item","keyFileName","assetKey","split","pop","File","Promise","all"],"mappings":"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"}
|
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { UploadAssetResponse, ListAssetsResponse } from '@flipdish/asset-management';
|
|
2
2
|
|
|
3
|
+
interface PresignedUploadItem {
|
|
4
|
+
uploadUrl: string;
|
|
5
|
+
assetKey: string;
|
|
6
|
+
expiresIn: number;
|
|
7
|
+
}
|
|
8
|
+
interface RequestUploadResponse {
|
|
9
|
+
data: PresignedUploadItem[];
|
|
10
|
+
}
|
|
11
|
+
interface ConfirmUploadParams {
|
|
12
|
+
orgId: string;
|
|
13
|
+
brandId?: string;
|
|
14
|
+
assetKeys: string[];
|
|
15
|
+
}
|
|
3
16
|
/**
|
|
4
17
|
* Fetches assets from the asset management service API
|
|
5
18
|
* @param orgId - organization ID.
|
|
@@ -25,6 +38,36 @@ interface UploadAssetParams {
|
|
|
25
38
|
* @returns Promise resolving to an array of uploaded assets
|
|
26
39
|
*/
|
|
27
40
|
declare const uploadAsset: (params: UploadAssetParams) => Promise<UploadAssetResponse>;
|
|
41
|
+
/**
|
|
42
|
+
* Requests presigned S3 PUT URLs for direct client-to-S3 uploads.
|
|
43
|
+
* @param params - orgId, optional brandId, and files to upload
|
|
44
|
+
* @returns Presigned upload items containing uploadUrl and assetKey per file
|
|
45
|
+
*/
|
|
46
|
+
declare const requestUpload: (params: UploadAssetParams) => Promise<RequestUploadResponse>;
|
|
47
|
+
/**
|
|
48
|
+
* Uploads a single file directly to S3 using a presigned PUT URL.
|
|
49
|
+
* Does not use credentials — the presigned URL is self-authenticating.
|
|
50
|
+
* @param uploadUrl - The presigned S3 PUT URL
|
|
51
|
+
* @param file - The file to upload
|
|
52
|
+
*/
|
|
53
|
+
declare const uploadFileToPresignedUrl: (uploadUrl: string, file: File) => Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Confirms that files have been uploaded to S3 and creates asset metadata records.
|
|
56
|
+
* @param params - orgId, optional brandId, and S3 asset keys from request-upload
|
|
57
|
+
* @returns The created asset records
|
|
58
|
+
*/
|
|
59
|
+
declare const confirmUpload: (params: ConfirmUploadParams) => Promise<UploadAssetResponse>;
|
|
60
|
+
/**
|
|
61
|
+
* Uploads assets via the presigned S3 URL flow:
|
|
62
|
+
* 1. Request presigned URLs from the service
|
|
63
|
+
* 2. PUT each file directly to its S3 presigned URL (in parallel)
|
|
64
|
+
* 3. Confirm uploads to persist asset metadata
|
|
65
|
+
*
|
|
66
|
+
* Supports files up to 20 MB
|
|
67
|
+
* @param params - Upload parameters containing orgId, brandId, and files
|
|
68
|
+
* @returns Promise resolving to the created asset records
|
|
69
|
+
*/
|
|
70
|
+
declare const uploadAssetViaPresignedUrl: (params: UploadAssetParams) => Promise<UploadAssetResponse>;
|
|
28
71
|
|
|
29
|
-
export { getAssets, uploadAsset };
|
|
72
|
+
export { confirmUpload, getAssets, requestUpload, uploadAsset, uploadAssetViaPresignedUrl, uploadFileToPresignedUrl };
|
|
30
73
|
export type { UploadAssetParams };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{getErrorMessage as t}from"../../../../utilities/apiUtils.js";import{sanitizeFile as
|
|
1
|
+
import{getErrorMessage as t}from"../../../../utilities/apiUtils.js";import{sanitizeFile as e}from"../../../../utilities/fileUtils.js";const o=()=>{const t=window.location?.host?.includes("portal.flipdish.com");return t?"https://api.portal.flipdish.com/assets":"https://api-prod-staging.portal.flipdishdev.com/assets"},r=async(t,e)=>{const r=((t,e)=>{if(!t)return;const r=e?`?brandId=${e}`:"";return`${o()}/orgs/${t}/assets${r}`})(t,e);if(!r)throw new Error("Organization ID is required to fetch assets");const s=await fetch(r,{method:"GET",credentials:"include"});if(!s.ok)throw new Error(`Failed to fetch assets: ${s.statusText}`);return await s.json()},s=async r=>{const{orgId:s,brandId:a,files:n}=r;if(!s)throw new Error("Organization ID is required to upload assets");if(!n||0===n.length)throw new Error("At least one file is required to upload");const i=n.map(e),d=`${o()}/orgs/${encodeURIComponent(s)}/assets/upload`,c=new FormData;for(const t of i)c.append("files",t);void 0!==a&&c.append("brandId",a);try{const t=await fetch(d,{method:"POST",credentials:"include",body:c});if(!t.ok){let e=t.statusText;try{const o=await t.json();e=o?.error?.message??o?.Message??e}catch{}throw new Error(e||"Failed to upload assets")}return await t.json()}catch(e){const o=t(e,"Failed to upload assets");throw new Error(o)}},a=async e=>{const{orgId:r,brandId:s,files:a}=e;if(!r)throw new Error("Organization ID is required to request upload");const n=`${o()}/orgs/${encodeURIComponent(r)}/assets/request-upload`,i={files:a.map(t=>({fileName:t.name,contentType:t.type}))};void 0!==s&&(i.brandId=s);try{const t=await fetch(n,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!t.ok){let e=t.statusText;try{const o=await t.json();e=o?.error?.message??o?.Message??e}catch{}throw new Error(e||"Failed to request upload")}return await t.json()}catch(e){const o=t(e,"Failed to request upload");throw new Error(o)}},n=async(t,e)=>{const o=await fetch(t,{method:"PUT",headers:{"Content-Type":e.type},body:e});if(!o.ok)throw new Error(`Failed to upload file to storage: ${o.statusText}`)},i=async e=>{const{orgId:r,brandId:s,assetKeys:a}=e;if(!r)throw new Error("Organization ID is required to confirm upload");const n=`${o()}/orgs/${encodeURIComponent(r)}/assets/confirm-upload`,i={assetKeys:a};void 0!==s&&(i.brandId=s);try{const t=await fetch(n,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!t.ok){let e=t.statusText;try{const o=await t.json();e=o?.error?.message??o?.Message??e}catch{}throw new Error(e||"Failed to confirm upload")}return await t.json()}catch(e){const o=t(e,"Failed to confirm upload");throw new Error(o)}},d=async o=>{const{orgId:r,brandId:s,files:d}=o;if(!r)throw new Error("Organization ID is required to upload assets");if(!d||0===d.length)throw new Error("At least one file is required to upload");const c=d.map(e);try{const{data:t}=await a({orgId:r,brandId:s,files:c}),e=c.map((e,o)=>{const r=t[o];if(!r)throw new Error(`No presigned URL returned for file: ${e.name}`);const s=r.assetKey.split("/").pop()??e.name;return s===e.name?e:new File([e],s,{type:e.type})});await Promise.all(e.map((e,o)=>n(t[o].uploadUrl,e)));const o=t.map(t=>t.assetKey);return await i({orgId:r,brandId:s,assetKeys:o})}catch(e){const o=t(e,"Failed to upload assets");throw new Error(o)}};export{i as confirmUpload,r as getAssets,a as requestUpload,s as uploadAsset,d as uploadAssetViaPresignedUrl,n as uploadFileToPresignedUrl};
|
|
2
2
|
//# sourceMappingURL=asset.service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset.service.js","sources":["../../../../../src/components/organisms/AssetManager/services/asset.service.ts"],"sourcesContent":["import { getErrorMessage } from '@fd/utilities/apiUtils';\n\nimport { sanitizeFile } from '../../../../utilities/fileUtils';\nimport type { ListAssetsResponse, UploadAssetResponse } from '../types/assets.type';\n\nconst PROD_API_URL = 'https://api.portal.flipdish.com/assets';\nconst DEV_STAGING_API_URL = 'https://api-prod-staging.portal.flipdishdev.com/assets';\n\n/**\n * Gets the asset management API base URL based on the current environment\n * @returns The appropriate API URL for the current environment\n */\nconst getAssetManagementApiUrl = (): string => {\n const isProd = window.location?.host?.includes('portal.flipdish.com');\n\n if (isProd) {\n return PROD_API_URL;\n }\n\n return DEV_STAGING_API_URL;\n};\n\nconst buildApiUrl = (orgId: string, brandId?: string): string | undefined => {\n if (!orgId) return undefined;\n const brandIdQueryParam = brandId ? `?brandId=${brandId}` : '';\n const baseUrl = getAssetManagementApiUrl();\n return `${baseUrl}/orgs/${orgId}/assets${brandIdQueryParam}`;\n};\n\n/**\n * Fetches assets from the asset management service API\n * @param orgId - organization ID.\n * @param brandId - brand ID.\n * @returns Promise resolving to an array of assets\n */\nexport const getAssets = async (orgId: string, brandId?: string): Promise<ListAssetsResponse> => {\n const apiUrl = buildApiUrl(orgId, brandId);\n\n if (!apiUrl) {\n throw new Error('Organization ID is required to fetch assets');\n }\n\n const response = await fetch(apiUrl, {\n method: 'GET',\n credentials: 'include',\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch assets: ${response.statusText}`);\n }\n\n return (await response.json()) as ListAssetsResponse;\n};\n\n/**\n * Upload parameters for asset upload\n */\nexport interface UploadAssetParams {\n /** Organization ID */\n orgId: string;\n /** Optional brand ID */\n brandId?: string;\n /** The files to upload */\n files: File[];\n}\n\n/**\n * Uploads multiple assets to the asset management service API\n * Sanitizes filenames by replacing spaces with dashes before uploading\n * @param params - Upload parameters containing orgId, brandId, and files\n * @returns Promise resolving to an array of uploaded assets\n */\nexport const uploadAsset = async (params: UploadAssetParams): Promise<UploadAssetResponse> => {\n const { orgId, brandId, files } = params;\n\n if (!orgId) {\n throw new Error('Organization ID is required to upload assets');\n }\n\n if (!files || files.length === 0) {\n throw new Error('At least one file is required to upload');\n }\n\n // Sanitize filenames by replacing spaces with dashes\n const sanitizedFiles = files.map(sanitizeFile);\n\n const baseUrl = getAssetManagementApiUrl();\n const uploadUrl = `${baseUrl}/orgs/${encodeURIComponent(orgId)}/assets/upload`;\n\n const formData = new FormData();\n for (const file of sanitizedFiles) {\n formData.append('files', file);\n }\n if (brandId !== undefined) {\n formData.append('brandId', brandId);\n }\n\n try {\n const response = await fetch(uploadUrl, {\n method: 'POST',\n credentials: 'include',\n body: formData,\n });\n\n if (!response.ok) {\n let message = response.statusText;\n try {\n const body = (await response.json()) as { error?: { message?: string }; Message?: string };\n message = body?.error?.message ?? body?.Message ?? message;\n } catch {\n // ignore JSON parse failure\n }\n throw new Error(message || 'Failed to upload assets');\n }\n\n return (await response.json()) as UploadAssetResponse;\n } catch (error: unknown) {\n const errorMessage = getErrorMessage(error, 'Failed to upload assets');\n throw new Error(errorMessage);\n }\n};\n"],"names":["getAssetManagementApiUrl","isProd","window","location","host","includes","getAssets","async","orgId","brandId","apiUrl","brandIdQueryParam","buildApiUrl","Error","response","fetch","method","credentials","ok","statusText","json","uploadAsset","params","files","length","sanitizedFiles","map","sanitizeFile","uploadUrl","encodeURIComponent","formData","FormData","file","append","undefined","body","message","error","Message","errorMessage","getErrorMessage"],"mappings":"sIAKA,MAOMA,EAA2B,KAC/B,MAAMC,EAASC,OAAOC,UAAUC,MAAMC,SAAS,uBAE/C,OAAIJ,EAVe,yCACO,0DA6BfK,EAAYC,MAAOC,EAAeC,KAC7C,MAAMC,EAdY,EAACF,EAAeC,KAClC,IAAKD,EAAO,OACZ,MAAMG,EAAoBF,EAAU,YAAYA,IAAY,GAE5D,MAAO,GADST,YACUQ,WAAeG,KAU1BC,CAAYJ,EAAOC,GAElC,IAAKC,EACH,MAAM,IAAIG,MAAM,+CAGlB,MAAMC,QAAiBC,MAAML,EAAQ,CACnCM,OAAQ,MACRC,YAAa,YAGf,IAAKH,EAASI,GACZ,MAAM,IAAIL,MAAM,2BAA2BC,EAASK,cAGtD,aAAcL,EAASM,QAqBZC,EAAcd,MAAOe,IAChC,MAAMd,MAAEA,EAAKC,QAAEA,EAAOc,MAAEA,GAAUD,EAElC,IAAKd,EACH,MAAM,IAAIK,MAAM,gDAGlB,IAAKU,GAA0B,IAAjBA,EAAMC,OAClB,MAAM,IAAIX,MAAM,2CAIlB,MAAMY,EAAiBF,EAAMG,IAAIC,GAG3BC,EAAY,GADF5B,YACqB6B,mBAAmBrB,mBAElDsB,EAAW,IAAIC,SACrB,IAAK,MAAMC,KAAQP,EACjBK,EAASG,OAAO,QAASD,QAEXE,IAAZzB,GACFqB,EAASG,OAAO,UAAWxB,GAG7B,IACE,MAAMK,QAAiBC,MAAMa,EAAW,CACtCZ,OAAQ,OACRC,YAAa,UACbkB,KAAML,IAGR,IAAKhB,EAASI,GAAI,CAChB,IAAIkB,EAAUtB,EAASK,WACvB,IACE,MAAMgB,QAAcrB,EAASM,OAC7BgB,EAAUD,GAAME,OAAOD,SAAWD,GAAMG,SAAWF,CACrD,CAAE,MAEF,CACA,MAAM,IAAIvB,MAAMuB,GAAW,0BAC7B,CAEA,aAActB,EAASM,MACzB,CAAE,MAAOiB,GACP,MAAME,EAAeC,EAAgBH,EAAO,2BAC5C,MAAM,IAAIxB,MAAM0B,EAClB"}
|
|
1
|
+
{"version":3,"file":"asset.service.js","sources":["../../../../../src/components/organisms/AssetManager/services/asset.service.ts"],"sourcesContent":["import { getErrorMessage } from '@fd/utilities/apiUtils';\n\nimport { sanitizeFile } from '../../../../utilities/fileUtils';\nimport type { ListAssetsResponse, UploadAssetResponse } from '../types/assets.type';\n\ninterface PresignedUploadItem {\n uploadUrl: string;\n assetKey: string;\n expiresIn: number;\n}\n\ninterface RequestUploadResponse {\n data: PresignedUploadItem[];\n}\n\ninterface ConfirmUploadParams {\n orgId: string;\n brandId?: string;\n assetKeys: string[];\n}\n\nconst PROD_API_URL = 'https://api.portal.flipdish.com/assets';\nconst DEV_STAGING_API_URL = 'https://api-prod-staging.portal.flipdishdev.com/assets';\n\n/**\n * Gets the asset management API base URL based on the current environment\n * @returns The appropriate API URL for the current environment\n */\nconst getAssetManagementApiUrl = (): string => {\n const isProd = window.location?.host?.includes('portal.flipdish.com');\n\n if (isProd) {\n return PROD_API_URL;\n }\n\n return DEV_STAGING_API_URL;\n};\n\nconst buildApiUrl = (orgId: string, brandId?: string): string | undefined => {\n if (!orgId) return undefined;\n const brandIdQueryParam = brandId ? `?brandId=${brandId}` : '';\n const baseUrl = getAssetManagementApiUrl();\n return `${baseUrl}/orgs/${orgId}/assets${brandIdQueryParam}`;\n};\n\n/**\n * Fetches assets from the asset management service API\n * @param orgId - organization ID.\n * @param brandId - brand ID.\n * @returns Promise resolving to an array of assets\n */\nexport const getAssets = async (orgId: string, brandId?: string): Promise<ListAssetsResponse> => {\n const apiUrl = buildApiUrl(orgId, brandId);\n\n if (!apiUrl) {\n throw new Error('Organization ID is required to fetch assets');\n }\n\n const response = await fetch(apiUrl, {\n method: 'GET',\n credentials: 'include',\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch assets: ${response.statusText}`);\n }\n\n return (await response.json()) as ListAssetsResponse;\n};\n\n/**\n * Upload parameters for asset upload\n */\nexport interface UploadAssetParams {\n /** Organization ID */\n orgId: string;\n /** Optional brand ID */\n brandId?: string;\n /** The files to upload */\n files: File[];\n}\n\n/**\n * Uploads multiple assets to the asset management service API\n * Sanitizes filenames by replacing spaces with dashes before uploading\n * @param params - Upload parameters containing orgId, brandId, and files\n * @returns Promise resolving to an array of uploaded assets\n */\nexport const uploadAsset = async (params: UploadAssetParams): Promise<UploadAssetResponse> => {\n const { orgId, brandId, files } = params;\n\n if (!orgId) {\n throw new Error('Organization ID is required to upload assets');\n }\n\n if (!files || files.length === 0) {\n throw new Error('At least one file is required to upload');\n }\n\n // Sanitize filenames by replacing spaces with dashes\n const sanitizedFiles = files.map(sanitizeFile);\n\n const baseUrl = getAssetManagementApiUrl();\n const uploadUrl = `${baseUrl}/orgs/${encodeURIComponent(orgId)}/assets/upload`;\n\n const formData = new FormData();\n for (const file of sanitizedFiles) {\n formData.append('files', file);\n }\n if (brandId !== undefined) {\n formData.append('brandId', brandId);\n }\n\n try {\n const response = await fetch(uploadUrl, {\n method: 'POST',\n credentials: 'include',\n body: formData,\n });\n\n if (!response.ok) {\n let message = response.statusText;\n try {\n const body = (await response.json()) as { error?: { message?: string }; Message?: string };\n message = body?.error?.message ?? body?.Message ?? message;\n } catch {\n // ignore JSON parse failure\n }\n throw new Error(message || 'Failed to upload assets');\n }\n\n return (await response.json()) as UploadAssetResponse;\n } catch (error: unknown) {\n const errorMessage = getErrorMessage(error, 'Failed to upload assets');\n throw new Error(errorMessage);\n }\n};\n\n/**\n * Requests presigned S3 PUT URLs for direct client-to-S3 uploads.\n * @param params - orgId, optional brandId, and files to upload\n * @returns Presigned upload items containing uploadUrl and assetKey per file\n */\nexport const requestUpload = async (params: UploadAssetParams): Promise<RequestUploadResponse> => {\n const { orgId, brandId, files } = params;\n\n if (!orgId) {\n throw new Error('Organization ID is required to request upload');\n }\n\n const baseUrl = getAssetManagementApiUrl();\n const url = `${baseUrl}/orgs/${encodeURIComponent(orgId)}/assets/request-upload`;\n\n const body: { files: Array<{ fileName: string; contentType: string }>; brandId?: string } = {\n files: files.map((file) => ({ fileName: file.name, contentType: file.type })),\n };\n if (brandId !== undefined) {\n body.brandId = brandId;\n }\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n let message = response.statusText;\n try {\n const responseBody = (await response.json()) as { error?: { message?: string }; Message?: string };\n message = responseBody?.error?.message ?? responseBody?.Message ?? message;\n } catch {\n // ignore JSON parse failure\n }\n throw new Error(message || 'Failed to request upload');\n }\n\n return (await response.json()) as RequestUploadResponse;\n } catch (error: unknown) {\n const errorMessage = getErrorMessage(error, 'Failed to request upload');\n throw new Error(errorMessage);\n }\n};\n\n/**\n * Uploads a single file directly to S3 using a presigned PUT URL.\n * Does not use credentials — the presigned URL is self-authenticating.\n * @param uploadUrl - The presigned S3 PUT URL\n * @param file - The file to upload\n */\nexport const uploadFileToPresignedUrl = async (uploadUrl: string, file: File): Promise<void> => {\n const response = await fetch(uploadUrl, {\n method: 'PUT',\n headers: { 'Content-Type': file.type },\n body: file,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to upload file to storage: ${response.statusText}`);\n }\n};\n\n/**\n * Confirms that files have been uploaded to S3 and creates asset metadata records.\n * @param params - orgId, optional brandId, and S3 asset keys from request-upload\n * @returns The created asset records\n */\nexport const confirmUpload = async (params: ConfirmUploadParams): Promise<UploadAssetResponse> => {\n const { orgId, brandId, assetKeys } = params;\n\n if (!orgId) {\n throw new Error('Organization ID is required to confirm upload');\n }\n\n const baseUrl = getAssetManagementApiUrl();\n const url = `${baseUrl}/orgs/${encodeURIComponent(orgId)}/assets/confirm-upload`;\n\n const body: { assetKeys: string[]; brandId?: string } = { assetKeys };\n if (brandId !== undefined) {\n body.brandId = brandId;\n }\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n let message = response.statusText;\n try {\n const responseBody = (await response.json()) as { error?: { message?: string }; Message?: string };\n message = responseBody?.error?.message ?? responseBody?.Message ?? message;\n } catch {\n // ignore JSON parse failure\n }\n throw new Error(message || 'Failed to confirm upload');\n }\n\n return (await response.json()) as UploadAssetResponse;\n } catch (error: unknown) {\n const errorMessage = getErrorMessage(error, 'Failed to confirm upload');\n throw new Error(errorMessage);\n }\n};\n\n/**\n * Uploads assets via the presigned S3 URL flow:\n * 1. Request presigned URLs from the service\n * 2. PUT each file directly to its S3 presigned URL (in parallel)\n * 3. Confirm uploads to persist asset metadata\n *\n * Supports files up to 20 MB\n * @param params - Upload parameters containing orgId, brandId, and files\n * @returns Promise resolving to the created asset records\n */\nexport const uploadAssetViaPresignedUrl = async (params: UploadAssetParams): Promise<UploadAssetResponse> => {\n const { orgId, brandId, files } = params;\n\n if (!orgId) {\n throw new Error('Organization ID is required to upload assets');\n }\n\n if (!files || files.length === 0) {\n throw new Error('At least one file is required to upload');\n }\n\n // Sanitize filenames (spaces → dashes) before requesting presigned URLs\n const sanitizedFiles = files.map(sanitizeFile);\n\n try {\n // Step 1: Get presigned S3 PUT URLs\n const { data: presignedItems } = await requestUpload({ orgId, brandId, files: sanitizedFiles });\n\n // Step 2: Align file names with the asset keys returned by the server.\n // The server may have deduplicated filenames (e.g. my-file.jpg → my-file-1.jpg),\n // so rename any file whose name doesn't match the end of its asset key.\n const alignedFiles = sanitizedFiles.map((file, index) => {\n const item = presignedItems[index];\n if (!item) {\n throw new Error(`No presigned URL returned for file: ${file.name}`);\n }\n const keyFileName = item.assetKey.split('/').pop() ?? file.name;\n if (keyFileName === file.name) {\n return file;\n }\n return new File([file], keyFileName, { type: file.type });\n });\n\n // Step 3: Upload all files in parallel to their respective presigned URLs\n await Promise.all(\n alignedFiles.map((file, index) => {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return uploadFileToPresignedUrl(presignedItems[index]!.uploadUrl, file);\n }),\n );\n\n // Step 4: Confirm uploads and get asset metadata\n const assetKeys = presignedItems.map((item) => item.assetKey);\n return await confirmUpload({ orgId, brandId, assetKeys });\n } catch (error: unknown) {\n const errorMessage = getErrorMessage(error, 'Failed to upload assets');\n throw new Error(errorMessage);\n }\n};\n"],"names":["getAssetManagementApiUrl","isProd","window","location","host","includes","getAssets","async","orgId","brandId","apiUrl","brandIdQueryParam","buildApiUrl","Error","response","fetch","method","credentials","ok","statusText","json","uploadAsset","params","files","length","sanitizedFiles","map","sanitizeFile","uploadUrl","encodeURIComponent","formData","FormData","file","append","undefined","body","message","error","Message","errorMessage","getErrorMessage","requestUpload","url","fileName","name","contentType","type","headers","JSON","stringify","responseBody","uploadFileToPresignedUrl","confirmUpload","assetKeys","uploadAssetViaPresignedUrl","data","presignedItems","alignedFiles","index","item","keyFileName","assetKey","split","pop","File","Promise","all"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";require("../../node_modules/.pnpm/msw@2.14.6_@types_node@20.19.41_typescript@5.4.5/node_modules/msw/lib/core/index.cjs.js");var e=require("../../components/organisms/AssetManager/__mocks__/mockAssets.cjs.js"),s=require("../../node_modules/.pnpm/msw@2.14.6_@types_node@20.19.41_typescript@5.4.5/node_modules/msw/lib/core/http.cjs.js"),t=require("../../node_modules/.pnpm/msw@2.14.6_@types_node@20.19.41_typescript@5.4.5/node_modules/msw/lib/core/HttpResponse.cjs.js");const o=[...[s.http.get("*/assets/orgs/:orgId/assets",({request:s,params:o})=>(console.log("[MSW] Intercepted GET assets request:",s.url,"orgId:",o.orgId),t.HttpResponse.json(e.mockAssets))),s.http.post("*/assets/orgs/:orgId/assets/upload",async({request:s,params:o})=>{console.log("[MSW] Intercepted POST upload request:",s.url,"orgId:",o.orgId),await new Promise(e=>setTimeout(e,2e3));const r=(await s.formData()).getAll("files").map(s=>e.createMockAsset({id:"asset_e3454351fw5",fileName:s.name,contentType:s.type||"image/jpeg",createdBy:"Test User",updatedBy:"Test User"}));return t.HttpResponse.json(r)})]];exports.handlers=
|
|
1
|
+
"use strict";require("../../node_modules/.pnpm/msw@2.14.6_@types_node@20.19.41_typescript@5.4.5/node_modules/msw/lib/core/index.cjs.js");var e=require("../../components/organisms/AssetManager/__mocks__/mockAssets.cjs.js"),s=require("../../node_modules/.pnpm/msw@2.14.6_@types_node@20.19.41_typescript@5.4.5/node_modules/msw/lib/core/http.cjs.js"),t=require("../../node_modules/.pnpm/msw@2.14.6_@types_node@20.19.41_typescript@5.4.5/node_modules/msw/lib/core/HttpResponse.cjs.js");const o="https://s3.mock.amazonaws.com/mock-flipdish-assets",r=[...[s.http.get("*/assets/orgs/:orgId/assets",({request:s,params:o})=>(console.log("[MSW] Intercepted GET assets request:",s.url,"orgId:",o.orgId),t.HttpResponse.json(e.mockAssets))),s.http.post("*/assets/orgs/:orgId/assets/upload",async({request:s,params:o})=>{console.log("[MSW] Intercepted POST upload request:",s.url,"orgId:",o.orgId),await new Promise(e=>setTimeout(e,2e3));const r=(await s.formData()).getAll("files").map(s=>e.createMockAsset({id:"asset_e3454351fw5",fileName:s.name,contentType:s.type||"image/jpeg",createdBy:"Test User",updatedBy:"Test User"}));return t.HttpResponse.json(r)}),s.http.post("*/assets/orgs/:orgId/assets/request-upload",async({request:e,params:s})=>{console.log("[MSW] Intercepted POST request-upload:",e.url,"orgId:",s.orgId);const r=await e.json(),a=String(s.orgId),n=r.files.map(e=>({uploadUrl:`${o}/${a}/${e.fileName}`,assetKey:`assets/${a}/${e.fileName}`,expiresIn:300}));return t.HttpResponse.json({data:n})}),s.http.put(`${o}/*`,async()=>(await new Promise(e=>setTimeout(e,2e3)),new t.HttpResponse(null,{status:200}))),s.http.post("*/assets/orgs/:orgId/assets/confirm-upload",async({request:s,params:o})=>{console.log("[MSW] Intercepted POST confirm-upload:",s.url,"orgId:",o.orgId);const r=(await s.json()).assetKeys.map(s=>{const t=s.split("/").pop()??"unknown";return e.createMockAsset({id:`asset_${Date.now()}_${t}`,fileName:t,contentType:"image/jpeg",createdBy:"Current User",updatedBy:"Current User"})});return t.HttpResponse.json({data:r})})]];exports.MOCK_S3_BASE_URL=o,exports.handlers=r;
|
|
2
2
|
//# sourceMappingURL=handlers.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.cjs.js","sources":["../../../src/mocks/msw/handlers.ts"],"sourcesContent":["import { http, HttpResponse } from 'msw';\n\nimport { createMockAsset, mockAssets } from '../../components/organisms/AssetManager/__mocks__/mockAssets';\n\n/** Mock delay (ms) for upload so loading indicator is visible in Storybook */\nconst UPLOAD_MOCK_DELAY_MS = 2000;\n\nconst assetManagementHandlers = [\n // GET assets handler\n http.get('*/assets/orgs/:orgId/assets', ({ request, params }) => {\n // eslint-disable-next-line no-console\n console.log('[MSW] Intercepted GET assets request:', request.url, 'orgId:', params['orgId']);\n return HttpResponse.json(mockAssets);\n }),\n\n // POST upload handler\n http.post('*/assets/orgs/:orgId/assets/upload', async ({ request, params }) => {\n // eslint-disable-next-line no-console\n console.log('[MSW] Intercepted POST upload request:', request.url, 'orgId:', params['orgId']);\n await new Promise((resolve) => setTimeout(resolve, UPLOAD_MOCK_DELAY_MS));\n const formData = await request.formData();\n const files = formData.getAll('files') as File[];\n\n const uploadedAssets = files.map((file) =>\n createMockAsset({\n id: `asset_e3454351fw5`,\n fileName: file.name,\n contentType: file.type || 'image/jpeg',\n createdBy: 'Test User',\n updatedBy: 'Test User',\n }),\n );\n\n return HttpResponse.json(uploadedAssets);\n }),\n];\n\nexport const handlers = [...assetManagementHandlers];\n"],"names":["handlers","http","get","request","params","console","log","url","HttpResponse","json","mockAssets","post","async","Promise","resolve","setTimeout","uploadedAssets","formData","getAll","map","file","createMockAsset","id","fileName","name","contentType","type","createdBy","updatedBy"],"mappings":"geAKA,
|
|
1
|
+
{"version":3,"file":"handlers.cjs.js","sources":["../../../src/mocks/msw/handlers.ts"],"sourcesContent":["import { http, HttpResponse } from 'msw';\n\nimport { createMockAsset, mockAssets } from '../../components/organisms/AssetManager/__mocks__/mockAssets';\n\n/** Mock delay (ms) for upload so loading indicator is visible in Storybook */\nconst UPLOAD_MOCK_DELAY_MS = 2000;\n\n/**\n * Base URL used in mock presigned S3 URLs so MSW can intercept the PUT requests.\n * The request-upload handler returns URLs under this base.\n */\nexport const MOCK_S3_BASE_URL = 'https://s3.mock.amazonaws.com/mock-flipdish-assets';\n\nconst assetManagementHandlers = [\n // GET assets handler\n http.get('*/assets/orgs/:orgId/assets', ({ request, params }) => {\n // eslint-disable-next-line no-console\n console.log('[MSW] Intercepted GET assets request:', request.url, 'orgId:', params['orgId']);\n return HttpResponse.json(mockAssets);\n }),\n\n // POST upload handler (legacy multipart form flow)\n http.post('*/assets/orgs/:orgId/assets/upload', async ({ request, params }) => {\n // eslint-disable-next-line no-console\n console.log('[MSW] Intercepted POST upload request:', request.url, 'orgId:', params['orgId']);\n await new Promise((resolve) => setTimeout(resolve, UPLOAD_MOCK_DELAY_MS));\n const formData = await request.formData();\n const files = formData.getAll('files') as File[];\n\n const uploadedAssets = files.map((file) =>\n createMockAsset({\n id: `asset_e3454351fw5`,\n fileName: file.name,\n contentType: file.type || 'image/jpeg',\n createdBy: 'Test User',\n updatedBy: 'Test User',\n }),\n );\n\n return HttpResponse.json(uploadedAssets);\n }),\n\n // POST request-upload handler (presigned URL flow — step 1)\n http.post('*/assets/orgs/:orgId/assets/request-upload', async ({ request, params }) => {\n // eslint-disable-next-line no-console\n console.log('[MSW] Intercepted POST request-upload:', request.url, 'orgId:', params['orgId']);\n const body = (await request.json()) as {\n files: Array<{ fileName: string; contentType: string }>;\n brandId?: string;\n };\n const orgId = String(params['orgId']);\n\n const data = body.files.map((file) => ({\n uploadUrl: `${MOCK_S3_BASE_URL}/${orgId}/${file.fileName}`,\n assetKey: `assets/${orgId}/${file.fileName}`,\n expiresIn: 300,\n }));\n\n return HttpResponse.json({ data });\n }),\n\n // PUT presigned S3 URL handler (presigned URL flow — step 2)\n http.put(`${MOCK_S3_BASE_URL}/*`, async () => {\n await new Promise((resolve) => setTimeout(resolve, UPLOAD_MOCK_DELAY_MS));\n return new HttpResponse(null, { status: 200 });\n }),\n\n // POST confirm-upload handler (presigned URL flow — step 3)\n http.post('*/assets/orgs/:orgId/assets/confirm-upload', async ({ request, params }) => {\n // eslint-disable-next-line no-console\n console.log('[MSW] Intercepted POST confirm-upload:', request.url, 'orgId:', params['orgId']);\n const body = (await request.json()) as { assetKeys: string[]; brandId?: string };\n\n const uploadedAssets = body.assetKeys.map((key) => {\n const fileName = key.split('/').pop() ?? 'unknown';\n return createMockAsset({\n id: `asset_${Date.now()}_${fileName}`,\n fileName,\n contentType: 'image/jpeg',\n createdBy: 'Current User',\n updatedBy: 'Current User',\n });\n });\n\n return HttpResponse.json({ data: uploadedAssets });\n }),\n];\n\nexport const handlers = [...assetManagementHandlers];\n"],"names":["MOCK_S3_BASE_URL","handlers","http","get","request","params","console","log","url","HttpResponse","json","mockAssets","post","async","Promise","resolve","setTimeout","uploadedAssets","formData","getAll","map","file","createMockAsset","id","fileName","name","contentType","type","createdBy","updatedBy","body","orgId","String","data","files","uploadUrl","assetKey","expiresIn","put","status","assetKeys","key","split","pop","Date","now"],"mappings":"geAKA,MAMaA,EAAmB,qDA6EnBC,EAAW,IA3EQ,CAE9BC,EAAAA,KAAKC,IAAI,8BAA+B,EAAGC,UAASC,aAElDC,QAAQC,IAAI,wCAAyCH,EAAQI,IAAK,SAAUH,EAAc,OACnFI,EAAAA,aAAaC,KAAKC,gBAI3BT,EAAAA,KAAKU,KAAK,qCAAsCC,OAAST,UAASC,aAEhEC,QAAQC,IAAI,yCAA0CH,EAAQI,IAAK,SAAUH,EAAc,aACrF,IAAIS,QAASC,GAAYC,WAAWD,EApBjB,MAqBzB,MAGME,SAHiBb,EAAQc,YACRC,OAAO,SAEDC,IAAKC,GAChCC,EAAAA,gBAAgB,CACdC,GAAI,oBACJC,SAAUH,EAAKI,KACfC,YAAaL,EAAKM,MAAQ,aAC1BC,UAAW,YACXC,UAAW,eAIf,OAAOpB,EAAAA,aAAaC,KAAKO,KAI3Bf,EAAAA,KAAKU,KAAK,6CAA8CC,OAAST,UAASC,aAExEC,QAAQC,IAAI,yCAA0CH,EAAQI,IAAK,SAAUH,EAAc,OAC3F,MAAMyB,QAAc1B,EAAQM,OAItBqB,EAAQC,OAAO3B,EAAc,OAE7B4B,EAAOH,EAAKI,MAAMd,IAAKC,IAAI,CAC/Bc,UAAW,GAAGnC,KAAoB+B,KAASV,EAAKG,WAChDY,SAAU,UAAUL,KAASV,EAAKG,WAClCa,UAAW,OAGb,OAAO5B,eAAaC,KAAK,CAAEuB,WAI7B/B,EAAAA,KAAKoC,IAAI,GAAGtC,MAAsBa,gBAC1B,IAAIC,QAASC,GAAYC,WAAWD,EA1DjB,MA2DlB,IAAIN,EAAAA,aAAa,KAAM,CAAE8B,OAAQ,QAI1CrC,EAAAA,KAAKU,KAAK,6CAA8CC,OAAST,UAASC,aAExEC,QAAQC,IAAI,yCAA0CH,EAAQI,IAAK,SAAUH,EAAc,OAC3F,MAEMY,SAFcb,EAAQM,QAEA8B,UAAUpB,IAAKqB,IACzC,MAAMjB,EAAWiB,EAAIC,MAAM,KAAKC,OAAS,UACzC,OAAOrB,kBAAgB,CACrBC,GAAI,SAASqB,KAAKC,SAASrB,IAC3BA,WACAE,YAAa,aACbE,UAAW,eACXC,UAAW,mBAIf,OAAOpB,EAAAA,aAAaC,KAAK,CAAEuB,KAAMhB"}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { HttpHandler } from '../../node_modules/.pnpm/msw@2.14.6_@types_node@20.19.41_typescript@5.4.5/node_modules/msw/lib/core/handlers/HttpHandler.d.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Base URL used in mock presigned S3 URLs so MSW can intercept the PUT requests.
|
|
5
|
+
* The request-upload handler returns URLs under this base.
|
|
6
|
+
*/
|
|
7
|
+
declare const MOCK_S3_BASE_URL = "https://s3.mock.amazonaws.com/mock-flipdish-assets";
|
|
3
8
|
declare const handlers: HttpHandler[];
|
|
4
9
|
|
|
5
|
-
export { handlers };
|
|
10
|
+
export { MOCK_S3_BASE_URL, handlers };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import"../../node_modules/.pnpm/msw@2.14.6_@types_node@20.19.41_typescript@5.4.5/node_modules/msw/lib/core/index.js";import{mockAssets as e,createMockAsset as s}from"../../components/organisms/AssetManager/__mocks__/mockAssets.js";import{http as
|
|
1
|
+
import"../../node_modules/.pnpm/msw@2.14.6_@types_node@20.19.41_typescript@5.4.5/node_modules/msw/lib/core/index.js";import{mockAssets as e,createMockAsset as s}from"../../components/organisms/AssetManager/__mocks__/mockAssets.js";import{http as t}from"../../node_modules/.pnpm/msw@2.14.6_@types_node@20.19.41_typescript@5.4.5/node_modules/msw/lib/core/http.js";import{HttpResponse as o}from"../../node_modules/.pnpm/msw@2.14.6_@types_node@20.19.41_typescript@5.4.5/node_modules/msw/lib/core/HttpResponse.js";const r="https://s3.mock.amazonaws.com/mock-flipdish-assets",a=[...[t.get("*/assets/orgs/:orgId/assets",({request:s,params:t})=>(console.log("[MSW] Intercepted GET assets request:",s.url,"orgId:",t.orgId),o.json(e))),t.post("*/assets/orgs/:orgId/assets/upload",async({request:e,params:t})=>{console.log("[MSW] Intercepted POST upload request:",e.url,"orgId:",t.orgId),await new Promise(e=>setTimeout(e,2e3));const r=(await e.formData()).getAll("files").map(e=>s({id:"asset_e3454351fw5",fileName:e.name,contentType:e.type||"image/jpeg",createdBy:"Test User",updatedBy:"Test User"}));return o.json(r)}),t.post("*/assets/orgs/:orgId/assets/request-upload",async({request:e,params:s})=>{console.log("[MSW] Intercepted POST request-upload:",e.url,"orgId:",s.orgId);const t=await e.json(),a=String(s.orgId),n=t.files.map(e=>({uploadUrl:`${r}/${a}/${e.fileName}`,assetKey:`assets/${a}/${e.fileName}`,expiresIn:300}));return o.json({data:n})}),t.put(`${r}/*`,async()=>(await new Promise(e=>setTimeout(e,2e3)),new o(null,{status:200}))),t.post("*/assets/orgs/:orgId/assets/confirm-upload",async({request:e,params:t})=>{console.log("[MSW] Intercepted POST confirm-upload:",e.url,"orgId:",t.orgId);const r=(await e.json()).assetKeys.map(e=>{const t=e.split("/").pop()??"unknown";return s({id:`asset_${Date.now()}_${t}`,fileName:t,contentType:"image/jpeg",createdBy:"Current User",updatedBy:"Current User"})});return o.json({data:r})})]];export{r as MOCK_S3_BASE_URL,a as handlers};
|
|
2
2
|
//# sourceMappingURL=handlers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.js","sources":["../../../src/mocks/msw/handlers.ts"],"sourcesContent":["import { http, HttpResponse } from 'msw';\n\nimport { createMockAsset, mockAssets } from '../../components/organisms/AssetManager/__mocks__/mockAssets';\n\n/** Mock delay (ms) for upload so loading indicator is visible in Storybook */\nconst UPLOAD_MOCK_DELAY_MS = 2000;\n\nconst assetManagementHandlers = [\n // GET assets handler\n http.get('*/assets/orgs/:orgId/assets', ({ request, params }) => {\n // eslint-disable-next-line no-console\n console.log('[MSW] Intercepted GET assets request:', request.url, 'orgId:', params['orgId']);\n return HttpResponse.json(mockAssets);\n }),\n\n // POST upload handler\n http.post('*/assets/orgs/:orgId/assets/upload', async ({ request, params }) => {\n // eslint-disable-next-line no-console\n console.log('[MSW] Intercepted POST upload request:', request.url, 'orgId:', params['orgId']);\n await new Promise((resolve) => setTimeout(resolve, UPLOAD_MOCK_DELAY_MS));\n const formData = await request.formData();\n const files = formData.getAll('files') as File[];\n\n const uploadedAssets = files.map((file) =>\n createMockAsset({\n id: `asset_e3454351fw5`,\n fileName: file.name,\n contentType: file.type || 'image/jpeg',\n createdBy: 'Test User',\n updatedBy: 'Test User',\n }),\n );\n\n return HttpResponse.json(uploadedAssets);\n }),\n];\n\nexport const handlers = [...assetManagementHandlers];\n"],"names":["handlers","http","get","request","params","console","log","url","HttpResponse","json","mockAssets","post","async","Promise","resolve","setTimeout","uploadedAssets","formData","getAll","map","file","createMockAsset","id","fileName","name","contentType","type","createdBy","updatedBy"],"mappings":"6fAKA,
|
|
1
|
+
{"version":3,"file":"handlers.js","sources":["../../../src/mocks/msw/handlers.ts"],"sourcesContent":["import { http, HttpResponse } from 'msw';\n\nimport { createMockAsset, mockAssets } from '../../components/organisms/AssetManager/__mocks__/mockAssets';\n\n/** Mock delay (ms) for upload so loading indicator is visible in Storybook */\nconst UPLOAD_MOCK_DELAY_MS = 2000;\n\n/**\n * Base URL used in mock presigned S3 URLs so MSW can intercept the PUT requests.\n * The request-upload handler returns URLs under this base.\n */\nexport const MOCK_S3_BASE_URL = 'https://s3.mock.amazonaws.com/mock-flipdish-assets';\n\nconst assetManagementHandlers = [\n // GET assets handler\n http.get('*/assets/orgs/:orgId/assets', ({ request, params }) => {\n // eslint-disable-next-line no-console\n console.log('[MSW] Intercepted GET assets request:', request.url, 'orgId:', params['orgId']);\n return HttpResponse.json(mockAssets);\n }),\n\n // POST upload handler (legacy multipart form flow)\n http.post('*/assets/orgs/:orgId/assets/upload', async ({ request, params }) => {\n // eslint-disable-next-line no-console\n console.log('[MSW] Intercepted POST upload request:', request.url, 'orgId:', params['orgId']);\n await new Promise((resolve) => setTimeout(resolve, UPLOAD_MOCK_DELAY_MS));\n const formData = await request.formData();\n const files = formData.getAll('files') as File[];\n\n const uploadedAssets = files.map((file) =>\n createMockAsset({\n id: `asset_e3454351fw5`,\n fileName: file.name,\n contentType: file.type || 'image/jpeg',\n createdBy: 'Test User',\n updatedBy: 'Test User',\n }),\n );\n\n return HttpResponse.json(uploadedAssets);\n }),\n\n // POST request-upload handler (presigned URL flow — step 1)\n http.post('*/assets/orgs/:orgId/assets/request-upload', async ({ request, params }) => {\n // eslint-disable-next-line no-console\n console.log('[MSW] Intercepted POST request-upload:', request.url, 'orgId:', params['orgId']);\n const body = (await request.json()) as {\n files: Array<{ fileName: string; contentType: string }>;\n brandId?: string;\n };\n const orgId = String(params['orgId']);\n\n const data = body.files.map((file) => ({\n uploadUrl: `${MOCK_S3_BASE_URL}/${orgId}/${file.fileName}`,\n assetKey: `assets/${orgId}/${file.fileName}`,\n expiresIn: 300,\n }));\n\n return HttpResponse.json({ data });\n }),\n\n // PUT presigned S3 URL handler (presigned URL flow — step 2)\n http.put(`${MOCK_S3_BASE_URL}/*`, async () => {\n await new Promise((resolve) => setTimeout(resolve, UPLOAD_MOCK_DELAY_MS));\n return new HttpResponse(null, { status: 200 });\n }),\n\n // POST confirm-upload handler (presigned URL flow — step 3)\n http.post('*/assets/orgs/:orgId/assets/confirm-upload', async ({ request, params }) => {\n // eslint-disable-next-line no-console\n console.log('[MSW] Intercepted POST confirm-upload:', request.url, 'orgId:', params['orgId']);\n const body = (await request.json()) as { assetKeys: string[]; brandId?: string };\n\n const uploadedAssets = body.assetKeys.map((key) => {\n const fileName = key.split('/').pop() ?? 'unknown';\n return createMockAsset({\n id: `asset_${Date.now()}_${fileName}`,\n fileName,\n contentType: 'image/jpeg',\n createdBy: 'Current User',\n updatedBy: 'Current User',\n });\n });\n\n return HttpResponse.json({ data: uploadedAssets });\n }),\n];\n\nexport const handlers = [...assetManagementHandlers];\n"],"names":["MOCK_S3_BASE_URL","handlers","http","get","request","params","console","log","url","HttpResponse","json","mockAssets","post","async","Promise","resolve","setTimeout","uploadedAssets","formData","getAll","map","file","createMockAsset","id","fileName","name","contentType","type","createdBy","updatedBy","body","orgId","String","data","files","uploadUrl","assetKey","expiresIn","put","status","assetKeys","key","split","pop","Date","now"],"mappings":"6fAKA,MAMaA,EAAmB,qDA6EnBC,EAAW,IA3EQ,CAE9BC,EAAKC,IAAI,8BAA+B,EAAGC,UAASC,aAElDC,QAAQC,IAAI,wCAAyCH,EAAQI,IAAK,SAAUH,EAAc,OACnFI,EAAaC,KAAKC,KAI3BT,EAAKU,KAAK,qCAAsCC,OAAST,UAASC,aAEhEC,QAAQC,IAAI,yCAA0CH,EAAQI,IAAK,SAAUH,EAAc,aACrF,IAAIS,QAASC,GAAYC,WAAWD,EApBjB,MAqBzB,MAGME,SAHiBb,EAAQc,YACRC,OAAO,SAEDC,IAAKC,GAChCC,EAAgB,CACdC,GAAI,oBACJC,SAAUH,EAAKI,KACfC,YAAaL,EAAKM,MAAQ,aAC1BC,UAAW,YACXC,UAAW,eAIf,OAAOpB,EAAaC,KAAKO,KAI3Bf,EAAKU,KAAK,6CAA8CC,OAAST,UAASC,aAExEC,QAAQC,IAAI,yCAA0CH,EAAQI,IAAK,SAAUH,EAAc,OAC3F,MAAMyB,QAAc1B,EAAQM,OAItBqB,EAAQC,OAAO3B,EAAc,OAE7B4B,EAAOH,EAAKI,MAAMd,IAAKC,IAAI,CAC/Bc,UAAW,GAAGnC,KAAoB+B,KAASV,EAAKG,WAChDY,SAAU,UAAUL,KAASV,EAAKG,WAClCa,UAAW,OAGb,OAAO5B,EAAaC,KAAK,CAAEuB,WAI7B/B,EAAKoC,IAAI,GAAGtC,MAAsBa,gBAC1B,IAAIC,QAASC,GAAYC,WAAWD,EA1DjB,MA2DlB,IAAIN,EAAa,KAAM,CAAE8B,OAAQ,QAI1CrC,EAAKU,KAAK,6CAA8CC,OAAST,UAASC,aAExEC,QAAQC,IAAI,yCAA0CH,EAAQI,IAAK,SAAUH,EAAc,OAC3F,MAEMY,SAFcb,EAAQM,QAEA8B,UAAUpB,IAAKqB,IACzC,MAAMjB,EAAWiB,EAAIC,MAAM,KAAKC,OAAS,UACzC,OAAOrB,EAAgB,CACrBC,GAAI,SAASqB,KAAKC,SAASrB,IAC3BA,WACAE,YAAa,aACbE,UAAW,eACXC,UAAW,mBAIf,OAAOpB,EAAaC,KAAK,CAAEuB,KAAMhB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flipdish/portal-library",
|
|
3
|
-
"version": "8.6.
|
|
3
|
+
"version": "8.6.6",
|
|
4
4
|
"files": [
|
|
5
5
|
"dist"
|
|
6
6
|
],
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@cloudinary/url-gen": "^1.21.0",
|
|
43
43
|
"@emotion/react": "11.14.0",
|
|
44
44
|
"@emotion/styled": "11.14.1",
|
|
45
|
-
"@flipdish/asset-management": "^0.0.5 || ^0.0.6 || ^0.0.7",
|
|
45
|
+
"@flipdish/asset-management": "^0.0.5 || ^0.0.6 || ^0.0.7 || ^0.0.10 || ^0.0.11",
|
|
46
46
|
"@mui/material": "6.5.0",
|
|
47
47
|
"@mui/x-date-pickers": "^7.23.3",
|
|
48
48
|
"@tanstack/react-query": "^5.62.0",
|
|
@@ -68,20 +68,20 @@
|
|
|
68
68
|
"@rollup/plugin-terser": "^0.4.4",
|
|
69
69
|
"@rollup/plugin-typescript": "^12.1.1",
|
|
70
70
|
"@rollup/plugin-url": "^8.0.2",
|
|
71
|
-
"@storybook/addon-a11y": "10.4.
|
|
71
|
+
"@storybook/addon-a11y": "10.4.1",
|
|
72
72
|
"@storybook/addon-designs": "^11.1.3",
|
|
73
|
-
"@storybook/addon-docs": "10.4.
|
|
74
|
-
"@storybook/addon-links": "10.4.
|
|
73
|
+
"@storybook/addon-docs": "10.4.1",
|
|
74
|
+
"@storybook/addon-links": "10.4.1",
|
|
75
75
|
"@storybook/addon-mcp": "^0.6.0",
|
|
76
|
-
"@storybook/addon-onboarding": "10.4.
|
|
77
|
-
"@storybook/react": "10.4.
|
|
78
|
-
"@storybook/react-vite": "10.4.
|
|
76
|
+
"@storybook/addon-onboarding": "10.4.1",
|
|
77
|
+
"@storybook/react": "10.4.1",
|
|
78
|
+
"@storybook/react-vite": "10.4.1",
|
|
79
79
|
"@svgr/rollup": "^8.1.0",
|
|
80
80
|
"@tanstack/react-query": "^5.62.0",
|
|
81
81
|
"@testing-library/jest-dom": "6.9.1",
|
|
82
82
|
"@testing-library/react": "15.0.7",
|
|
83
83
|
"@types/lodash.debounce": "^4.0.9",
|
|
84
|
-
"@types/react": "18.3.
|
|
84
|
+
"@types/react": "18.3.29",
|
|
85
85
|
"@types/react-dom": "18.3.7",
|
|
86
86
|
"@types/react-imgix": "^9.5.4",
|
|
87
87
|
"@typescript-eslint/eslint-plugin": "7.18.0",
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"eslint-plugin-react-hooks": "4.6.2",
|
|
102
102
|
"eslint-plugin-react-refresh": "0.5.2",
|
|
103
103
|
"eslint-plugin-simple-import-sort": "^12.0.0",
|
|
104
|
-
"eslint-plugin-storybook": "10.4.
|
|
104
|
+
"eslint-plugin-storybook": "10.4.1",
|
|
105
105
|
"eslint-plugin-testing-library": "^7.1.1",
|
|
106
106
|
"jsdom": "24.1.3",
|
|
107
107
|
"lodash.debounce": "^4.0.8",
|
|
@@ -116,8 +116,8 @@
|
|
|
116
116
|
"rollup-plugin-multi-input": "^1.5.0",
|
|
117
117
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
118
118
|
"rollup-plugin-postcss": "^4.0.2",
|
|
119
|
-
"storybook": "10.4.
|
|
120
|
-
"storybook-addon-pseudo-states": "10.4.
|
|
119
|
+
"storybook": "10.4.1",
|
|
120
|
+
"storybook-addon-pseudo-states": "10.4.1",
|
|
121
121
|
"tslib": "^2.8.0",
|
|
122
122
|
"typescript": "5.4.5",
|
|
123
123
|
"vite": "5.4.21",
|