@fluid-app/portal-sdk 0.1.76 → 0.1.78
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/{AlertWidget-BrUi7EqF.cjs → AlertWidget-B2Qcisk7.cjs} +2 -2
- package/dist/{AlertWidget-BrUi7EqF.cjs.map → AlertWidget-B2Qcisk7.cjs.map} +1 -1
- package/dist/{AlertWidget-yjstYYw9.mjs → AlertWidget-C-7uX0HS.mjs} +2 -2
- package/dist/{AlertWidget-yjstYYw9.mjs.map → AlertWidget-C-7uX0HS.mjs.map} +1 -1
- package/dist/{AppDownloadScreen-B2Cfv-uS.cjs → AppDownloadScreen-BlfMkfi_.cjs} +5 -5
- package/dist/{AppDownloadScreen-0bjHFsn8.mjs → AppDownloadScreen-C_NGlFZa.mjs} +2 -2
- package/dist/{AppDownloadScreen-0bjHFsn8.mjs.map → AppDownloadScreen-C_NGlFZa.mjs.map} +1 -1
- package/dist/{AppDownloadScreen-BICgVr39.cjs → AppDownloadScreen-Cw1sGNNg.cjs} +2 -2
- package/dist/{AppDownloadScreen-BICgVr39.cjs.map → AppDownloadScreen-Cw1sGNNg.cjs.map} +1 -1
- package/dist/{ContactsScreen-BY_bV9hD.cjs → ContactsScreen-C6T8Vfz4.cjs} +5 -5
- package/dist/{ContactsScreen-B45I4fsu.cjs → ContactsScreen-CwBXsCW8.cjs} +2 -2
- package/dist/{ContactsScreen-B45I4fsu.cjs.map → ContactsScreen-CwBXsCW8.cjs.map} +1 -1
- package/dist/{ContactsScreen-fv44z4G1.mjs → ContactsScreen-D4Cj5AKH.mjs} +2 -2
- package/dist/{ContactsScreen-fv44z4G1.mjs.map → ContactsScreen-D4Cj5AKH.mjs.map} +1 -1
- package/dist/{FluidProvider-BPFbPldT.mjs → FluidProvider-6ARIhCTq.mjs} +7 -7
- package/dist/{FluidProvider-BPFbPldT.mjs.map → FluidProvider-6ARIhCTq.mjs.map} +1 -1
- package/dist/{FluidProvider-B7LKjuMi.cjs → FluidProvider-CidgidrK.cjs} +7 -7
- package/dist/{FluidProvider-B7LKjuMi.cjs.map → FluidProvider-CidgidrK.cjs.map} +1 -1
- package/dist/{LinkWidget-nstWDcsr.cjs → LinkWidget-JKkMn6Pt.cjs} +5 -4
- package/dist/LinkWidget-JKkMn6Pt.cjs.map +1 -0
- package/dist/{LinkWidget-DUxod35m.cjs → LinkWidget-RYHmBVzc.cjs} +1 -1
- package/dist/{LinkWidget-BdQSowRZ.mjs → LinkWidget-e0aFNkZJ.mjs} +5 -4
- package/dist/LinkWidget-e0aFNkZJ.mjs.map +1 -0
- package/dist/{MessagingScreen-ms1wGWHQ.cjs → MessagingScreen-BA8ujs72.cjs} +5 -5
- package/dist/{MessagingScreen-DGttFuoB.cjs → MessagingScreen-BshcZj2z.cjs} +3 -3
- package/dist/{MessagingScreen-DGttFuoB.cjs.map → MessagingScreen-BshcZj2z.cjs.map} +1 -1
- package/dist/{MessagingScreen-Bp5t4STI.mjs → MessagingScreen-DevHJI6g.mjs} +5 -5
- package/dist/{MessagingScreen-BHulaY7A.mjs → MessagingScreen-Dkm1cqJ8.mjs} +3 -3
- package/dist/{MessagingScreen-BHulaY7A.mjs.map → MessagingScreen-Dkm1cqJ8.mjs.map} +1 -1
- package/dist/{MySiteScreen-DY7PSqP6.cjs → MySiteScreen-BTGBB4-v.cjs} +2 -2
- package/dist/{MySiteScreen-DY7PSqP6.cjs.map → MySiteScreen-BTGBB4-v.cjs.map} +1 -1
- package/dist/{MySiteScreen-BfYDwE3C.mjs → MySiteScreen-O7m3F8bT.mjs} +2 -2
- package/dist/{MySiteScreen-BfYDwE3C.mjs.map → MySiteScreen-O7m3F8bT.mjs.map} +1 -1
- package/dist/{MySiteScreen-CEb9L0vp.cjs → MySiteScreen-i8J8Ttzz.cjs} +5 -5
- package/dist/{OrdersScreen-CB_VOlRI.cjs → OrdersScreen-BF86JWxG.cjs} +3 -3
- package/dist/{OrdersScreen-CB_VOlRI.cjs.map → OrdersScreen-BF86JWxG.cjs.map} +1 -1
- package/dist/{OrdersScreen-D5_PtRCp.cjs → OrdersScreen-CWIKWDTW.cjs} +5 -5
- package/dist/{OrdersScreen-CuQTI5f9.mjs → OrdersScreen-DoXE66il.mjs} +3 -3
- package/dist/{OrdersScreen-CuQTI5f9.mjs.map → OrdersScreen-DoXE66il.mjs.map} +1 -1
- package/dist/{ProductsScreen-DD07WW44.cjs → ProductsScreen-868va-qn.cjs} +6 -6
- package/dist/{ProductsScreen-SExV06P8.mjs → ProductsScreen-Bef0sSgj.mjs} +6 -6
- package/dist/{ProductsScreen-KLajmCLk.mjs → ProductsScreen-CFKSDJJ0.mjs} +4 -4
- package/dist/{ProductsScreen-KLajmCLk.mjs.map → ProductsScreen-CFKSDJJ0.mjs.map} +1 -1
- package/dist/{ProductsScreen-D1hLP4qn.cjs → ProductsScreen-D4dDVYFJ.cjs} +4 -4
- package/dist/{ProductsScreen-D1hLP4qn.cjs.map → ProductsScreen-D4dDVYFJ.cjs.map} +1 -1
- package/dist/{ProfileScreen-D6ie-eDm.cjs → ProfileScreen-B_I_A77d.cjs} +3 -3
- package/dist/{ProfileScreen-D6ie-eDm.cjs.map → ProfileScreen-B_I_A77d.cjs.map} +1 -1
- package/dist/{ProfileScreen-Y5zQgZL0.mjs → ProfileScreen-BsUzZmHF.mjs} +3 -3
- package/dist/{ProfileScreen-Y5zQgZL0.mjs.map → ProfileScreen-BsUzZmHF.mjs.map} +1 -1
- package/dist/{ProfileScreen-VBlfOlav.cjs → ProfileScreen-C168MNNR.cjs} +5 -5
- package/dist/{ShareablesScreen-CTruUco1.mjs → ShareablesScreen-2EJChSOH.mjs} +4 -4
- package/dist/{ShareablesScreen-CTruUco1.mjs.map → ShareablesScreen-2EJChSOH.mjs.map} +1 -1
- package/dist/{ShareablesScreen-C_aJXryA.mjs → ShareablesScreen-2N0DWP77.mjs} +6 -6
- package/dist/{ShareablesScreen-BKidUZz_.cjs → ShareablesScreen-BgUQxGzL.cjs} +6 -6
- package/dist/{ShareablesScreen-CA8sm7n_.cjs → ShareablesScreen-D9uPu6DJ.cjs} +4 -4
- package/dist/{ShareablesScreen-CA8sm7n_.cjs.map → ShareablesScreen-D9uPu6DJ.cjs.map} +1 -1
- package/dist/{ShopScreen-BZLvCzbI.mjs → ShopScreen-BaqN34iX.mjs} +6 -6
- package/dist/{ShopScreen-BZLvCzbI.mjs.map → ShopScreen-BaqN34iX.mjs.map} +1 -1
- package/dist/{ShopScreen-D0xqkK7W.cjs → ShopScreen-CTp2FaRe.cjs} +5 -5
- package/dist/{ShopScreen-saEPQJZ-.cjs → ShopScreen-DhbYFraU.cjs} +8 -8
- package/dist/{ShopScreen-saEPQJZ-.cjs.map → ShopScreen-DhbYFraU.cjs.map} +1 -1
- package/dist/{SubscriptionsScreen-D5oQ8bt1.cjs → SubscriptionsScreen-CwDH0DNT.cjs} +3 -3
- package/dist/{SubscriptionsScreen-D5oQ8bt1.cjs.map → SubscriptionsScreen-CwDH0DNT.cjs.map} +1 -1
- package/dist/{SubscriptionsScreen-CBoAwhKQ.mjs → SubscriptionsScreen-fliVeZk_.mjs} +3 -3
- package/dist/{SubscriptionsScreen-CBoAwhKQ.mjs.map → SubscriptionsScreen-fliVeZk_.mjs.map} +1 -1
- package/dist/{SubscriptionsScreen-DxYZJ0k6.cjs → SubscriptionsScreen-nkxjIDie.cjs} +5 -5
- package/dist/{TextWidget-GofZkamu.cjs → TextWidget-BCzx6OXG.cjs} +8 -7
- package/dist/TextWidget-BCzx6OXG.cjs.map +1 -0
- package/dist/{TextWidget-CJocBD6P.mjs → TextWidget-CADHRPPp.mjs} +8 -7
- package/dist/TextWidget-CADHRPPp.mjs.map +1 -0
- package/dist/index.cjs +38 -38
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +38 -38
- package/dist/{src-B-aQFEbA.cjs → src-fXyI4AWk.cjs} +36 -35
- package/dist/src-fXyI4AWk.cjs.map +1 -0
- package/dist/{src-CvQ9ezCI.mjs → src-nhqydD53.mjs} +36 -35
- package/dist/src-nhqydD53.mjs.map +1 -0
- package/dist/{use-account-clients-G3DvbvPH.mjs → use-account-clients-CDAazmHw.mjs} +2 -2
- package/dist/{use-account-clients-G3DvbvPH.mjs.map → use-account-clients-CDAazmHw.mjs.map} +1 -1
- package/dist/{use-account-clients-8yvdvTIC.cjs → use-account-clients-CgP9ZZbx.cjs} +2 -2
- package/dist/{use-account-clients-8yvdvTIC.cjs.map → use-account-clients-CgP9ZZbx.cjs.map} +1 -1
- package/dist/{use-current-user-CWnlXU8G.cjs → use-current-user-C3xp6f2-.cjs} +3 -3
- package/dist/{use-current-user-CWnlXU8G.cjs.map → use-current-user-C3xp6f2-.cjs.map} +1 -1
- package/dist/{use-current-user-DJW-ZwIi.mjs → use-current-user-CudhrJtA.mjs} +3 -3
- package/dist/{use-current-user-DJW-ZwIi.mjs.map → use-current-user-CudhrJtA.mjs.map} +1 -1
- package/dist/{use-customer-account-CJDZKn97.cjs → use-customer-account-72qR-BtL.cjs} +3 -3
- package/dist/{use-customer-account-CJDZKn97.cjs.map → use-customer-account-72qR-BtL.cjs.map} +1 -1
- package/dist/{use-customer-account-BhjZjCcF.mjs → use-customer-account-Dfyhs3LY.mjs} +3 -3
- package/dist/{use-customer-account-BhjZjCcF.mjs.map → use-customer-account-Dfyhs3LY.mjs.map} +1 -1
- package/dist/{use-fluid-api-YzLdiq5t.mjs → use-fluid-api-C53ZoBpG.mjs} +2 -2
- package/dist/{use-fluid-api-YzLdiq5t.mjs.map → use-fluid-api-C53ZoBpG.mjs.map} +1 -1
- package/dist/{use-fluid-api-BcfyUuOe.cjs → use-fluid-api-Cc0xHt74.cjs} +2 -2
- package/dist/{use-fluid-api-BcfyUuOe.cjs.map → use-fluid-api-Cc0xHt74.cjs.map} +1 -1
- package/package.json +23 -19
- package/dist/LinkWidget-BdQSowRZ.mjs.map +0 -1
- package/dist/LinkWidget-nstWDcsr.cjs.map +0 -1
- package/dist/TextWidget-CJocBD6P.mjs.map +0 -1
- package/dist/TextWidget-GofZkamu.cjs.map +0 -1
- package/dist/src-B-aQFEbA.cjs.map +0 -1
- package/dist/src-CvQ9ezCI.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"src-nhqydD53.mjs","names":["share.createShareLink","productMedia.getProductMedia","media.createMedia","playlists.addItemToPlaylist","playlists.removeItemsFromPlaylist","playlists.createPlaylist","playlists.updatePlaylist","DEFAULT_IMAGE","PAGE_SIZE","GRID_CLASS","FacebookIcon","InstagramIcon","FacebookIcon","InstagramIcon","productMedia.getProductMedia","MarketingAssetsGrid","PAGE_SIZE","GRID_CLASS","media.getMedia","media.getMediaById","X","K","P","u","centerCrop","makeAspectCrop","z","DialogPrimitive.Content","defaultFiltersState","ReactCrop","formatFileSize","DialogPrimitive.Content","defaultDamFilters","DEFAULT_IMAGE","PAGE_SIZE","GRID_CLASS","playlists.getPlaylists","DEFAULT_IMAGE","DEFAULT_IMAGE","DEFAULT_IMAGE","playlists.getPlaylistById","mediaApi.getMedia","playlists.addItemToPlaylist","playlists.getPlaylistById","fileResources.getFileResources"],"sources":["../../../shareables/core/src/context.tsx","../../../shareables/core/src/query-keys.ts","../../../shareables/core/src/hooks/use-share-link.ts","../../../shareables/core/src/hooks/use-product-media-counts.ts","../../../shareables/core/src/hooks/use-media-mutations.ts","../../../shareables/core/src/utils.ts","../../../shareables/core/src/hooks/use-playlist-mutations.ts","../../../shareables/ui/src/context.tsx","../../../shareables/ui/src/utils/strip-tags.ts","../../../shareables/ui/src/utils/social-sharing.ts","../../../shareables/ui/src/utils/os-detection.ts","../../../shareables/ui/src/components/MediaShare/ShareItemCard.tsx","../../../shareables/ui/src/components/MediaShare/products/ShareProductCard.tsx","../../../shareables/ui/src/components/screens/ProductsScreen.tsx","../../../shareables/ui/src/components/SharePage/SharePageImageDisplay.tsx","../../../shareables/ui/src/components/QrCodeDisplay.tsx","../../../shareables/ui/src/components/icons/social-icons.tsx","../../../shareables/ui/src/components/SharePage/ShareLinkSection.tsx","../../../shareables/ui/src/components/ShareablesModal/MarketingAssetsGrid.tsx","../../../shareables/ui/src/components/screens/ProductDetailScreen.tsx","../../../shareables/ui/src/components/screens/MediaListingScreen.tsx","../../../shareables/ui/src/components/screens/MediaDetailScreen.tsx","../../../file-picker/ui/src/context/FilePickerContext.tsx","../../../file-picker/ui/src/utils/brand-icons.tsx","../../../file-picker/core/src/schemas/dam.ts","../../../file-picker/core/src/schemas/file-picker-config.ts","../../../file-picker/core/src/constants/filter-options.ts","../../../file-picker/core/src/utils/mime-type.ts","../../../../node_modules/.pnpm/react-image-crop@11.0.10_react@19.2.4/node_modules/react-image-crop/dist/index.js","../../../file-picker/core/src/utils/image-crop.ts","../../../file-picker/core/src/utils/dam-asset.ts","../../../file-picker/core/src/utils/dam-url.ts","../../../file-picker/core/src/utils/filename-sanitizer.ts","../../../file-picker/api-client/src/api/dam-assets.ts","../../../file-picker/api-client/src/api/dam-query.ts","../../../file-picker/api-client/src/api/url-proxy.ts","../../../file-picker/api-client/src/api/unsplash.ts","../../../file-picker/api-client/src/query-keys.ts","../../../file-picker/ui/src/hooks/use-debounced-search.ts","../../../file-picker/ui/src/hooks/use-dam-library.ts","../../../file-picker/ui/src/components/AddFolderModal.tsx","../../../file-picker/ui/src/components/AssetActions.tsx","../../../file-picker/ui/src/utils/icons.ts","../../../file-picker/ui/src/components/DamLibraryAsset/AssetThumbnail.tsx","../../../file-picker/ui/src/components/DamLibraryAsset/SelectionIndexBadge.tsx","../../../file-picker/ui/src/components/DamLibraryAsset/AssetActionsMenu.tsx","../../../file-picker/ui/src/components/LibraryAssetGrid.tsx","../../../file-picker/ui/src/components/DamLibrary.tsx","../../../file-picker/ui/src/components/FilePickerSidebar.tsx","../../../file-picker/ui/src/components/FilePickerFiltersPanel.tsx","../../../file-picker/ui/src/components/FilePickerHeader.tsx","../../../file-picker/ui/src/components/MediaTab.tsx","../../../file-picker/ui/src/hooks/use-unsplash-picker.ts","../../../file-picker/ui/src/components/UnsplashPicker.tsx","../../../file-picker/ui/src/hooks/use-url-upload.ts","../../../file-picker/ui/src/components/UrlUpload.tsx","../../../file-picker/ui/src/hooks/use-computer-upload.ts","../../../file-picker/ui/src/components/ImageCropModal.tsx","../../../file-picker/ui/src/components/ComputerUpload.tsx","../../../file-picker/ui/src/components/FilePickerContent.tsx","../../../file-picker/ui/src/components/SelectionPanel.tsx","../../../file-picker/ui/src/components/FilePicker.tsx","../../../shareables/ui/src/components/screens/MediaCreateScreen.tsx","../../../shareables/ui/src/components/playlists/PlaylistCard.tsx","../../../shareables/ui/src/components/playlists/SearchAndSortBar.tsx","../../../shareables/ui/src/components/playlists/BulkSelectionBar.tsx","../../../shareables/ui/src/components/screens/PlaylistsListingScreen.tsx","../../../shareables/ui/src/constants.ts","../../../shareables/ui/src/components/SharePage/TaggedProductsList.tsx","../../../shareables/ui/src/components/SharePage/PlaylistItemsList.tsx","../../../shareables/ui/src/components/screens/PlaylistDetailScreen.tsx","../../../shareables/ui/src/components/playlists/form/PlaylistFormProvider.tsx","../../../shareables/ui/src/components/playlists/form/PlaylistItemsContext.tsx","../../../shareables/ui/src/components/playlists/form/PlaylistFormHeader.tsx","../../../shareables/ui/src/components/playlists/RichTextEditor.tsx","../../../shareables/ui/src/components/playlists/form/PlaylistFormDetails.tsx","../../../shareables/ui/src/components/playlists/form/SortableRow.tsx","../../../shareables/ui/src/components/playlists/form/SortableTable.tsx","../../../shareables/ui/src/components/playlists/form/PlaylistItemsSection.tsx","../../../shareables/ui/src/components/playlists/form/PlaylistOpenGraphPreview.tsx","../../../shareables/ui/src/components/screens/PlaylistCreateScreen.tsx","../../../shareables/ui/src/components/screens/FilesListingScreen.tsx","../../../shareables/ui/src/components/ShareablesApp.tsx","../../../shareables/ui/src/components/ProductsApp.tsx"],"sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { FetchClient } from \"@fluid-app/api-client-core\";\n\ninterface ShareablesCoreConfig {\n client: FetchClient;\n user: { id: number } | null;\n /** When true, use rep-scoped API endpoints (e.g. /users/v2025-06/media) */\n repContext?: boolean;\n}\n\nconst ShareablesCoreContext = createContext<ShareablesCoreConfig | null>(null);\n\nexport function ShareablesCoreProvider({\n config,\n children,\n}: {\n config: ShareablesCoreConfig;\n children: React.ReactNode;\n}): React.JSX.Element {\n return (\n <ShareablesCoreContext.Provider value={config}>\n {children}\n </ShareablesCoreContext.Provider>\n );\n}\n\nexport function useShareablesClient(): FetchClient {\n const ctx = useContext(ShareablesCoreContext);\n if (!ctx) {\n throw new Error(\n \"useShareablesClient must be used within a ShareablesCoreProvider\",\n );\n }\n return ctx.client;\n}\n\nexport function useShareablesUser(): { id: number } | null {\n const ctx = useContext(ShareablesCoreContext);\n if (!ctx) {\n throw new Error(\n \"useShareablesUser must be used within a ShareablesCoreProvider\",\n );\n }\n return ctx.user;\n}\n\nexport function useRepContext(): boolean {\n const ctx = useContext(ShareablesCoreContext);\n return ctx?.repContext ?? false;\n}\n","export const shareablesKeys = {\n media: {\n all: [\"media\"] as const,\n list: (search?: string, sortDesc?: boolean, repContext?: boolean) =>\n [\"media\", \"list\", search, sortDesc, repContext] as const,\n detail: (id: number, repContext?: boolean) =>\n [\"media\", \"detail\", id, repContext] as const,\n grid: () => [\"media\", \"grid\"] as const,\n },\n playlists: {\n all: [\"playlists\"] as const,\n detail: (id: number) => [\"playlists\", \"detail\", id] as const,\n },\n productMedia: {\n all: [\"productMedia\"] as const,\n counts: (ids: number[]) =>\n [\"productMedia\", \"counts\", ids.toSorted()] as const,\n count: (id: number) => [\"productMedia\", \"count\", id] as const,\n },\n shareLinks: {\n all: [\"shareLinks\"] as const,\n link: (type: string, id?: number, contactId?: number, locale?: string) =>\n [\"shareLinks\", \"link\", type, id, contactId, locale] as const,\n },\n};\n","import { useCallback } from \"react\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { share, type shareables } from \"@fluid-app/shareables-api-client\";\nimport { useShareablesClient } from \"../context\";\nimport { shareablesKeys } from \"../query-keys\";\n\nexport interface ShareLinkItem {\n id?: number;\n share_link?: string;\n}\n\nexport function useShareLink(\n item: ShareLinkItem,\n shareableType: string,\n contactId?: number,\n locale = \"en\",\n) {\n const client = useShareablesClient();\n const queryClient = useQueryClient();\n\n // Check if we need an ID for this shareable type\n const isIdRequired = shareableType !== \"MySite\";\n\n // Check if the ID is a valid database ID (integer) vs a generated float ID\n const isValidDatabaseId =\n item?.id && Number.isInteger(item.id) && item.id > 1;\n\n const shouldFetch = isIdRequired ? isValidDatabaseId : true;\n\n const queryKey = shareablesKeys.shareLinks.link(\n shareableType,\n item?.id,\n contactId,\n locale,\n );\n\n const {\n data: shareLink,\n isLoading: loading,\n error,\n refetch,\n } = useQuery({\n queryKey,\n queryFn: async () => {\n // If we have an existing share_link, return it\n if (item?.share_link && !contactId) {\n return item.share_link;\n }\n\n if (isIdRequired && !item?.id) {\n throw new Error(\"Something went wrong, please try again.\");\n }\n\n if (isIdRequired && !isValidDatabaseId) {\n throw new Error(\"Share links are not available for this item.\");\n }\n\n const input: shareables.CreateShareLinkInput = {\n locale,\n relateableType: shareableType,\n ...(isIdRequired && item?.id && { relateableId: item.id }),\n ...(contactId && { contactId }),\n };\n\n const link = await share.createShareLink(client, input);\n return link;\n },\n enabled: isIdRequired ? Boolean(shouldFetch && item?.id) : true,\n staleTime: 5 * 60 * 1000,\n gcTime: 10 * 60 * 1000,\n });\n\n const getShareLink = useCallback(async () => {\n if (shareLink) return shareLink;\n const result = await refetch();\n return result.data;\n }, [shareLink, refetch]);\n\n const resetShareLink = useCallback(() => {\n queryClient.removeQueries({ queryKey });\n }, [queryClient, queryKey]);\n\n return {\n loading,\n shareLink,\n getShareLink,\n resetShareLink,\n error,\n };\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { productMedia } from \"@fluid-app/shareables-api-client\";\nimport { useShareablesClient } from \"../context\";\nimport { shareablesKeys } from \"../query-keys\";\n\ninterface ProductMediaCountsResult {\n [productId: number]: number;\n}\n\nexport function useProductMediaCounts(productIds: number[]) {\n const client = useShareablesClient();\n\n return useQuery({\n queryKey: shareablesKeys.productMedia.counts(productIds),\n queryFn: async (): Promise<ProductMediaCountsResult> => {\n const counts: ProductMediaCountsResult = {};\n\n // Fetch media for each product in parallel\n const promises = productIds.map(async (productId) => {\n try {\n const response = await productMedia.getProductMedia(\n client,\n productId,\n );\n const mediaArray = response.media || [];\n counts[productId] = mediaArray.length;\n } catch (error) {\n console.warn(\n `Failed to fetch media count for product ${productId}:`,\n error,\n );\n counts[productId] = 0;\n }\n });\n\n await Promise.allSettled(promises);\n return counts;\n },\n enabled: productIds.length > 0,\n staleTime: 5 * 60 * 1000,\n gcTime: 10 * 60 * 1000,\n });\n}\n\nexport function useProductMediaCount(productId: number) {\n const client = useShareablesClient();\n\n return useQuery({\n queryKey: shareablesKeys.productMedia.count(productId),\n queryFn: async (): Promise<number> => {\n try {\n const response = await productMedia.getProductMedia(client, productId);\n const mediaArray = response.media || [];\n return mediaArray.length;\n } catch (error) {\n console.warn(\n `Failed to fetch media count for product ${productId}:`,\n error,\n );\n return 0;\n }\n },\n enabled: !!productId,\n staleTime: 5 * 60 * 1000,\n gcTime: 10 * 60 * 1000,\n });\n}\n","import { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { media, type shareables } from \"@fluid-app/shareables-api-client\";\nimport { useShareablesClient, useRepContext } from \"../context\";\nimport { shareablesKeys } from \"../query-keys\";\n\nexport interface UseMediaMutationsOptions {\n onDeleteSuccess?: (id: number) => void;\n onDeleteError?: (error: Error, id: number) => void;\n onDuplicateSuccess?: (newMedia: shareables.MediumResponse) => void;\n onDuplicateError?: (error: Error) => void;\n}\n\nexport const useMediaMutations = (options?: UseMediaMutationsOptions) => {\n const client = useShareablesClient();\n const queryClient = useQueryClient();\n const repContext = useRepContext();\n\n // Delete media mutation with optimistic updates\n const deleteMutation = useMutation({\n mutationFn: (id: number) => {\n return media.deleteMedia(client, id, repContext);\n },\n onMutate: async (id: number) => {\n await queryClient.cancelQueries({\n queryKey: shareablesKeys.media.all,\n });\n\n // Optimistically remove from media lists\n const previousData = queryClient.getQueriesData({\n queryKey: shareablesKeys.media.all,\n });\n\n queryClient.setQueriesData<unknown>(\n { queryKey: shareablesKeys.media.all },\n (oldData: unknown) => {\n if (!oldData) return oldData;\n\n // Handle different response formats\n if (Array.isArray(oldData)) {\n return oldData.filter((item: { id: number }) => item.id !== id);\n }\n\n const dataObj = oldData as Record<string, unknown>;\n if (dataObj.data && Array.isArray(dataObj.data)) {\n return {\n ...dataObj,\n data: dataObj.data.filter(\n (item: { id: number }) => item.id !== id,\n ),\n };\n }\n\n if (dataObj.media && Array.isArray(dataObj.media)) {\n return {\n ...dataObj,\n media: dataObj.media.filter(\n (item: { id: number }) => item.id !== id,\n ),\n };\n }\n\n return oldData;\n },\n );\n\n // Remove individual media query\n queryClient.removeQueries({\n queryKey: shareablesKeys.media.detail(id, repContext),\n });\n\n return { previousData, id };\n },\n onSuccess: async (_, id) => {\n // Invalidate all media queries to refetch fresh data\n queryClient.invalidateQueries({\n queryKey: shareablesKeys.media.all,\n });\n options?.onDeleteSuccess?.(id);\n },\n onError: (error: Error, _id: number, context) => {\n // Rollback optimistic updates\n if (context?.previousData) {\n context.previousData.forEach(([queryKey, data]) => {\n queryClient.setQueryData(queryKey, data);\n });\n }\n\n console.error(\"Error deleting media:\", error);\n options?.onDeleteError?.(error, _id);\n },\n });\n\n // Duplicate media mutation with optimistic update\n const duplicateMutation = useMutation({\n mutationFn: (mediaData: shareables.MediumUpdate) => {\n return media.createMedia(client, mediaData, repContext);\n },\n onMutate: async (mediaData) => {\n await queryClient.cancelQueries({\n queryKey: shareablesKeys.media.all,\n });\n\n // Generate temporary ID for optimistic update\n const tempId = Date.now();\n\n // Create optimistic media object\n const optimisticMedia = {\n id: tempId,\n title: mediaData.title || \"Untitled Media\",\n description: mediaData.description || null,\n media_type: \"share\",\n media_format: null,\n image_url: mediaData.image_url || null,\n video_url: mediaData.video_url || null,\n pdf_url: mediaData.pdf_url || null,\n stripped: mediaData.description || null,\n active: mediaData.active ?? true,\n visibility: mediaData.visibility || null,\n share_link: null,\n views: 0,\n leads: 0,\n watch: null,\n video_status: null,\n duration: null,\n cta_url: mediaData.cta_url || null,\n cta_button_text: mediaData.cta_button_text || null,\n cta_enabled: mediaData.cta_enabled ?? false,\n cta_action_type: mediaData.cta_action_type || null,\n video_shopping_enabled: mediaData.video_shopping_enabled ?? false,\n prompts_enabled: mediaData.prompts_enabled ?? false,\n ranks: [],\n preview_link: null,\n attached_shareables: [],\n subtitles: {},\n user_id: null,\n created_at: new Date().toISOString(),\n };\n\n // Snapshot previous data for rollback\n const previousMediaLists = queryClient.getQueriesData({\n queryKey: shareablesKeys.media.all,\n });\n\n // Optimistically add media to list queries\n queryClient.setQueriesData<unknown>(\n { queryKey: shareablesKeys.media.all },\n (oldData: unknown) => {\n if (!oldData) return [optimisticMedia];\n\n // Handle different response formats\n if (Array.isArray(oldData)) {\n return [optimisticMedia, ...oldData];\n }\n\n const dataObj = oldData as Record<string, unknown>;\n if (dataObj.data && Array.isArray(dataObj.data)) {\n return { ...dataObj, data: [optimisticMedia, ...dataObj.data] };\n }\n\n if (dataObj.media && Array.isArray(dataObj.media)) {\n return { ...dataObj, media: [optimisticMedia, ...dataObj.media] };\n }\n\n return oldData;\n },\n );\n\n // Set optimistic individual media query\n queryClient.setQueryData(\n shareablesKeys.media.detail(tempId, repContext),\n {\n media: optimisticMedia,\n },\n );\n\n return { previousMediaLists, tempId };\n },\n onError: (_error, _mediaData, context) => {\n // Rollback optimistic updates\n if (context) {\n context.previousMediaLists.forEach(([queryKey, data]) => {\n queryClient.setQueryData(queryKey, data);\n });\n\n queryClient.removeQueries({\n queryKey: shareablesKeys.media.detail(context.tempId),\n });\n }\n\n console.error(\"Error duplicating media:\", _error);\n options?.onDuplicateError?.(_error);\n },\n onSuccess: async (data, _mediaData, context) => {\n const realMediaId = data.id;\n\n if (context) {\n // Remove optimistic data\n queryClient.removeQueries({\n queryKey: shareablesKeys.media.detail(context.tempId),\n });\n }\n\n // Set real media data\n queryClient.setQueryData(\n shareablesKeys.media.detail(realMediaId, repContext),\n {\n media: data,\n },\n );\n\n options?.onDuplicateSuccess?.(data);\n\n // Invalidate lists to get fresh data\n queryClient.invalidateQueries({\n queryKey: shareablesKeys.media.all,\n predicate: (query) => {\n return query.queryKey.length === 1 && query.queryKey[0] === \"media\";\n },\n });\n },\n });\n\n // Handler functions\n const handleDelete = async (id: number) => {\n try {\n await deleteMutation.mutateAsync(id);\n } catch (error) {\n console.error(\"Delete failed:\", error);\n }\n };\n\n const handleDuplicate = async (mediaData: shareables.MediumUpdate) => {\n try {\n await duplicateMutation.mutateAsync(mediaData);\n } catch (error) {\n console.error(\"Duplicate failed:\", error);\n }\n };\n\n return {\n deleteMutation,\n duplicateMutation,\n handleDelete,\n handleDuplicate,\n };\n};\n\n// ---------------------------------------------------------------------------\n// useCreateMediaMutation — dedicated create (no optimistic duplication logic)\n// ---------------------------------------------------------------------------\n\nexport interface UseCreateMediaOptions {\n onSuccess?: (newMedia: shareables.MediumResponse) => void;\n onError?: (error: Error) => void;\n}\n\nexport const useCreateMediaMutation = (options?: UseCreateMediaOptions) => {\n const client = useShareablesClient();\n const queryClient = useQueryClient();\n const repContext = useRepContext();\n\n return useMutation({\n mutationFn: (payload: shareables.MediumUpdate) => {\n return media.createMedia(client, payload, repContext);\n },\n onSuccess: (data) => {\n queryClient.invalidateQueries({\n queryKey: shareablesKeys.media.all,\n });\n options?.onSuccess?.(data);\n },\n onError: (error) => {\n console.error(\"Error creating media:\", error);\n options?.onError?.(error as Error);\n },\n });\n};\n","/**\n * Check if an error is a cancellation/abort error from TanStack Query or AbortController.\n */\nexport function isCancellationError(error: unknown): boolean {\n return (\n !!error &&\n typeof error === \"object\" &&\n \"name\" in error &&\n (error.name === \"CancelledError\" || error.name === \"AbortError\")\n );\n}\n","import { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { playlists, type shareables } from \"@fluid-app/shareables-api-client\";\nimport { useShareablesClient } from \"../context\";\nimport { shareablesKeys } from \"../query-keys\";\nimport { isCancellationError } from \"../utils\";\n\n// ---------------------------------------------------------------------------\n// Types for optimistic updates (internal)\n// ---------------------------------------------------------------------------\n\ninterface PlaylistItemLike {\n id?: number | null;\n order?: number;\n relateable_type?: string | null;\n relateable?: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// useAddItemToPlaylistMutation\n// ---------------------------------------------------------------------------\n\nexport interface UseAddItemOptions {\n onSuccess?: (playlistId: number) => void;\n onError?: (error: Error, playlistId: number) => void;\n}\n\ninterface AddItemToPlaylistParams {\n playlistId: number;\n newItem: PlaylistItemLike;\n data: { relateable_type: string; relateable_id: number; order: number };\n}\n\nexport const useAddItemToPlaylistMutation = (options?: UseAddItemOptions) => {\n const client = useShareablesClient();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({ playlistId, data }: AddItemToPlaylistParams) => {\n return playlists.addItemToPlaylist(client, playlistId, {\n items: [data],\n });\n },\n onMutate: async ({ playlistId, newItem }: AddItemToPlaylistParams) => {\n await queryClient.cancelQueries({\n queryKey: shareablesKeys.playlists.detail(playlistId),\n });\n\n const previousData = queryClient.getQueryData(\n shareablesKeys.playlists.detail(playlistId),\n );\n\n queryClient.setQueryData(\n shareablesKeys.playlists.detail(playlistId),\n (old: unknown) => {\n if (!old || typeof old !== \"object\" || !(\"playlist\" in old))\n return old;\n\n const playlistData = old as {\n playlist: { items?: PlaylistItemLike[] };\n };\n return {\n ...playlistData,\n playlist: {\n ...playlistData.playlist,\n items: [...(playlistData.playlist.items || []), newItem],\n },\n };\n },\n );\n\n return { previousData };\n },\n onSuccess: (_, { playlistId }) => {\n queryClient.invalidateQueries({\n queryKey: shareablesKeys.playlists.detail(playlistId),\n });\n queryClient.invalidateQueries({\n queryKey: shareablesKeys.playlists.all,\n });\n options?.onSuccess?.(playlistId);\n },\n onError: (error, { playlistId }, context) => {\n // Always rollback optimistic updates first\n if (context?.previousData) {\n queryClient.setQueryData(\n shareablesKeys.playlists.detail(playlistId),\n context.previousData,\n );\n }\n\n if (isCancellationError(error)) return;\n\n console.error(\"Error adding item to playlist:\", error);\n options?.onError?.(error as Error, playlistId);\n },\n });\n};\n\n// ---------------------------------------------------------------------------\n// useRemoveItemsFromPlaylistMutation\n// ---------------------------------------------------------------------------\n\nexport interface UseRemoveItemsOptions {\n onSuccess?: (playlistId: number) => void;\n onError?: (error: Error, playlistId: number) => void;\n}\n\ninterface RemoveItemsFromPlaylistParams {\n playlistId: number;\n itemIds: number[];\n}\n\nexport const useRemoveItemsFromPlaylistMutation = (\n options?: UseRemoveItemsOptions,\n) => {\n const client = useShareablesClient();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({ playlistId, itemIds }: RemoveItemsFromPlaylistParams) => {\n return playlists.removeItemsFromPlaylist(client, playlistId, {\n item_ids: itemIds,\n });\n },\n onMutate: async ({\n playlistId,\n itemIds,\n }: RemoveItemsFromPlaylistParams) => {\n await queryClient.cancelQueries({\n queryKey: shareablesKeys.playlists.detail(playlistId),\n });\n\n const previousData = queryClient.getQueryData(\n shareablesKeys.playlists.detail(playlistId),\n );\n\n queryClient.setQueryData(\n shareablesKeys.playlists.detail(playlistId),\n (old: unknown) => {\n if (!old || typeof old !== \"object\" || !(\"playlist\" in old))\n return old;\n\n const playlistData = old as {\n playlist: { items?: PlaylistItemLike[] };\n };\n return {\n ...playlistData,\n playlist: {\n ...playlistData.playlist,\n items: (playlistData.playlist.items || []).filter(\n (item) => item.id != null && !itemIds.includes(item.id),\n ),\n },\n };\n },\n );\n\n return { previousData };\n },\n onSuccess: (_, { playlistId }) => {\n queryClient.invalidateQueries({\n queryKey: shareablesKeys.playlists.detail(playlistId),\n });\n queryClient.invalidateQueries({\n queryKey: shareablesKeys.playlists.all,\n });\n options?.onSuccess?.(playlistId);\n },\n onError: (error, { playlistId }, context) => {\n // Always rollback optimistic updates first\n if (context?.previousData) {\n queryClient.setQueryData(\n shareablesKeys.playlists.detail(playlistId),\n context.previousData,\n );\n }\n\n if (isCancellationError(error)) return;\n\n // \"No valid playlist items found\" means the last item was removed — treat as success\n const errorMessage =\n error && typeof error === \"object\" && \"message\" in error\n ? String(error.message)\n : \"\";\n\n if (errorMessage.includes(\"No valid playlist items found\")) {\n queryClient.invalidateQueries({\n queryKey: shareablesKeys.playlists.detail(playlistId),\n });\n options?.onSuccess?.(playlistId);\n return;\n }\n\n console.error(\"Error removing item from playlist:\", error);\n options?.onError?.(error as Error, playlistId);\n },\n });\n};\n\n// ---------------------------------------------------------------------------\n// useCreatePlaylistMutation\n// ---------------------------------------------------------------------------\n\nexport interface UseCreatePlaylistOptions {\n onSuccess?: (response: shareables.GetPlaylistResponse) => void;\n onError?: (error: Error) => void;\n}\n\nexport const useCreatePlaylistMutation = (\n options?: UseCreatePlaylistOptions,\n) => {\n const client = useShareablesClient();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (input: shareables.CreatePlaylistInput) => {\n return playlists.createPlaylist(client, input);\n },\n onSuccess: (createdPlaylist) => {\n queryClient.invalidateQueries({\n queryKey: shareablesKeys.playlists.all,\n });\n options?.onSuccess?.(createdPlaylist);\n },\n onError: (error) => {\n if (isCancellationError(error)) return;\n\n console.error(\"Error creating playlist:\", error);\n options?.onError?.(error as Error);\n },\n });\n};\n\n// ---------------------------------------------------------------------------\n// useUpdatePlaylistMutation\n// ---------------------------------------------------------------------------\n\nexport interface UseUpdatePlaylistOptions {\n onSuccess?: (playlistId: number) => void;\n onError?: (error: Error) => void;\n}\n\ninterface UpdatePlaylistParams {\n playlistId: number;\n data: shareables.UpdatePlaylistInput;\n}\n\nexport const useUpdatePlaylistMutation = (\n options?: UseUpdatePlaylistOptions,\n) => {\n const client = useShareablesClient();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: ({ playlistId, data }: UpdatePlaylistParams) => {\n return playlists.updatePlaylist(client, playlistId, data);\n },\n onSuccess: (_, { playlistId }) => {\n queryClient.invalidateQueries({\n queryKey: shareablesKeys.playlists.all,\n });\n queryClient.invalidateQueries({\n queryKey: shareablesKeys.playlists.detail(playlistId),\n });\n options?.onSuccess?.(playlistId);\n },\n onError: (error) => {\n if (isCancellationError(error)) return;\n\n console.error(\"Error updating playlist:\", error);\n options?.onError?.(error as Error);\n },\n });\n};\n","\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { FilePickerClient } from \"@fluid-app/file-picker-api-client\";\n\nexport type RenderImageProps = {\n src: string;\n alt: string;\n width?: number;\n height?: number;\n fill?: boolean;\n className?: string;\n onError?: () => void;\n};\n\nexport interface ShareablesUIConfig {\n /** User info for fallback logo, etc. */\n user?: { id: number; company?: { logo_url?: string | null } | null } | null;\n /** Affiliate ID for user-scoped operations (favorites toggle) */\n affiliateId?: number | null;\n /** Base path for rep links (e.g., \"/rep/123\") */\n basePath: string;\n /** Navigation callback — replaces useRouter.push */\n navigate: (path: string) => void;\n /** Toast callback — replaces fluidToast */\n showToast: (opts: {\n title: string;\n type: \"success\" | \"error\" | \"warning\";\n error?: unknown;\n }) => void;\n /** MySite share handler — admin-coupled (dynamic import).\n * The implementation is responsible for providing user-specific fields\n * (affiliate_id, country_id) from its own auth context. */\n onMySiteShare?: (params: {\n shareLink: string;\n relateable_id: number;\n relateable_type: string;\n }) => Promise<void>;\n /** File download handler — replaces useFileDownload */\n onFileDownload?: (url: string, filename: string) => void;\n /** File picker API client for DAM asset browsing/uploading */\n filePickerClient?: FilePickerClient;\n /** Custom image renderer — replaces next/image imports */\n renderImage?: (props: RenderImageProps) => React.ReactNode;\n /** Toggle favorite on a playlist (Library) or other entity.\n * Host app provides the implementation since the endpoint lives\n * outside the shareables API domain. */\n onToggleFavorite?: (params: {\n favoriteableId: number;\n favoriteableType: string;\n }) => Promise<{ is_favorited: boolean }>;\n /** Delete a playlist by ID. */\n onDeletePlaylist?: (playlistId: number) => Promise<void>;\n}\n\nconst MISSING_PROVIDER = Symbol(\"missing-shareables-provider\");\n\nconst ShareablesUIContext = createContext<\n ShareablesUIConfig | typeof MISSING_PROVIDER\n>(MISSING_PROVIDER);\n\nexport function ShareablesUIProvider({\n config,\n children,\n}: {\n config: ShareablesUIConfig;\n children: React.ReactNode;\n}) {\n return (\n <ShareablesUIContext.Provider value={config}>\n {children}\n </ShareablesUIContext.Provider>\n );\n}\n\nfunction defaultRenderImage(props: RenderImageProps): React.ReactNode {\n return (\n <img\n src={props.src}\n alt={props.alt}\n width={props.width}\n height={props.height}\n className={`${props.fill ? \"absolute inset-0 h-full w-full object-cover\" : \"\"} ${props.className ?? \"\"}`}\n onError={props.onError}\n />\n );\n}\n\nexport function useRenderImage(): (props: RenderImageProps) => React.ReactNode {\n const ctx = useContext(ShareablesUIContext);\n if (ctx === MISSING_PROVIDER) {\n return defaultRenderImage;\n }\n return ctx.renderImage ?? defaultRenderImage;\n}\n\nexport function useShareablesUI(): ShareablesUIConfig {\n const ctx = useContext(ShareablesUIContext);\n if (ctx === MISSING_PROVIDER) {\n throw new Error(\n \"useShareablesUI must be used within a <ShareablesUIProvider>. \" +\n \"Wrap your component tree with <ShareablesUIProvider config={...}>.\",\n );\n }\n return ctx;\n}\n","export const stripTags = (str: string) => {\n if (!str) return \"\";\n\n if (typeof document === \"undefined\") {\n // SSR fallback: decode common HTML entities then strip tags\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/<[^>]*>/g, \"\");\n }\n\n // Decoding HTML entities since user-input description might contain them\n const temp = document.createElement(\"textarea\");\n temp.innerHTML = str;\n const decoded = temp.value;\n\n const doc = new DOMParser().parseFromString(decoded, \"text/html\");\n return doc.body.textContent || \"\";\n};\n","export interface SocialShareParams {\n url: string;\n title?: string;\n description?: string;\n hashtags?: string[];\n}\n\nexport function getFacebookShareUrl({\n url,\n title,\n description,\n}: SocialShareParams): string {\n const params = new URLSearchParams({\n u: url,\n ...(title && { quote: title }),\n ...(description && { description }),\n });\n\n return `https://www.facebook.com/sharer/sharer.php?${params.toString()}`;\n}\n\nexport function getXShareUrl({\n url,\n title,\n hashtags,\n}: SocialShareParams): string {\n const params = new URLSearchParams({\n url,\n ...(title && { text: title }),\n ...(hashtags && hashtags.length > 0 && { hashtags: hashtags.join(\",\") }),\n });\n\n return `https://twitter.com/intent/tweet?${params.toString()}`;\n}\n\nexport function getLinkedInShareUrl({\n url,\n title,\n description,\n}: SocialShareParams): string {\n const params = new URLSearchParams({\n url,\n ...(title && { title }),\n ...(description && { summary: description }),\n });\n\n return `https://www.linkedin.com/sharing/share-offsite/?${params.toString()}`;\n}\n\nexport function handleInstagramShare({\n url,\n}: SocialShareParams): Promise<void> {\n return navigator.clipboard.writeText(url).then(() => {\n setTimeout(() => {\n window.open(\"https://www.instagram.com\", \"_blank\");\n }, 2000);\n });\n}\n\nexport function handleTikTokShare({ url }: SocialShareParams): Promise<void> {\n return navigator.clipboard.writeText(url).then(() => {\n setTimeout(() => {\n window.open(\"https://www.tiktok.com\", \"_blank\");\n }, 2000);\n });\n}\n\nexport function openSocialShare(shareUrl: string): void {\n const width = 600;\n const height = 400;\n const left = (window.screen.width - width) / 2;\n const top = (window.screen.height - height) / 2;\n\n window.open(\n shareUrl,\n \"social-share\",\n `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`,\n );\n}\n\nexport async function handleSocialShare(\n platform: \"facebook\" | \"x\" | \"linkedin\" | \"instagram\" | \"tiktok\",\n params: SocialShareParams,\n onSuccess?: (message: string) => void,\n onError?: (message: string) => void,\n): Promise<void> {\n try {\n switch (platform) {\n case \"facebook\":\n openSocialShare(getFacebookShareUrl(params));\n onSuccess?.(\"Opening Facebook share dialog...\");\n break;\n\n case \"x\":\n openSocialShare(getXShareUrl(params));\n onSuccess?.(\"Opening X share dialog...\");\n break;\n\n case \"linkedin\":\n openSocialShare(getLinkedInShareUrl(params));\n onSuccess?.(\"Opening LinkedIn share dialog...\");\n break;\n\n case \"instagram\":\n await handleInstagramShare(params);\n onSuccess?.(\n \"Link copied! Opening Instagram and paste link in bio or post!.\",\n );\n break;\n\n case \"tiktok\":\n await handleTikTokShare(params);\n onSuccess?.(\"Link copied! Opening TikTok and paste in your link!\");\n break;\n\n default:\n throw new Error(`Unsupported platform: ${platform}`);\n }\n } catch (error) {\n console.error(\"Social sharing error:\", error);\n onError?.(`Failed to share on ${platform}. Please try again.`);\n }\n}\n","export type OperatingSystem =\n | \"mac\"\n | \"windows\"\n | \"linux\"\n | \"ios\"\n | \"android\"\n | \"unknown\";\n\nexport function getOperatingSystem(): OperatingSystem {\n if (typeof navigator === \"undefined\") {\n return \"unknown\";\n }\n\n const userAgent = navigator.userAgent.toLowerCase();\n const platform = navigator.platform?.toLowerCase() || \"\";\n\n if (/iphone|ipad|ipod/.test(userAgent)) {\n return \"ios\";\n }\n\n if (/android/.test(userAgent)) {\n return \"android\";\n }\n\n if (/mac/.test(platform) || /darwin/.test(userAgent)) {\n return \"mac\";\n }\n\n if (/win/.test(platform) || /windows/.test(userAgent)) {\n return \"windows\";\n }\n\n if (/linux/.test(platform) || /x11/.test(platform)) {\n return \"linux\";\n }\n\n return \"unknown\";\n}\n\nexport function isMobileDevice(): boolean {\n const os = getOperatingSystem();\n return os === \"ios\" || os === \"android\";\n}\n\nexport function isDesktopDevice(): boolean {\n const os = getOperatingSystem();\n return os === \"mac\" || os === \"windows\" || os === \"linux\";\n}\n","\"use client\";\n\nimport { Card, Badge } from \"@fluid-app/ui-primitives\";\nimport { CirclePlay } from \"lucide-react\";\nimport { useShareablesUI, useRenderImage } from \"../../context\";\n\nexport interface ShareItemCardProps {\n /** Display title */\n title: string;\n /** Image URL to display */\n imageUrl?: string | null;\n /** Navigation URL when card is clicked */\n href: string;\n /** Optional badge to display (e.g., \"5 items\", \"12 assets\") */\n badge?: {\n text: string;\n isLoading?: boolean;\n };\n /** Whether this item is a video */\n isVideo?: boolean;\n /** Subtitle text below the title */\n subtitle?: string;\n}\n\nconst DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\nexport default function ShareItemCard({\n title,\n imageUrl,\n href,\n badge,\n isVideo = false,\n subtitle = \"Click to find shareable assets\",\n}: ShareItemCardProps) {\n const { navigate } = useShareablesUI();\n const renderImage = useRenderImage();\n\n function handleClick() {\n navigate(href);\n }\n\n return (\n <Card\n role=\"button\"\n tabIndex={0}\n onClick={handleClick}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n handleClick();\n }\n }}\n className=\"group hover:bg-muted cursor-pointer gap-0 overflow-hidden rounded-lg border-0 p-0 shadow-none transition-colors\"\n >\n {/* Image container */}\n <div className=\"bg-muted relative aspect-square overflow-hidden rounded-lg\">\n {renderImage({\n src: imageUrl || DEFAULT_IMAGE,\n alt: title,\n fill: true,\n className: \"object-cover transition-transform group-hover:scale-105\",\n })}\n\n {/* Video play indicator */}\n {isVideo && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <div className=\"bg-foreground/50 flex h-16 w-16 items-center justify-center rounded-full backdrop-blur-sm\">\n <CirclePlay className=\"text-background h-12 w-12\" />\n </div>\n </div>\n )}\n\n {/* Badge */}\n {badge && !isVideo && !badge.isLoading && (\n <Badge className=\"absolute top-2 right-2 shadow-lg\" variant=\"default\">\n {badge.text}\n </Badge>\n )}\n\n {/* Loading indicator for badge */}\n {badge?.isLoading && !isVideo && (\n <div className=\"bg-muted-foreground absolute top-2 right-2 h-6 w-6 animate-pulse rounded-full\" />\n )}\n </div>\n\n {/* Item info */}\n <div className=\"px-2 pt-2 pb-4\">\n <h3 className=\"text-foreground line-clamp-2 text-sm leading-tight font-bold\">\n {title || \"Untitled\"}\n </h3>\n <p className=\"text-muted-foreground mt-1 text-xs\">{subtitle}</p>\n </div>\n </Card>\n );\n}\n","import { useProductMediaCount } from \"@fluid-app/shareables-core\";\nimport ShareItemCard from \"../ShareItemCard\";\n\ninterface ShareProductCardProps {\n product: {\n id: number;\n title: string;\n image_url?: string | null;\n images?: Array<{ image_url: string; position?: number }> | null;\n kind?: string;\n video_url?: string;\n };\n}\n\nfunction getProductImageUrl(\n product: ShareProductCardProps[\"product\"],\n): string | null {\n if (Array.isArray(product.images) && product.images.length > 0) {\n const sortedImages = [...product.images].sort(\n (a, b) => (a.position ?? 0) - (b.position ?? 0),\n );\n const primaryImage = sortedImages[0];\n if (primaryImage?.image_url) {\n return primaryImage.image_url;\n }\n }\n return product.image_url ?? null;\n}\n\nexport default function ShareProductCard({ product }: ShareProductCardProps) {\n const { data: mediaCount = 0, isLoading: isLoadingMediaCount } =\n useProductMediaCount(product.id);\n\n const imageUrl = getProductImageUrl(product);\n const isVideo = product.kind === \"video\" && !!product.video_url;\n\n return (\n <ShareItemCard\n title={product.title}\n imageUrl={imageUrl}\n href={`product/${product.id}`}\n badge={\n !isVideo\n ? { text: `${mediaCount} assets`, isLoading: isLoadingMediaCount }\n : undefined\n }\n isVideo={isVideo}\n />\n );\n}\n","\"use client\";\n\nimport { useState, useEffect, useRef, useCallback, useMemo } from \"react\";\nimport { useInfiniteQuery } from \"@tanstack/react-query\";\nimport { listProducts } from \"@fluid-app/products-api-client\";\nimport { useShareablesClient } from \"@fluid-app/shareables-core\";\nimport {\n Breadcrumb,\n BreadcrumbList,\n BreadcrumbItem,\n BreadcrumbPage,\n Input,\n Skeleton,\n} from \"@fluid-app/ui-primitives\";\nimport { Search, ArrowUpAZ, ArrowDownZA } from \"lucide-react\";\nimport { useScreenHeaderBreadcrumbs } from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport ShareProductCard from \"../MediaShare/products/ShareProductCard\";\n\nconst PAGE_SIZE = 24;\n\nconst GRID_CLASS =\n \"grid grid-cols-1 gap-8 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-4\";\n\nexport interface ProductsScreenProps {\n countryCode?: string;\n onNavigate?: (screen: string, detailId?: string) => void;\n}\n\nexport function ProductsScreen({\n countryCode,\n onNavigate,\n}: ProductsScreenProps) {\n const client = useShareablesClient();\n\n const headerBreadcrumbs = useMemo(\n () => (\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">Products</BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n ),\n [],\n );\n useScreenHeaderBreadcrumbs(headerBreadcrumbs);\n\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [debouncedSearch, setDebouncedSearch] = useState(\"\");\n const [sortDesc, setSortDesc] = useState(false);\n const observerTarget = useRef<HTMLDivElement>(null);\n\n // Debounce search input\n useEffect(() => {\n const timer = setTimeout(() => {\n setDebouncedSearch(searchTerm);\n }, 300);\n return () => clearTimeout(timer);\n }, [searchTerm]);\n\n const {\n data,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n fetchNextPage,\n error,\n } = useInfiniteQuery({\n queryKey: [\"shareables-products\", debouncedSearch, sortDesc, countryCode],\n queryFn: async ({ pageParam = 1 }) => {\n const response = await listProducts(client, {\n page: pageParam,\n per_page: PAGE_SIZE,\n search_query: debouncedSearch || undefined,\n sorting: { id: \"title\", desc: sortDesc },\n status: [\"active\"],\n country_code: countryCode ? [countryCode] : [\"US\"],\n published_stores: [\"rep\"],\n });\n return response.products;\n },\n getNextPageParam: (lastPage, allPages) =>\n lastPage.length === PAGE_SIZE ? allPages.length + 1 : undefined,\n initialPageParam: 1,\n });\n\n const handleIntersect = useCallback(\n (entries: IntersectionObserverEntry[]) => {\n if (entries[0]?.isIntersecting && hasNextPage && !isFetchingNextPage) {\n fetchNextPage();\n }\n },\n [hasNextPage, isFetchingNextPage, fetchNextPage],\n );\n\n useEffect(() => {\n const target = observerTarget.current;\n if (!target) return;\n\n const observer = new IntersectionObserver(handleIntersect, {\n threshold: 0.1,\n rootMargin: \"200px\",\n });\n observer.observe(target);\n return () => observer.disconnect();\n }, [handleIntersect]);\n\n const products = data?.pages.flat() ?? [];\n\n // Loading skeleton\n if (isLoading) {\n return (\n <div className=\"space-y-6 px-4 py-4 md:px-10 md:py-6\">\n <div className=\"flex items-center gap-3\">\n <Skeleton className=\"h-10 flex-1\" />\n <Skeleton className=\"h-10 w-10\" />\n </div>\n <div className={GRID_CLASS}>\n {Array.from({ length: 8 }).map((_, i) => (\n <div key={i} className=\"space-y-2\">\n <Skeleton className=\"aspect-square w-full rounded-lg\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-3 w-1/2\" />\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex flex-col items-center justify-center py-16\">\n <p className=\"text-destructive text-sm\">\n Failed to load products. Please try again.\n </p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 px-4 py-4 md:px-10 md:py-6\">\n {/* Search and sort controls */}\n <div className=\"flex items-center gap-3\">\n <div className=\"relative flex-1\">\n <Search className=\"text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2\" />\n <Input\n placeholder=\"Search products...\"\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n className=\"pl-9\"\n />\n </div>\n <button\n type=\"button\"\n onClick={() => setSortDesc((prev) => !prev)}\n className=\"border-input bg-background hover:bg-accent hover:text-accent-foreground inline-flex h-10 w-10 items-center justify-center rounded-md border\"\n title={sortDesc ? \"Sort A-Z\" : \"Sort Z-A\"}\n >\n {sortDesc ? (\n <ArrowDownZA className=\"h-4 w-4\" />\n ) : (\n <ArrowUpAZ className=\"h-4 w-4\" />\n )}\n </button>\n </div>\n\n {/* Products grid */}\n {products.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-16\">\n <p className=\"text-muted-foreground text-sm\">\n {debouncedSearch\n ? \"No products match your search.\"\n : \"No products available.\"}\n </p>\n </div>\n ) : (\n <div className={GRID_CLASS}>\n {products.map((product) => (\n <ShareProductCard\n key={product.id}\n product={{\n id: product.id,\n title: product.title ?? \"\",\n image_url: product.image_url,\n images: product.images as\n | Array<{ image_url: string; position?: number }>\n | undefined,\n }}\n />\n ))}\n </div>\n )}\n\n {/* Loading more indicator */}\n {isFetchingNextPage && (\n <div className=\"flex justify-center py-4\">\n <div className=\"border-primary h-6 w-6 animate-spin rounded-full border-2 border-t-transparent\" />\n </div>\n )}\n\n {/* Intersection observer sentinel */}\n <div ref={observerTarget} className=\"h-1\" />\n </div>\n );\n}\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { Button, Badge } from \"@fluid-app/ui-primitives\";\nimport { useRenderImage } from \"../../context\";\nimport { Maximize, X } from \"lucide-react\";\n\ninterface SharePageImageDisplayProps {\n displayImage: string;\n displayTitle: string;\n displayVideo?: string;\n isVideo: boolean;\n badgeLabel: string;\n}\n\nexport default function SharePageImageDisplay({\n displayImage,\n displayTitle,\n displayVideo,\n isVideo,\n badgeLabel,\n}: SharePageImageDisplayProps) {\n const [isPreviewOpen, setIsPreviewOpen] = useState(false);\n const renderImage = useRenderImage();\n\n return (\n <div className=\"relative h-full w-full overflow-hidden rounded-2xl bg-gray-100\">\n {isVideo ? (\n <video\n src={displayVideo || \"\"}\n className=\"h-full w-full object-cover\"\n autoPlay\n muted\n loop\n controls\n />\n ) : (\n renderImage({\n src: displayImage,\n alt: displayTitle,\n fill: true,\n className: \"object-cover\",\n })\n )}\n\n {/* Badge in top-right corner */}\n <Badge\n variant=\"secondary\"\n className=\"absolute top-3 right-3 z-10 bg-gray-100 text-gray-900 hover:bg-gray-100\"\n >\n {badgeLabel}\n </Badge>\n\n {/* Expand button for videos */}\n {isVideo && (\n <Button\n onClick={() => setIsPreviewOpen(true)}\n className=\"absolute top-3 left-3 z-10 flex h-8 w-8 items-center justify-center rounded-full bg-white/90 text-gray-700 backdrop-blur-sm transition-colors hover:bg-white\"\n variant=\"ghost\"\n size=\"sm\"\n >\n <Maximize className=\"h-4 w-4\" />\n </Button>\n )}\n\n {/* Fullscreen Video Preview */}\n {isPreviewOpen && isVideo && (\n <div\n className=\"fixed inset-0 z-9999 flex items-center justify-center bg-black\"\n onClick={() => setIsPreviewOpen(false)}\n >\n <Button\n onClick={() => setIsPreviewOpen(false)}\n className=\"absolute top-4 right-4 z-10000 flex h-10 w-10 items-center justify-center rounded-full bg-white/90 text-gray-900 backdrop-blur-sm transition-colors hover:bg-white\"\n variant=\"ghost\"\n size=\"sm\"\n >\n <X className=\"h-5 w-5\" />\n </Button>\n <video\n src={displayVideo || \"\"}\n className=\"max-h-screen max-w-full\"\n autoPlay\n controls\n onClick={(e) => e.stopPropagation()}\n />\n </div>\n )}\n </div>\n );\n}\n","import { useMemo } from \"react\";\n\ninterface QrCodeDisplayProps {\n url: string | null | undefined;\n alt?: string;\n size?: \"sm\" | \"md\" | \"lg\";\n className?: string;\n}\n\nexport function QrCodeDisplay({\n url,\n alt = \"QR Code\",\n size = \"md\",\n className = \"\",\n}: QrCodeDisplayProps) {\n const qrCodeUrl = useMemo(\n () =>\n url\n ? `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(url)}`\n : \"\",\n [url],\n );\n\n const sizeClasses = {\n sm: \"h-16 w-16\",\n md: \"h-[120px] w-[120px]\",\n lg: \"h-40 w-40\",\n };\n\n const iconSizes = {\n sm: \"h-4 w-4\",\n md: \"h-8 w-8\",\n lg: \"h-12 w-12\",\n };\n\n return (\n <div\n className={`flex ${sizeClasses[size]} items-center justify-center overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm ${className}`}\n >\n {qrCodeUrl ? (\n <img\n src={qrCodeUrl}\n alt={alt}\n className=\"h-full w-full object-cover\"\n loading=\"lazy\"\n onError={(e) => {\n e.currentTarget.style.display = \"none\";\n const parent = e.currentTarget.parentElement;\n if (parent) parent.title = \"QR code failed to load\";\n }}\n />\n ) : (\n <div className=\"flex items-center justify-center text-gray-400\">\n <svg\n className={iconSizes[size]}\n fill=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path d=\"M3 3h6v6H3V3zm8 0h6v6h-6V3zm8 0h2v2h-2V3zm0 4h2v2h-2V7zM3 11h6v6H3v-6zm8 0h6v6h-6v-6zm8 0h2v2h-2v-2zm0 4h2v2h-2v-2zM3 19h6v2H3v-2zm8 0h6v2h-6v-2zm8 0h2v2h-2v-2z\" />\n </svg>\n </div>\n )}\n </div>\n );\n}\n","import type { SVGProps } from \"react\";\n\ntype IconProps = SVGProps<SVGSVGElement>;\n\nexport function FacebookIcon(props: IconProps) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n {...props}\n >\n <path d=\"M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z\" />\n </svg>\n );\n}\n\nexport function InstagramIcon(props: IconProps) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n {...props}\n >\n <path d=\"M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z\" />\n </svg>\n );\n}\n\nexport function TikTokIcon(props: IconProps) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n {...props}\n >\n <path d=\"M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z\" />\n </svg>\n );\n}\n\nexport function XTwitterIcon(props: IconProps) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n {...props}\n >\n <path d=\"M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z\" />\n </svg>\n );\n}\n","\"use client\";\n\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"@fluid-app/ui-primitives\";\nimport { QrCodeDisplay } from \"../QrCodeDisplay\";\nimport { Menu, Copy } from \"lucide-react\";\nimport {\n FacebookIcon,\n InstagramIcon,\n TikTokIcon,\n XTwitterIcon,\n} from \"../icons/social-icons\";\nimport { handleSocialShare } from \"../../utils/social-sharing\";\nimport { isMobileDevice } from \"../../utils/os-detection\";\nimport { useShareablesUI } from \"../../context\";\n\ninterface ShareLinkSectionProps {\n shareLink: string | null;\n loading: boolean;\n displayTitle: string;\n isVideo: boolean;\n relateableId: number;\n relateableType: \"Product\" | \"Medium\" | \"Page\" | \"EnrollmentPack\" | \"Library\";\n}\n\nexport default function ShareLinkSection({\n shareLink,\n loading,\n displayTitle,\n isVideo,\n relateableId,\n relateableType,\n}: ShareLinkSectionProps) {\n const { showToast, onMySiteShare } = useShareablesUI();\n\n const handleCopyLink = async () => {\n if (shareLink) {\n try {\n await navigator.clipboard.writeText(shareLink);\n showToast({\n title: \"Share link copied to clipboard!\",\n type: \"success\",\n });\n } catch (error) {\n showToast({\n title: \"Failed to copy link to clipboard\",\n type: \"error\",\n error,\n });\n }\n }\n };\n\n const handlePlatformShare = async (platform: string) => {\n if (!shareLink) {\n showToast({\n title: \"Share link not available yet. Please wait...\",\n type: \"error\",\n });\n return;\n }\n await handleSocialShare(\n platform.toLowerCase() as\n | \"facebook\"\n | \"x\"\n | \"linkedin\"\n | \"instagram\"\n | \"tiktok\",\n {\n url: shareLink,\n title: displayTitle,\n description: `Check out this ${isVideo ? \"video\" : \"image\"}: ${displayTitle}`,\n },\n (message) => showToast({ title: message, type: \"success\" }),\n (message) => showToast({ title: message, type: \"error\" }),\n );\n };\n\n const handleMySiteShareClick = async () => {\n if (!shareLink) {\n showToast({\n title: \"Share link not available yet. Please wait...\",\n type: \"error\",\n });\n return;\n }\n\n if (onMySiteShare) {\n try {\n await onMySiteShare({\n shareLink,\n relateable_id: relateableId,\n relateable_type: relateableType,\n });\n } catch (error) {\n showToast({\n title: \"Failed to share to MySite\",\n type: \"error\",\n error,\n });\n }\n }\n };\n\n const socialPlatforms = [\n { name: \"Facebook\", Icon: FacebookIcon },\n { name: \"Instagram\", Icon: InstagramIcon },\n { name: \"TikTok\", Icon: TikTokIcon },\n { name: \"X\", Icon: XTwitterIcon },\n ];\n\n return (\n <div className=\"space-y-4\">\n {/* Header with divider lines */}\n <div className=\"flex items-center gap-3\">\n <h3 className=\"text-foreground shrink-0 text-sm font-semibold\">\n Share Your Unique Link\n </h3>\n <div className=\"bg-foreground h-px flex-1\" />\n </div>\n\n <div className=\"flex items-start gap-4\">\n {/* QR Code */}\n <div className=\"bg-muted shrink-0 rounded-md p-2\">\n <QrCodeDisplay\n url={shareLink}\n alt=\"QR Code for share link\"\n size=\"sm\"\n className=\"h-[75px] w-[75px] rounded-none! border-none\"\n />\n </div>\n\n <div className=\"flex min-w-0 flex-1 flex-col gap-3\">\n {/* Social buttons */}\n <div className=\"scrollbar-none flex flex-nowrap items-center gap-2 overflow-x-auto\">\n <Button\n onClick={handleMySiteShareClick}\n variant=\"secondary\"\n size=\"sm\"\n className=\"border-foreground bg-background text-foreground hover:bg-muted h-8 rounded-md border px-3 text-xs font-semibold transition-colors\"\n >\n MySite\n </Button>\n {isMobileDevice() ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n className=\"border-foreground bg-background text-foreground hover:bg-muted h-8 rounded-md border px-3 text-xs font-semibold transition-colors\"\n >\n <Menu className=\"mr-1 h-3 w-3\" />\n Share\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"w-48\">\n {socialPlatforms.map((platform) => (\n <DropdownMenuItem\n key={platform.name}\n onClick={() => handlePlatformShare(platform.name)}\n className=\"flex cursor-pointer items-center gap-2\"\n >\n <platform.Icon className=\"h-4 w-4\" />\n {platform.name}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n ) : (\n socialPlatforms.map((platform) => (\n <Button\n key={platform.name}\n onClick={() => handlePlatformShare(platform.name)}\n variant=\"secondary\"\n size=\"sm\"\n className=\"border-foreground bg-background text-foreground hover:bg-muted h-8 rounded-md border px-3 text-xs font-semibold transition-colors\"\n >\n <platform.Icon className=\"mr-1.5 h-3 w-3\" />\n {platform.name}\n </Button>\n ))\n )}\n </div>\n\n {/* Gradient URL bar */}\n <div\n className=\"flex h-10 items-center gap-2 rounded-lg px-3\"\n style={{\n background:\n \"linear-gradient(81.27deg, #E5F7FF 0.95%, #FFDBF5 34.5%, #FFE0CF 60.44%, #EDFFFB 75.06%, #FFDBF5 99.76%)\",\n }}\n >\n <span className=\"flex-1 truncate text-sm text-[#344054]\">\n {shareLink || (loading ? \"Generating link...\" : \"Loading...\")}\n </span>\n <Button\n onClick={handleCopyLink}\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-6 w-6 shrink-0 p-0 hover:bg-transparent\"\n disabled={!shareLink}\n >\n <Copy className=\"h-4 w-4 text-[#344054]\" />\n </Button>\n </div>\n </div>\n </div>\n </div>\n );\n}\n","import React, { useCallback } from \"react\";\nimport { Spinner, Button } from \"@fluid-app/ui-primitives\";\nimport { useRenderImage } from \"../../context\";\nimport { Image, Play, FileImage } from \"lucide-react\";\n\nconst PLACEHOLDER_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/placeholder.png\";\n\nconst FILTER_TABS = [\"All\", \"Images\", \"Videos\"];\n\n// Sub-components\n\ninterface FilterTabsProps {\n tabs: string[];\n activeTab: string;\n onTabChange: (tab: string) => void;\n}\n\nconst FilterTabs = React.memo(function FilterTabs({\n tabs,\n activeTab,\n onTabChange,\n}: FilterTabsProps) {\n return (\n <div className=\"mb-3 flex gap-2\">\n {tabs.map((tab) => (\n <Button\n key={tab}\n onClick={() => onTabChange(tab)}\n variant=\"secondary\"\n size=\"sm\"\n className={`inline-flex items-center gap-x-1.5 rounded-sm border-none px-2 py-1 text-xs font-medium capitalize shadow-none! transition-colors ${\n activeTab === tab\n ? \"bg-primary text-primary-foreground ring-primary hover:bg-primary hover:text-primary-foreground hover:ring-primary\"\n : \"bg-muted text-foreground hover:bg-primary hover:text-primary-foreground hover:ring-primary\"\n }`}\n >\n {tab}\n </Button>\n ))}\n </div>\n );\n});\n\ninterface MediaCardProps {\n mediaItem: MediaItem;\n onClick: (mediaItem: MediaItem) => void;\n}\n\nconst MediaCard = React.memo(function MediaCard({\n mediaItem,\n onClick,\n}: MediaCardProps) {\n const renderImage = useRenderImage();\n const isVideo = mediaItem.kind === \"video\";\n const isImage = mediaItem.kind === \"image\";\n\n const MediaIcon = isVideo ? Play : isImage ? FileImage : Image;\n\n return (\n <button\n onClick={() => onClick(mediaItem)}\n className=\"group flex w-full flex-col gap-2 text-left transition-all\"\n >\n {/* Media Image */}\n <div className=\"bg-background relative aspect-[3/4] w-full overflow-hidden rounded-lg\">\n {renderImage({\n src: mediaItem.image_url || PLACEHOLDER_IMAGE,\n alt: mediaItem.title || \"Media\",\n fill: true,\n className: \"object-cover\",\n })}\n\n {/* Media Type Icon Badge in top-right corner */}\n <div className=\"absolute top-2 right-2\">\n <div className=\"bg-background/90 flex h-8 w-8 items-center justify-center rounded-full shadow-md backdrop-blur-sm\">\n <MediaIcon className=\"h-4 w-4 text-gray-900\" />\n </div>\n </div>\n </div>\n\n {/* Title */}\n <h3 className=\"text-foreground line-clamp-2 px-1 text-[15px] leading-[1.4] font-semibold\">\n {mediaItem.title || \"Untitled\"}\n </h3>\n </button>\n );\n});\n\ninterface LoadingStateProps {\n message?: string;\n}\n\nconst LoadingState = React.memo(function LoadingState({\n message = \"Loading media...\",\n}: LoadingStateProps) {\n return (\n <div className=\"flex flex-col items-center justify-center gap-3 py-12\">\n <Spinner className=\"size-8\" />\n <div className=\"text-muted-foreground text-sm\">{message}</div>\n </div>\n );\n});\n\ninterface ErrorStateProps {\n message?: string;\n}\n\nconst ErrorState = React.memo(function ErrorState({\n message = \"Failed to load media\",\n}: ErrorStateProps) {\n return (\n <div className=\"flex items-center justify-center\">\n <div className=\"text-sm text-red-500\">{message}</div>\n </div>\n );\n});\n\ninterface EmptyStateProps {\n message: string;\n icon?: React.ReactNode;\n description?: string;\n}\n\nconst EmptyState = React.memo(function EmptyState({\n message,\n icon,\n description,\n}: EmptyStateProps) {\n return (\n <div className=\"flex items-center justify-center py-8\">\n {icon && <div className=\"mb-2 text-gray-400\">{icon}</div>}\n <div className=\"text-center\">\n <div className=\"text-sm text-gray-500\">{message}</div>\n {description && (\n <div className=\"text-xs text-gray-400\">{description}</div>\n )}\n </div>\n </div>\n );\n});\n\nconst AnalyticsPlaceholder = React.memo(function AnalyticsPlaceholder() {\n return (\n <div className=\"flex flex-1 items-center justify-center\">\n <div className=\"text-center\">\n <div className=\"mb-1 text-sm text-gray-500\">Analytics Coming Soon</div>\n <div className=\"text-xs text-gray-400\">\n View engagement metrics and performance data\n </div>\n </div>\n </div>\n );\n});\n\nconst RELATEABLE_TYPE_LABELS: Record<string, string> = {\n Product: \"product\",\n Medium: \"media\",\n EnrollmentPack: \"enrollment pack\",\n MySite: \"site\",\n Library: \"library\",\n};\n\nconst NoAssetsState = React.memo(function NoAssetsState({\n relateable_type,\n}: {\n relateable_type: string;\n}) {\n const label = RELATEABLE_TYPE_LABELS[relateable_type] ?? \"item\";\n return (\n <div className=\"flex flex-1 items-center justify-center px-6\">\n <div className=\"text-center\">\n <div className=\"mb-2 text-gray-400\">\n <Image className=\"mx-auto h-12 w-12\" />\n </div>\n <div className=\"mb-1 text-sm text-gray-500\">No Marketing Assets</div>\n <div className=\"text-xs text-gray-400\">\n This {label} does not have any associated media\n </div>\n </div>\n </div>\n );\n});\n\nexport interface MediaItem {\n id: number;\n title: string;\n image_url?: string | null;\n kind: string;\n video_url?: string | null;\n media_format?: string | null;\n}\n\ninterface MarketingAssetsGridProps {\n isLoading: boolean;\n error: Error | null;\n activeTab: string;\n onTabChange: (tab: string) => void;\n mediaItems: MediaItem[];\n onMediaItemClick: (mediaItem: MediaItem) => void;\n showEmptyState?: boolean;\n activeMainTab?: string;\n relateable_type:\n | \"Product\"\n | \"Medium\"\n | \"EnrollmentPack\"\n | \"MySite\"\n | \"Library\";\n}\n\nfunction MarketingAssetsGrid({\n isLoading,\n error,\n activeTab,\n onTabChange,\n mediaItems,\n onMediaItemClick,\n showEmptyState = false,\n activeMainTab = \"Related Sharables\",\n relateable_type,\n}: MarketingAssetsGridProps) {\n const handleMediaItemClick = useCallback(\n (mediaItem: MediaItem) => {\n onMediaItemClick(mediaItem);\n },\n [onMediaItemClick],\n );\n\n // Render content based on current state\n const renderContent = () => {\n if (showEmptyState) {\n return <NoAssetsState relateable_type={relateable_type} />;\n }\n if (activeMainTab === \"Analytics\" && relateable_type === \"Medium\") {\n return <AnalyticsPlaceholder />;\n }\n\n if (activeMainTab === \"Related Sharables\") {\n return (\n <>\n {/* Header */}\n <div className=\"mb-4 px-4 pt-4\">\n <h2 className=\"text-foreground text-[15px] leading-[1.4] font-semibold\">\n Related Sharables ({mediaItems.length})\n </h2>\n </div>\n\n {/* Content */}\n <div className=\"px-4 pb-4\">\n <FilterTabs\n tabs={FILTER_TABS}\n activeTab={activeTab}\n onTabChange={onTabChange}\n />\n\n {isLoading && <LoadingState />}\n {error && <ErrorState />}\n\n {!isLoading && !error && mediaItems.length > 0 && (\n <div className=\"grid grid-cols-2 gap-4 xl:grid-cols-3\">\n {mediaItems.map((mediaItem: MediaItem) => (\n <MediaCard\n key={mediaItem.id}\n mediaItem={mediaItem}\n onClick={handleMediaItemClick}\n />\n ))}\n </div>\n )}\n\n {!isLoading && !error && mediaItems.length === 0 && (\n <EmptyState message={`No ${activeTab.toLowerCase()} found`} />\n )}\n </div>\n </>\n );\n }\n\n return null;\n };\n\n return (\n <div\n className=\"hide-scrollbar bg-background flex min-h-0 flex-1 flex-col overflow-y-auto\"\n style={{ scrollbarWidth: \"none\", msOverflowStyle: \"none\" }}\n >\n {/* MainTabs hidden */}\n {renderContent()}\n </div>\n );\n}\n\n// Memoize the entire component to prevent unnecessary re-renders\nexport default React.memo(MarketingAssetsGrid);\n","\"use client\";\n\nimport { useState, useCallback, useMemo } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { getProduct } from \"@fluid-app/products-api-client\";\nimport { productMedia } from \"@fluid-app/shareables-api-client\";\nimport { useShareablesClient, useShareLink } from \"@fluid-app/shareables-core\";\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n Button,\n Separator,\n Spinner,\n} from \"@fluid-app/ui-primitives\";\nimport { Download } from \"lucide-react\";\nimport { useScreenHeaderBreadcrumbs } from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport SharePageImageDisplay from \"../SharePage/SharePageImageDisplay\";\nimport ShareLinkSection from \"../SharePage/ShareLinkSection\";\nimport MarketingAssetsGrid from \"../ShareablesModal/MarketingAssetsGrid\";\nimport type { MediaItem } from \"../ShareablesModal/MarketingAssetsGrid\";\nimport { useShareablesUI } from \"../../context\";\nimport { stripTags } from \"../../utils/strip-tags\";\n\nexport interface ProductDetailScreenProps {\n productId: string;\n countryCode?: string;\n onNavigate?: (screen: string, detailId?: string) => void;\n onBack?: () => void;\n}\n\nexport function ProductDetailScreen({\n productId,\n countryCode,\n onNavigate,\n onBack,\n}: ProductDetailScreenProps) {\n const client = useShareablesClient();\n const { navigate, showToast, onFileDownload } = useShareablesUI();\n const [activeTab, setActiveTab] = useState(\"All\");\n const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false);\n\n // Fetch product\n const { data: productResponse, isLoading: isLoadingProduct } = useQuery({\n queryKey: [\"product\", productId],\n queryFn: () => getProduct(client, productId, { country_code: countryCode }),\n });\n\n // Fetch product media\n const {\n data: productMediaResponse,\n isLoading: isLoadingMedia,\n error: mediaError,\n } = useQuery({\n queryKey: [\"media\", \"public\", productId],\n queryFn: () => productMedia.getProductMedia(client, Number(productId)),\n });\n\n // Share link\n const {\n shareLink,\n loading: shareLinkLoading,\n error: shareLinkError,\n } = useShareLink({ id: Number(productId) }, \"Product\");\n\n const product = productResponse?.product;\n\n // Derive display values\n const displayTitle = product?.title || \"\";\n const displayImage = product?.image_url || \"\";\n const displayDescription = product?.description || product?.stripped || \"\";\n\n const headerBreadcrumbs = useMemo(\n () => (\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbLink\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n (onBack ?? (() => navigate(\"products\")))();\n }}\n >\n Products\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {displayTitle || \"Product\"}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n ),\n [displayTitle, onBack, navigate],\n );\n useScreenHeaderBreadcrumbs(headerBreadcrumbs);\n const strippedDescription = stripTags(displayDescription);\n const shouldShowReadMore = strippedDescription.length > 150;\n const displayPrice =\n product?.display_price ||\n (product?.price ? `$${product.price}` : undefined);\n\n // Filter media items\n const filteredMediaItems: MediaItem[] = (productMediaResponse?.media ?? [])\n .filter((item: { kind: string | null }) => {\n if (activeTab === \"All\") return true;\n if (activeTab === \"Images\") return item.kind === \"image\";\n if (activeTab === \"Videos\") return item.kind === \"video\";\n return true;\n })\n .map(\n (item: {\n id: number;\n title: string;\n image_url?: string | null;\n kind: string | null;\n video_url?: string | null;\n media_format?: string | null;\n }) => ({\n id: item.id,\n title: item.title,\n image_url: item.image_url,\n kind: item.kind || \"\",\n video_url: item.video_url,\n media_format: item.media_format,\n }),\n );\n\n const hasMedia = (productMediaResponse?.media?.length || 0) > 0;\n\n // Download handler\n const handleDownload = useCallback(() => {\n if (!displayImage) {\n showToast({\n title: \"No downloadable asset available\",\n type: \"error\",\n });\n return;\n }\n if (onFileDownload) {\n onFileDownload(displayImage, displayTitle || \"product\");\n } else {\n window.open(displayImage, \"_blank\");\n }\n }, [displayImage, displayTitle, onFileDownload, showToast]);\n\n // Media item click\n const handleMediaItemClick = useCallback(\n (mediaItem: MediaItem) => {\n onNavigate?.(\"media\", String(mediaItem.id));\n },\n [onNavigate],\n );\n\n // Loading state\n if (isLoadingProduct) {\n return (\n <div className=\"flex items-center justify-center py-16\">\n <Spinner className=\"size-8\" />\n </div>\n );\n }\n\n // No data state\n if (!product) {\n return (\n <div className=\"flex flex-col items-center justify-center py-16\">\n <p className=\"text-destructive text-sm\">\n Product not found or failed to load.\n </p>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col gap-4 px-4 py-4 md:px-10 md:py-6\">\n <div className=\"mx-auto flex w-full max-w-480 flex-col gap-6 md:h-[calc(100vh-140px)] md:flex-row\">\n {/* Left Column - Image with badge */}\n <div className=\"aspect-square w-full md:aspect-auto md:h-full\">\n <div className=\"relative h-full overflow-hidden rounded-2xl\">\n <SharePageImageDisplay\n displayImage={displayImage}\n displayTitle={displayTitle}\n isVideo={false}\n badgeLabel=\"Product\"\n />\n </div>\n </div>\n\n {/* Right Column - Details and Related Sharables */}\n <div className=\"flex w-full flex-col md:overflow-y-auto\">\n {/* Details Panel */}\n <div className=\"space-y-4\">\n {/* Title */}\n <h1 className=\"text-foreground text-[26px] leading-[1.2] font-semibold\">\n {displayTitle}\n </h1>\n\n {/* Description */}\n {strippedDescription && (\n <div className=\"text-foreground/70 text-sm leading-relaxed\">\n <div\n className={\n !isDescriptionExpanded && shouldShowReadMore\n ? \"line-clamp-3\"\n : \"\"\n }\n >\n {strippedDescription}\n </div>\n {shouldShowReadMore && (\n <Button\n onClick={() =>\n setIsDescriptionExpanded(!isDescriptionExpanded)\n }\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-foreground hover:text-foreground/80 mt-1 h-auto p-0 text-xs font-normal underline\"\n >\n {isDescriptionExpanded ? \"Read less\" : \"Read more\"}\n </Button>\n )}\n </div>\n )}\n\n {/* Price */}\n {displayPrice && (\n <div className=\"text-foreground flex items-center gap-2 text-sm\">\n <span className=\"font-semibold\">Price</span>\n <span>|</span>\n <span className=\"font-semibold\">{displayPrice}</span>\n </div>\n )}\n\n {/* Download Button */}\n {displayImage && (\n <Button\n onClick={handleDownload}\n className=\"bg-foreground text-background hover:bg-foreground/70 flex h-10 w-full items-center justify-between rounded-lg px-4 transition-all\"\n >\n <span className=\"text-sm font-medium\">Download Asset</span>\n <Download className=\"h-4 w-4\" />\n </Button>\n )}\n\n {/* Share Link Section */}\n <ShareLinkSection\n shareLink={shareLinkError ? null : shareLink || null}\n loading={shareLinkLoading}\n displayTitle={displayTitle}\n isVideo={false}\n relateableId={Number(productId)}\n relateableType=\"Product\"\n />\n </div>\n\n {/* Divider + Marketing Assets */}\n <Separator className=\"border-foreground my-4\" />\n\n <div className=\"hide-scrollbar bg-background h-full overflow-y-auto rounded-lg\">\n <MarketingAssetsGrid\n isLoading={isLoadingMedia}\n error={mediaError as Error | null}\n activeTab={activeTab}\n onTabChange={setActiveTab}\n mediaItems={filteredMediaItems}\n onMediaItemClick={handleMediaItemClick}\n showEmptyState={!hasMedia}\n activeMainTab=\"Related Sharables\"\n relateable_type=\"Product\"\n />\n </div>\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport { useState, useEffect, useRef, useCallback, useMemo } from \"react\";\nimport { useInfiniteQuery } from \"@tanstack/react-query\";\nimport { media } from \"@fluid-app/shareables-api-client\";\nimport {\n useShareablesClient,\n useRepContext,\n shareablesKeys,\n} from \"@fluid-app/shareables-core\";\nimport {\n Breadcrumb,\n BreadcrumbList,\n BreadcrumbItem,\n BreadcrumbPage,\n Button,\n Input,\n Skeleton,\n} from \"@fluid-app/ui-primitives\";\nimport {\n Search,\n ArrowUpAZ,\n ArrowDownZA,\n LayoutGrid,\n List,\n Plus,\n} from \"lucide-react\";\nimport {\n useScreenHeaderActions,\n useScreenHeaderBreadcrumbs,\n} from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport ShareItemCard from \"../MediaShare/ShareItemCard\";\nimport { useShareablesUI } from \"../../context\";\n\nconst PAGE_SIZE = 24;\n\nconst GRID_CLASS =\n \"grid grid-cols-1 gap-8 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-4\";\n\nfunction getMediaKindLabel(kind: string | null): string {\n switch (kind) {\n case \"video\":\n return \"Video\";\n case \"pdf\":\n return \"PDF\";\n default:\n return \"Image\";\n }\n}\n\nexport interface MediaListingScreenProps {\n onNavigate?: (screen: string, detailId?: string) => void;\n}\n\ntype ViewMode = \"grid\" | \"list\";\n\nexport function MediaListingScreen({ onNavigate }: MediaListingScreenProps) {\n const client = useShareablesClient();\n const repContext = useRepContext();\n const { navigate } = useShareablesUI();\n\n const headerActions = useMemo(\n () => (\n <Button onClick={() => navigate(\"media/new\")} size=\"sm\">\n <Plus className=\"mr-1 h-4 w-4\" />\n Add Media\n </Button>\n ),\n [navigate],\n );\n useScreenHeaderActions(headerActions);\n\n const headerBreadcrumbs = useMemo(\n () => (\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">Media</BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n ),\n [],\n );\n useScreenHeaderBreadcrumbs(headerBreadcrumbs);\n\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [debouncedSearch, setDebouncedSearch] = useState(\"\");\n const [sortDesc, setSortDesc] = useState(false);\n const [viewMode, setViewMode] = useState<ViewMode>(\"grid\");\n const observerTarget = useRef<HTMLDivElement>(null);\n\n // Debounce search input\n useEffect(() => {\n const timer = setTimeout(() => {\n setDebouncedSearch(searchTerm);\n }, 300);\n return () => clearTimeout(timer);\n }, [searchTerm]);\n\n const {\n data,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n fetchNextPage,\n error,\n } = useInfiniteQuery({\n queryKey: shareablesKeys.media.list(debouncedSearch, sortDesc, repContext),\n queryFn: async ({ pageParam = 1 }) => {\n const response = await media.getMedia(\n client,\n {\n page: pageParam,\n per_page: PAGE_SIZE,\n search_query: debouncedSearch || undefined,\n sorted_by: sortDesc ? \"title_desc\" : \"title_asc\",\n },\n repContext,\n );\n return response.media;\n },\n getNextPageParam: (lastPage, allPages) =>\n lastPage.length === PAGE_SIZE ? allPages.length + 1 : undefined,\n initialPageParam: 1,\n });\n\n const handleIntersect = useCallback(\n (entries: IntersectionObserverEntry[]) => {\n if (entries[0]?.isIntersecting && hasNextPage && !isFetchingNextPage) {\n fetchNextPage();\n }\n },\n [hasNextPage, isFetchingNextPage, fetchNextPage],\n );\n\n useEffect(() => {\n const target = observerTarget.current;\n if (!target) return;\n\n const observer = new IntersectionObserver(handleIntersect, {\n threshold: 0.1,\n rootMargin: \"200px\",\n });\n observer.observe(target);\n return () => observer.disconnect();\n }, [handleIntersect]);\n\n const mediaItems = data?.pages.flat() ?? [];\n\n // Loading skeleton\n if (isLoading) {\n return (\n <div className=\"space-y-6 px-4 py-4 md:px-10 md:py-6\">\n <div className=\"flex items-center gap-3\">\n <Skeleton className=\"h-10 flex-1\" />\n <Skeleton className=\"h-10 w-10\" />\n </div>\n <div className={GRID_CLASS}>\n {Array.from({ length: 8 }).map((_, i) => (\n <div key={i} className=\"space-y-2\">\n <Skeleton className=\"aspect-square w-full rounded-lg\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-3 w-1/2\" />\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex flex-col items-center justify-center py-16\">\n <p className=\"text-destructive text-sm\">\n Failed to load media. Please try again.\n </p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 px-4 py-4 md:px-10 md:py-6\">\n {/* Search, sort, and view toggle */}\n <div className=\"flex items-center gap-3\">\n <div className=\"relative flex-1\">\n <Search className=\"text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2\" />\n <Input\n placeholder=\"Search media...\"\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n className=\"pl-9\"\n />\n </div>\n <button\n type=\"button\"\n onClick={() => setSortDesc((prev) => !prev)}\n className=\"border-input bg-background hover:bg-accent hover:text-accent-foreground inline-flex h-10 w-10 items-center justify-center rounded-md border\"\n title={sortDesc ? \"Sort A-Z\" : \"Sort Z-A\"}\n >\n {sortDesc ? (\n <ArrowDownZA className=\"h-4 w-4\" />\n ) : (\n <ArrowUpAZ className=\"h-4 w-4\" />\n )}\n </button>\n\n {/* View toggle */}\n <div className=\"border-input bg-muted flex items-center rounded-lg border p-0.5\">\n <button\n type=\"button\"\n onClick={() => setViewMode(\"list\")}\n className={`flex h-8 w-8 items-center justify-center rounded-md transition-all ${\n viewMode === \"list\"\n ? \"bg-background text-foreground shadow-sm\"\n : \"text-muted-foreground hover:text-foreground\"\n }`}\n title=\"List view\"\n >\n <List className=\"h-4 w-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode(\"grid\")}\n className={`flex h-8 w-8 items-center justify-center rounded-md transition-all ${\n viewMode === \"grid\"\n ? \"bg-background text-foreground shadow-sm\"\n : \"text-muted-foreground hover:text-foreground\"\n }`}\n title=\"Grid view\"\n >\n <LayoutGrid className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n\n {/* Media content */}\n {mediaItems.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-16\">\n <p className=\"text-muted-foreground text-sm\">\n {debouncedSearch\n ? \"No media match your search.\"\n : \"No media available.\"}\n </p>\n </div>\n ) : viewMode === \"grid\" ? (\n <div className={GRID_CLASS}>\n {mediaItems.map((item) => (\n <ShareItemCard\n key={item.id}\n title={item.title ?? \"\"}\n imageUrl={item.image_url}\n href={`media/${item.id}`}\n badge={{ text: getMediaKindLabel(item.kind) }}\n isVideo={item.kind === \"video\"}\n subtitle=\"Click to view media\"\n />\n ))}\n </div>\n ) : (\n <div className=\"divide-border divide-y rounded-lg border\">\n {mediaItems.map((item) => (\n <button\n key={item.id}\n type=\"button\"\n onClick={() => navigate(`media/${item.id}`)}\n className=\"hover:bg-muted flex w-full items-center gap-4 px-4 py-3 text-left transition-colors\"\n >\n <div className=\"bg-muted h-12 w-12 shrink-0 overflow-hidden rounded-md\">\n {item.image_url ? (\n <img\n src={item.image_url}\n alt={item.title ?? \"\"}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"bg-muted h-full w-full\" />\n )}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-foreground truncate text-sm font-medium\">\n {item.title ?? \"Untitled\"}\n </p>\n <p className=\"text-muted-foreground text-xs\">\n {getMediaKindLabel(item.kind)}\n </p>\n </div>\n </button>\n ))}\n </div>\n )}\n\n {/* Loading more indicator */}\n {isFetchingNextPage && (\n <div className=\"flex justify-center py-4\">\n <div className=\"border-primary h-6 w-6 animate-spin rounded-full border-2 border-t-transparent\" />\n </div>\n )}\n\n {/* Intersection observer sentinel */}\n <div ref={observerTarget} className=\"h-1\" />\n </div>\n );\n}\n","\"use client\";\n\nimport { useState, useCallback, useMemo } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { media } from \"@fluid-app/shareables-api-client\";\nimport {\n useShareablesClient,\n useRepContext,\n useShareLink,\n shareablesKeys,\n} from \"@fluid-app/shareables-core\";\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n Button,\n Spinner,\n} from \"@fluid-app/ui-primitives\";\nimport { Download } from \"lucide-react\";\nimport { useScreenHeaderBreadcrumbs } from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport SharePageImageDisplay from \"../SharePage/SharePageImageDisplay\";\nimport ShareLinkSection from \"../SharePage/ShareLinkSection\";\nimport { useShareablesUI } from \"../../context\";\nimport { stripTags } from \"../../utils/strip-tags\";\n\nexport interface MediaDetailScreenProps {\n mediaId: string;\n onNavigate?: (screen: string, detailId?: string) => void;\n onBack?: () => void;\n}\n\nfunction getBadgeLabel(kind: string | null): string {\n switch (kind) {\n case \"video\":\n return \"Video\";\n case \"image\":\n return \"Image\";\n case \"pdf\":\n return \"PDF\";\n default:\n return \"Media\";\n }\n}\n\nexport function MediaDetailScreen({\n mediaId,\n onNavigate: _onNavigate,\n onBack,\n}: MediaDetailScreenProps) {\n const client = useShareablesClient();\n const repContext = useRepContext();\n const { navigate, showToast, onFileDownload } = useShareablesUI();\n const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false);\n\n // Fetch media\n const { data: mediaResponse, isLoading } = useQuery({\n queryKey: shareablesKeys.media.detail(Number(mediaId), repContext),\n queryFn: () =>\n media.getMediaById(client, Number(mediaId), undefined, repContext),\n });\n\n // Share link — relateableType for media is \"Medium\"\n const {\n shareLink,\n loading: shareLinkLoading,\n error: shareLinkError,\n } = useShareLink({ id: Number(mediaId) }, \"Medium\");\n\n const mediaItem = mediaResponse?.media;\n\n // Derive display values\n const displayTitle = mediaItem?.title || \"\";\n const displayImage = mediaItem?.image_url || \"\";\n const displayVideo = mediaItem?.video_url || \"\";\n const isVideo = mediaItem?.kind === \"video\" && !!displayVideo;\n\n const headerBreadcrumbs = useMemo(\n () => (\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbLink\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n (onBack ?? (() => navigate(\"media\")))();\n }}\n >\n Media\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {displayTitle || \"Media\"}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n ),\n [displayTitle, onBack, navigate],\n );\n useScreenHeaderBreadcrumbs(headerBreadcrumbs);\n const badgeLabel = getBadgeLabel(mediaItem?.kind ?? null);\n\n const rawDescription =\n mediaItem?.description?.body || mediaItem?.stripped || \"\";\n const strippedDescription = stripTags(rawDescription);\n const shouldShowReadMore = strippedDescription.length > 150;\n\n // Download URL: video URL for videos, image URL otherwise\n const downloadUrl = isVideo ? displayVideo : displayImage;\n\n // Download handler\n const handleDownload = useCallback(() => {\n if (!downloadUrl) {\n showToast({\n title: \"No downloadable asset available\",\n type: \"error\",\n });\n return;\n }\n if (onFileDownload) {\n onFileDownload(downloadUrl, displayTitle || \"media\");\n } else {\n window.open(downloadUrl, \"_blank\");\n }\n }, [downloadUrl, displayTitle, onFileDownload, showToast]);\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center py-16\">\n <Spinner className=\"size-8\" />\n </div>\n );\n }\n\n // No data state\n if (!mediaItem) {\n return (\n <div className=\"flex flex-col items-center justify-center py-16\">\n <p className=\"text-destructive text-sm\">\n Media not found or failed to load.\n </p>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col gap-4 px-4 py-4 md:px-10 md:py-6\">\n <div className=\"mx-auto flex w-full max-w-480 flex-col gap-6 md:h-[calc(100vh-140px)] md:flex-row\">\n {/* Left Column - Image/Video with badge */}\n <div className=\"aspect-square w-full md:aspect-auto md:h-full\">\n <div className=\"relative h-full overflow-hidden rounded-2xl\">\n <SharePageImageDisplay\n displayImage={displayImage}\n displayTitle={displayTitle}\n displayVideo={isVideo ? displayVideo : undefined}\n isVideo={isVideo}\n badgeLabel={badgeLabel}\n />\n </div>\n </div>\n\n {/* Right Column - Details */}\n <div className=\"flex w-full flex-col md:overflow-y-auto\">\n <div className=\"space-y-4\">\n {/* Title */}\n <h1 className=\"text-foreground text-[26px] leading-[1.2] font-semibold\">\n {displayTitle}\n </h1>\n\n {/* Description */}\n {strippedDescription && (\n <div className=\"text-foreground/70 text-sm leading-relaxed\">\n <div\n className={\n !isDescriptionExpanded && shouldShowReadMore\n ? \"line-clamp-3\"\n : \"\"\n }\n >\n {strippedDescription}\n </div>\n {shouldShowReadMore && (\n <Button\n onClick={() =>\n setIsDescriptionExpanded(!isDescriptionExpanded)\n }\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-foreground hover:text-foreground/80 mt-1 h-auto p-0 text-xs font-normal underline\"\n >\n {isDescriptionExpanded ? \"Read less\" : \"Read more\"}\n </Button>\n )}\n </div>\n )}\n\n {/* Download Button */}\n {downloadUrl && (\n <Button\n onClick={handleDownload}\n className=\"bg-foreground text-background hover:bg-foreground/70 flex h-10 w-full items-center justify-between rounded-lg px-4 transition-all\"\n >\n <span className=\"text-sm font-medium\">Download Asset</span>\n <Download className=\"h-4 w-4\" />\n </Button>\n )}\n\n {/* Share Link Section */}\n <ShareLinkSection\n shareLink={shareLinkError ? null : shareLink || null}\n loading={shareLinkLoading}\n displayTitle={displayTitle}\n isVideo={isVideo}\n relateableId={Number(mediaId)}\n relateableType=\"Medium\"\n />\n </div>\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { FilePickerClient } from \"@fluid-app/file-picker-api-client\";\n\nexport interface FilePickerToast {\n success(title: string): void;\n error(title: string, error?: unknown): void;\n loading(title: string): string | number;\n dismiss(id: string | number): void;\n}\n\nexport interface FilePickerContextValue {\n apiClient: FilePickerClient;\n companyId?: number;\n userId?: number;\n toast: FilePickerToast;\n unsplashAccessKey?: string;\n shareablesClient?: {\n media: { list: (...args: unknown[]) => Promise<unknown> };\n };\n apiBaseUrl?: string;\n}\n\nconst FilePickerContext = createContext<FilePickerContextValue | null>(null);\n\nexport function FilePickerProvider({\n children,\n value,\n}: {\n children: React.ReactNode;\n value: FilePickerContextValue;\n}): React.JSX.Element {\n return (\n <FilePickerContext.Provider value={value}>\n {children}\n </FilePickerContext.Provider>\n );\n}\n\nexport function useFilePickerContext(): FilePickerContextValue {\n const ctx = useContext(FilePickerContext);\n if (!ctx) {\n throw new Error(\n \"useFilePickerContext must be used within a FilePickerProvider\",\n );\n }\n return ctx;\n}\n","import type React from \"react\";\n\ninterface BrandIconProps {\n className?: string;\n}\n\nexport function GoogleIcon({ className }: BrandIconProps): React.JSX.Element {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z\" />\n <path d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\" />\n <path d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\" />\n <path d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\" />\n </svg>\n );\n}\n\nexport function InstagramIcon({\n className,\n}: BrandIconProps): React.JSX.Element {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 1 0 0 12.324 6.162 6.162 0 0 0 0-12.324zM12 16a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm6.406-11.845a1.44 1.44 0 1 0 0 2.881 1.44 1.44 0 0 0 0-2.881z\" />\n </svg>\n );\n}\n\nexport function FacebookIcon({ className }: BrandIconProps): React.JSX.Element {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z\" />\n </svg>\n );\n}\n\nexport function TiktokIcon({ className }: BrandIconProps): React.JSX.Element {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z\" />\n </svg>\n );\n}\n\nexport function DropboxIcon({ className }: BrandIconProps): React.JSX.Element {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12 6.13L6 10.13l6 4-6 4-6-4 6-4-6-4 6-4 6 4zm0 0l6-4 6 4-6 4 6 4-6 4-6-4 6-4-6-4zm0 16.74l-6-4 6-4 6 4-6 4z\" />\n </svg>\n );\n}\n\nexport function UnsplashIcon({ className }: BrandIconProps): React.JSX.Element {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M7.5 6.75V0h9v6.75h-9zm9 3.75H24V24H0V10.5h7.5v6.75h9V10.5z\" />\n </svg>\n );\n}\n","import { z } from \"zod\";\n\ntype DamVariantApi = {\n id: string;\n url: string | null;\n file_name: string;\n mime_type: string;\n content: any;\n created_at: string;\n updated_at: string;\n default: boolean;\n is_original: boolean;\n is_text: boolean;\n media_type: string;\n processing_status: string;\n tags: string[];\n};\n\nexport const damVariantSchema: z.ZodType<DamVariantApi> = z.object({\n id: z.string(),\n url: z.string().nullable(),\n file_name: z.string(),\n mime_type: z.string(),\n content: z.any().nullable(),\n created_at: z.string(),\n updated_at: z.string(),\n default: z.boolean(),\n is_original: z.boolean(),\n is_text: z.boolean(),\n media_type: z.string(),\n processing_status: z.string(),\n tags: z.array(z.string()),\n});\n\ntype DamAssetApi = {\n id: number;\n canonical_path: string;\n category: string;\n code: string;\n company: string;\n created_at: string;\n default_variant_id: string;\n default_variant_url?: string;\n description: string;\n name: string;\n updated_at: string;\n variants?: DamVariantApi[];\n};\n\nexport const damAssetSchema: z.ZodType<DamAssetApi> = z.object({\n id: z.number(),\n canonical_path: z.string(),\n category: z.string(),\n code: z.string(),\n company: z.string(),\n created_at: z.string(),\n default_variant_id: z.string(),\n default_variant_url: z.string().optional(),\n description: z.string(),\n name: z.string(),\n updated_at: z.string(),\n variants: z.array(damVariantSchema).optional(),\n});\n\ntype DamTreeFolderNode = {\n asset_code?: string | Record<string, unknown>;\n name?: string | Record<string, unknown>;\n category?: string | Record<string, unknown>;\n variants?: unknown[] | Record<string, unknown>;\n [key: string]: unknown;\n};\n\nexport const damTreeFolderNodeSchema: z.ZodType<DamTreeFolderNode> = z\n .object({\n asset_code: z\n .union([z.string(), z.record(z.string(), z.unknown())])\n .optional(),\n name: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),\n category: z\n .union([z.string(), z.record(z.string(), z.unknown())])\n .optional(),\n variants: z\n .union([z.array(z.unknown()), z.record(z.string(), z.unknown())])\n .optional(),\n })\n .passthrough();\n\nexport const damTreeSchema: z.ZodType<Record<string, any>> = z.record(\n z.string(),\n z.union([\n z.lazy(() => damTreeSchema),\n damAssetSchema,\n damTreeFolderNodeSchema,\n ]),\n);\n\ntype DamQueryResponse = {\n path: string;\n tree: Record<string, any>;\n meta?: { next_cursor?: string };\n};\n\nexport const damQueryResponseSchema: z.ZodType<DamQueryResponse> = z.object({\n path: z.string(),\n tree: damTreeSchema,\n meta: z\n .object({\n next_cursor: z.string().optional(),\n })\n .optional(),\n});\n\ntype DamAssetCreateRequest = {\n asset: {\n file: any;\n name: string;\n description?: string;\n tags?: string;\n };\n};\n\nexport const damAssetCreateRequestSchema: z.ZodType<DamAssetCreateRequest> =\n z.object({\n asset: z.object({\n file: z.any(),\n name: z.string(),\n description: z.string().optional(),\n tags: z.string().optional(),\n }),\n });\n\ntype DamAssetCreateResponse = {\n asset: DamAssetApi;\n meta: { request_id: string; timestamp: string };\n};\n\nexport const damAssetCreateResponseSchema: z.ZodType<DamAssetCreateResponse> =\n z.object({\n asset: damAssetSchema,\n meta: z.object({\n request_id: z.string(),\n timestamp: z.string(),\n }),\n });\n\n// Schema for creating asset with placeholder (for ImageKit direct upload)\ntype DamAssetCreateWithPlaceholderRequest = {\n placeholder_asset: {\n mime_type: string;\n name?: string;\n description?: string;\n };\n skip_autotagging?: boolean;\n};\n\nexport const damAssetCreateWithPlaceholderRequestSchema: z.ZodType<DamAssetCreateWithPlaceholderRequest> =\n z.object({\n placeholder_asset: z.object({\n mime_type: z.string(),\n name: z.string().optional(),\n description: z.string().optional(),\n }),\n skip_autotagging: z.boolean().optional(),\n });\n\n// Schema for creating asset path without file upload (legacy)\ntype DamAssetPathCreateRequest = {\n asset?: {\n file: any;\n name: string;\n description?: string;\n tags?: string;\n };\n text_asset?: {\n file_name: string;\n mime_type: string;\n text: string;\n name?: string;\n description?: string;\n tags?: string;\n };\n placeholder_asset?: {\n mime_type: string;\n name?: string;\n description?: string;\n };\n skip_autotagging?: boolean;\n};\n\nexport const damAssetPathCreateRequestSchema: z.ZodType<DamAssetPathCreateRequest> =\n z.object({\n asset: z\n .object({\n file: z.any(),\n name: z.string(),\n description: z.string().optional(),\n tags: z.string().optional(),\n })\n .optional(),\n text_asset: z\n .object({\n file_name: z.string(),\n mime_type: z.string(),\n text: z.string(),\n name: z.string().optional(),\n description: z.string().optional(),\n tags: z.string().optional(),\n })\n .optional(),\n placeholder_asset: z\n .object({\n mime_type: z.string(),\n name: z.string().optional(),\n description: z.string().optional(),\n })\n .optional(),\n skip_autotagging: z.boolean().optional(),\n });\n\ntype DamAssetPathCreateResponse = {\n asset: { id: number; canonical_path: string; name: string };\n meta: { request_id: string; timestamp: string };\n};\n\nexport const damAssetPathCreateResponseSchema: z.ZodType<DamAssetPathCreateResponse> =\n z.object({\n asset: z.object({\n id: z.number(),\n canonical_path: z.string(),\n name: z.string(),\n }),\n meta: z.object({\n request_id: z.string(),\n timestamp: z.string(),\n }),\n });\n\nexport type { DamVariantApi, DamAssetApi };\nexport type DamTreeApi = z.infer<typeof damTreeSchema>;\nexport type { DamQueryResponse };\nexport type { DamAssetCreateRequest };\nexport type { DamAssetCreateResponse };\nexport type { DamAssetCreateWithPlaceholderRequest };\nexport type { DamAssetPathCreateRequest };\nexport type { DamAssetPathCreateResponse };\n","import { z } from \"zod\";\n\ntype FilePickerConfigInput = {\n accept?: string[];\n maxFiles?: number;\n maxSize?: number;\n showProgress?: boolean;\n showVariants?: boolean;\n fileTypeFilter?: string[];\n allowedMethods?: (\n | \"upload\"\n | \"computer\"\n | \"dam\"\n | \"media\"\n | \"url\"\n | \"unsplash\"\n | \"google-drive\"\n | \"instagram\"\n | \"facebook\"\n | \"tiktok\"\n | \"dropbox\"\n )[];\n};\n\ntype FilePickerConfigOutput = {\n accept?: string[];\n maxFiles: number;\n maxSize?: number;\n showProgress: boolean;\n showVariants: boolean;\n fileTypeFilter?: string[];\n allowedMethods?: (\n | \"upload\"\n | \"computer\"\n | \"dam\"\n | \"media\"\n | \"url\"\n | \"unsplash\"\n | \"google-drive\"\n | \"instagram\"\n | \"facebook\"\n | \"tiktok\"\n | \"dropbox\"\n )[];\n};\n\nexport const filePickerConfigSchema: z.ZodType<FilePickerConfigOutput> =\n z.object({\n accept: z.array(z.string()).optional(),\n maxFiles: z.number().min(1).optional().default(1),\n maxSize: z.number().positive().optional(),\n showProgress: z.boolean().optional().default(true),\n showVariants: z.boolean().optional().default(false),\n fileTypeFilter: z.array(z.string()).optional(),\n allowedMethods: z\n .array(\n z.enum([\n \"upload\",\n \"computer\",\n \"dam\",\n \"media\",\n \"url\",\n \"unsplash\",\n \"google-drive\",\n \"instagram\",\n \"facebook\",\n \"tiktok\",\n \"dropbox\",\n ]),\n )\n .optional(),\n });\n\nexport type { FilePickerConfigInput, FilePickerConfigOutput };\n","export const FILTER_VALUE_ALL = \"__all__\";\n\nexport const DAM_TYPE_FILTER_OPTIONS: { value: string; label: string }[] = [\n { value: FILTER_VALUE_ALL, label: \"All types\" },\n { value: \"images\", label: \"Image\" },\n { value: \"videos\", label: \"Video\" },\n { value: \"audio\", label: \"Audio\" },\n { value: \"text\", label: \"Text\" },\n { value: \"documents\", label: \"Document\" },\n { value: \"other\", label: \"Others\" },\n];\n\nexport function getCategoryFromTypeFilter(value: string): string | undefined {\n if (!value) return undefined;\n return value;\n}\n\nexport const DATE_FILTER_OPTIONS: readonly { value: string; label: string }[] =\n [\n { value: FILTER_VALUE_ALL, label: \"Any time\" },\n { value: \"24h\", label: \"Past 24 hours\" },\n { value: \"7d\", label: \"Past 7 days\" },\n { value: \"30d\", label: \"Past 30 days\" },\n { value: \"90d\", label: \"Past 90 days\" },\n ];\n\nexport const UPLOADED_BY_OPTIONS: readonly { value: string; label: string }[] =\n [\n { value: FILTER_VALUE_ALL, label: \"Anyone\" },\n { value: \"me\", label: \"Me\" },\n ];\n\nexport const USAGE_STATUS_OPTIONS: readonly {\n value: string;\n label: string;\n}[] = [\n { value: FILTER_VALUE_ALL, label: \"Any\" },\n { value: \"used\", label: \"Used\" },\n { value: \"unused\", label: \"Unused\" },\n];\n\nexport const SOURCE_OPTIONS: readonly { value: string; label: string }[] = [\n { value: FILTER_VALUE_ALL, label: \"Any\" },\n { value: \"upload\", label: \"Upload\" },\n { value: \"dam\", label: \"DAM\" },\n];\n\n// Need to add \"newest\" and \"oldest\" options later after we have the created_at field in the API\nexport type SortOptionId = \"name_asc\" | \"name_desc\";\n\nexport const SORT_OPTIONS: Array<{\n id: SortOptionId;\n label: string;\n sortBy: string;\n sortDirection: \"asc\" | \"desc\";\n}> = [\n { id: \"name_asc\", label: \"Name A–Z\", sortBy: \"name\", sortDirection: \"asc\" },\n { id: \"name_desc\", label: \"Name Z–A\", sortBy: \"name\", sortDirection: \"desc\" },\n];\n","const EXTENSION_MIME_MAP: Record<string, string> = {\n \".heic\": \"image/heic\",\n \".heics\": \"image/heic-sequence\",\n \".heif\": \"image/heif\",\n};\n\n/**\n * Resolves MIME type from file.type, falling back to extension-based lookup\n * when the browser returns an empty type (e.g. HEIC on Chrome/Firefox).\n */\nexport function getFileMimeType(file: File): string {\n if (file.type) return file.type;\n const ext = file.name.match(/\\.[^.]+$/)?.[0]?.toLowerCase();\n return (ext && EXTENSION_MIME_MAP[ext]) || \"application/octet-stream\";\n}\n\n/**\n * Validates if a file type matches the accepted types\n */\nexport function isFileTypeAccepted(\n mimeType: string,\n fileName: string,\n acceptTypes: string[],\n): boolean {\n if (!acceptTypes || acceptTypes.length === 0) {\n return true; // No restrictions\n }\n\n return acceptTypes.some((acceptType) => {\n // Handle MIME type patterns (e.g., \"image/*\", \"video/*\")\n if (acceptType.includes(\"*\")) {\n const baseType = acceptType.split(\"/\")[0];\n const fileBaseType = mimeType.split(\"/\")[0];\n return baseType === fileBaseType;\n }\n\n // Handle exact MIME type matches (e.g., \"image/jpeg\", \"video/mp4\")\n if (acceptType.includes(\"/\") && !acceptType.startsWith(\".\")) {\n return mimeType === acceptType;\n }\n\n // Handle file extension matches (e.g., \".jpg\", \".mp4\")\n if (acceptType.startsWith(\".\")) {\n const lastDotIndex = fileName.lastIndexOf(\".\");\n if (lastDotIndex === -1) return false;\n const fileExtension = fileName.toLowerCase().substring(lastDotIndex);\n return fileExtension === acceptType.toLowerCase();\n }\n\n return false;\n });\n}\n\n/**\n * Gets a user-friendly description of accepted file types\n */\nexport function getAcceptedTypesDescription(acceptTypes: string[]): string {\n if (!acceptTypes || acceptTypes.length === 0) {\n return \"Any file type\";\n }\n\n const descriptions: string[] = [];\n const seenTypes = new Set<string>();\n\n acceptTypes.forEach((type) => {\n if (seenTypes.has(type)) return;\n seenTypes.add(type);\n\n if (type === \"image/*\") {\n descriptions.push(\"Images\");\n } else if (type === \"video/*\") {\n descriptions.push(\"Videos\");\n } else if (type === \"audio/*\") {\n descriptions.push(\"Audio\");\n } else if (type === \"application/pdf\") {\n descriptions.push(\"PDF files\");\n } else if (type.startsWith(\".\")) {\n descriptions.push(`${type.toUpperCase()} files`);\n } else if (type.includes(\"/\")) {\n descriptions.push(type);\n }\n });\n\n if (descriptions.length === 0) {\n return acceptTypes.join(\", \");\n }\n\n if (descriptions.length === 1) {\n return descriptions[0]!;\n }\n\n if (descriptions.length === 2) {\n return `${descriptions[0]!} and ${descriptions[1]!}`;\n }\n\n return `${descriptions.slice(0, -1).join(\", \")}, and ${descriptions[descriptions.length - 1]!}`;\n}\n","var _ = Object.defineProperty;\nvar $ = (a, h, e) => h in a ? _(a, h, { enumerable: !0, configurable: !0, writable: !0, value: e }) : a[h] = e;\nvar m = (a, h, e) => $(a, typeof h != \"symbol\" ? h + \"\" : h, e);\nimport u, { PureComponent as K, createRef as P } from \"react\";\nconst E = {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n unit: \"px\"\n}, b = (a, h, e) => Math.min(Math.max(a, h), e), H = (...a) => a.filter((h) => h && typeof h == \"string\").join(\" \"), X = (a, h) => a === h || a.width === h.width && a.height === h.height && a.x === h.x && a.y === h.y && a.unit === h.unit;\nfunction B(a, h, e, n) {\n const t = D(a, e, n);\n return a.width && (t.height = t.width / h), a.height && (t.width = t.height * h), t.y + t.height > n && (t.height = n - t.y, t.width = t.height * h), t.x + t.width > e && (t.width = e - t.x, t.height = t.width / h), a.unit === \"%\" ? v(t, e, n) : t;\n}\nfunction L(a, h, e) {\n const n = D(a, h, e);\n return n.x = (h - n.width) / 2, n.y = (e - n.height) / 2, a.unit === \"%\" ? v(n, h, e) : n;\n}\nfunction v(a, h, e) {\n return a.unit === \"%\" ? { ...E, ...a, unit: \"%\" } : {\n unit: \"%\",\n x: a.x ? a.x / h * 100 : 0,\n y: a.y ? a.y / e * 100 : 0,\n width: a.width ? a.width / h * 100 : 0,\n height: a.height ? a.height / e * 100 : 0\n };\n}\nfunction D(a, h, e) {\n return a.unit ? a.unit === \"px\" ? { ...E, ...a, unit: \"px\" } : {\n unit: \"px\",\n x: a.x ? a.x * h / 100 : 0,\n y: a.y ? a.y * e / 100 : 0,\n width: a.width ? a.width * h / 100 : 0,\n height: a.height ? a.height * e / 100 : 0\n } : { ...E, ...a, unit: \"px\" };\n}\nfunction k(a, h, e, n, t, d = 0, r = 0, o = n, w = t) {\n const i = { ...a };\n let s = Math.min(d, n), c = Math.min(r, t), g = Math.min(o, n), p = Math.min(w, t);\n h && (h > 1 ? (s = r ? r * h : s, c = s / h, g = o * h) : (c = d ? d / h : c, s = c * h, p = w / h)), i.y < 0 && (i.height = Math.max(i.height + i.y, c), i.y = 0), i.x < 0 && (i.width = Math.max(i.width + i.x, s), i.x = 0);\n const l = n - (i.x + i.width);\n l < 0 && (i.x = Math.min(i.x, n - s), i.width += l);\n const C = t - (i.y + i.height);\n if (C < 0 && (i.y = Math.min(i.y, t - c), i.height += C), i.width < s && ((e === \"sw\" || e == \"nw\") && (i.x -= s - i.width), i.width = s), i.height < c && ((e === \"nw\" || e == \"ne\") && (i.y -= c - i.height), i.height = c), i.width > g && ((e === \"sw\" || e == \"nw\") && (i.x -= g - i.width), i.width = g), i.height > p && ((e === \"nw\" || e == \"ne\") && (i.y -= p - i.height), i.height = p), h) {\n const y = i.width / i.height;\n if (y < h) {\n const f = Math.max(i.width / h, c);\n (e === \"nw\" || e == \"ne\") && (i.y -= f - i.height), i.height = f;\n } else if (y > h) {\n const f = Math.max(i.height * h, s);\n (e === \"sw\" || e == \"nw\") && (i.x -= f - i.width), i.width = f;\n }\n }\n return i;\n}\nfunction I(a, h, e, n) {\n const t = { ...a };\n return h === \"ArrowLeft\" ? n === \"nw\" ? (t.x -= e, t.y -= e, t.width += e, t.height += e) : n === \"w\" ? (t.x -= e, t.width += e) : n === \"sw\" ? (t.x -= e, t.width += e, t.height += e) : n === \"ne\" ? (t.y += e, t.width -= e, t.height -= e) : n === \"e\" ? t.width -= e : n === \"se\" && (t.width -= e, t.height -= e) : h === \"ArrowRight\" && (n === \"nw\" ? (t.x += e, t.y += e, t.width -= e, t.height -= e) : n === \"w\" ? (t.x += e, t.width -= e) : n === \"sw\" ? (t.x += e, t.width -= e, t.height -= e) : n === \"ne\" ? (t.y -= e, t.width += e, t.height += e) : n === \"e\" ? t.width += e : n === \"se\" && (t.width += e, t.height += e)), h === \"ArrowUp\" ? n === \"nw\" ? (t.x -= e, t.y -= e, t.width += e, t.height += e) : n === \"n\" ? (t.y -= e, t.height += e) : n === \"ne\" ? (t.y -= e, t.width += e, t.height += e) : n === \"sw\" ? (t.x += e, t.width -= e, t.height -= e) : n === \"s\" ? t.height -= e : n === \"se\" && (t.width -= e, t.height -= e) : h === \"ArrowDown\" && (n === \"nw\" ? (t.x += e, t.y += e, t.width -= e, t.height -= e) : n === \"n\" ? (t.y += e, t.height -= e) : n === \"ne\" ? (t.y += e, t.width -= e, t.height -= e) : n === \"sw\" ? (t.x -= e, t.width += e, t.height += e) : n === \"s\" ? t.height += e : n === \"se\" && (t.width += e, t.height += e)), t;\n}\nconst M = { capture: !0, passive: !1 };\nlet N = 0;\nconst x = class x extends K {\n constructor() {\n super(...arguments);\n m(this, \"docMoveBound\", !1);\n m(this, \"mouseDownOnCrop\", !1);\n m(this, \"dragStarted\", !1);\n m(this, \"evData\", {\n startClientX: 0,\n startClientY: 0,\n startCropX: 0,\n startCropY: 0,\n clientX: 0,\n clientY: 0,\n isResize: !0\n });\n m(this, \"componentRef\", P());\n m(this, \"mediaRef\", P());\n m(this, \"resizeObserver\");\n m(this, \"initChangeCalled\", !1);\n m(this, \"instanceId\", `rc-${N++}`);\n m(this, \"state\", {\n cropIsActive: !1,\n newCropIsBeingDrawn: !1\n });\n m(this, \"onCropPointerDown\", (e) => {\n const { crop: n, disabled: t } = this.props, d = this.getBox();\n if (!n)\n return;\n const r = D(n, d.width, d.height);\n if (t)\n return;\n e.cancelable && e.preventDefault(), this.bindDocMove(), this.componentRef.current.focus({ preventScroll: !0 });\n const o = e.target.dataset.ord, w = !!o;\n let i = e.clientX, s = e.clientY, c = r.x, g = r.y;\n if (o) {\n const p = e.clientX - d.x, l = e.clientY - d.y;\n let C = 0, y = 0;\n o === \"ne\" || o == \"e\" ? (C = p - (r.x + r.width), y = l - r.y, c = r.x, g = r.y + r.height) : o === \"se\" || o === \"s\" ? (C = p - (r.x + r.width), y = l - (r.y + r.height), c = r.x, g = r.y) : o === \"sw\" || o == \"w\" ? (C = p - r.x, y = l - (r.y + r.height), c = r.x + r.width, g = r.y) : (o === \"nw\" || o == \"n\") && (C = p - r.x, y = l - r.y, c = r.x + r.width, g = r.y + r.height), i = c + d.x + C, s = g + d.y + y;\n }\n this.evData = {\n startClientX: i,\n startClientY: s,\n startCropX: c,\n startCropY: g,\n clientX: e.clientX,\n clientY: e.clientY,\n isResize: w,\n ord: o\n }, this.mouseDownOnCrop = !0, this.setState({ cropIsActive: !0 });\n });\n m(this, \"onComponentPointerDown\", (e) => {\n const { crop: n, disabled: t, locked: d, keepSelection: r, onChange: o } = this.props, w = this.getBox();\n if (t || d || r && n)\n return;\n e.cancelable && e.preventDefault(), this.bindDocMove(), this.componentRef.current.focus({ preventScroll: !0 });\n const i = e.clientX - w.x, s = e.clientY - w.y, c = {\n unit: \"px\",\n x: i,\n y: s,\n width: 0,\n height: 0\n };\n this.evData = {\n startClientX: e.clientX,\n startClientY: e.clientY,\n startCropX: i,\n startCropY: s,\n clientX: e.clientX,\n clientY: e.clientY,\n isResize: !0\n }, this.mouseDownOnCrop = !0, o(D(c, w.width, w.height), v(c, w.width, w.height)), this.setState({ cropIsActive: !0, newCropIsBeingDrawn: !0 });\n });\n m(this, \"onDocPointerMove\", (e) => {\n const { crop: n, disabled: t, onChange: d, onDragStart: r } = this.props, o = this.getBox();\n if (t || !n || !this.mouseDownOnCrop)\n return;\n e.cancelable && e.preventDefault(), this.dragStarted || (this.dragStarted = !0, r && r(e));\n const { evData: w } = this;\n w.clientX = e.clientX, w.clientY = e.clientY;\n let i;\n w.isResize ? i = this.resizeCrop() : i = this.dragCrop(), X(n, i) || d(\n D(i, o.width, o.height),\n v(i, o.width, o.height)\n );\n });\n m(this, \"onComponentKeyDown\", (e) => {\n const { crop: n, disabled: t, onChange: d, onComplete: r } = this.props;\n if (t)\n return;\n const o = e.key;\n let w = !1;\n if (!n)\n return;\n const i = this.getBox(), s = this.makePixelCrop(i), g = (navigator.platform.match(\"Mac\") ? e.metaKey : e.ctrlKey) ? x.nudgeStepLarge : e.shiftKey ? x.nudgeStepMedium : x.nudgeStep;\n if (o === \"ArrowLeft\" ? (s.x -= g, w = !0) : o === \"ArrowRight\" ? (s.x += g, w = !0) : o === \"ArrowUp\" ? (s.y -= g, w = !0) : o === \"ArrowDown\" && (s.y += g, w = !0), w) {\n e.cancelable && e.preventDefault(), s.x = b(s.x, 0, i.width - s.width), s.y = b(s.y, 0, i.height - s.height);\n const p = D(s, i.width, i.height), l = v(s, i.width, i.height);\n d(p, l), r && r(p, l);\n }\n });\n m(this, \"onHandlerKeyDown\", (e, n) => {\n const {\n aspect: t = 0,\n crop: d,\n disabled: r,\n minWidth: o = 0,\n minHeight: w = 0,\n maxWidth: i,\n maxHeight: s,\n onChange: c,\n onComplete: g\n } = this.props, p = this.getBox();\n if (r || !d)\n return;\n if (e.key === \"ArrowUp\" || e.key === \"ArrowDown\" || e.key === \"ArrowLeft\" || e.key === \"ArrowRight\")\n e.stopPropagation(), e.preventDefault();\n else\n return;\n const C = (navigator.platform.match(\"Mac\") ? e.metaKey : e.ctrlKey) ? x.nudgeStepLarge : e.shiftKey ? x.nudgeStepMedium : x.nudgeStep, y = D(d, p.width, p.height), f = I(y, e.key, C, n), R = k(\n f,\n t,\n n,\n p.width,\n p.height,\n o,\n w,\n i,\n s\n );\n if (!X(d, R)) {\n const Y = v(R, p.width, p.height);\n c(R, Y), g && g(R, Y);\n }\n });\n m(this, \"onDocPointerDone\", (e) => {\n const { crop: n, disabled: t, onComplete: d, onDragEnd: r } = this.props, o = this.getBox();\n this.unbindDocMove(), !(t || !n) && this.mouseDownOnCrop && (this.mouseDownOnCrop = !1, this.dragStarted = !1, r && r(e), d && d(D(n, o.width, o.height), v(n, o.width, o.height)), this.setState({ cropIsActive: !1, newCropIsBeingDrawn: !1 }));\n });\n m(this, \"onDragFocus\", () => {\n var e;\n (e = this.componentRef.current) == null || e.scrollTo(0, 0);\n });\n }\n get document() {\n return document;\n }\n // We unfortunately get the bounding box every time as x+y changes\n // due to scrolling.\n getBox() {\n const e = this.mediaRef.current;\n if (!e)\n return { x: 0, y: 0, width: 0, height: 0 };\n const { x: n, y: t, width: d, height: r } = e.getBoundingClientRect();\n return { x: n, y: t, width: d, height: r };\n }\n componentDidUpdate(e) {\n const { crop: n, onComplete: t } = this.props;\n if (t && !e.crop && n) {\n const { width: d, height: r } = this.getBox();\n d && r && t(D(n, d, r), v(n, d, r));\n }\n }\n componentWillUnmount() {\n this.resizeObserver && this.resizeObserver.disconnect(), this.unbindDocMove();\n }\n bindDocMove() {\n this.docMoveBound || (this.document.addEventListener(\"pointermove\", this.onDocPointerMove, M), this.document.addEventListener(\"pointerup\", this.onDocPointerDone, M), this.document.addEventListener(\"pointercancel\", this.onDocPointerDone, M), this.docMoveBound = !0);\n }\n unbindDocMove() {\n this.docMoveBound && (this.document.removeEventListener(\"pointermove\", this.onDocPointerMove, M), this.document.removeEventListener(\"pointerup\", this.onDocPointerDone, M), this.document.removeEventListener(\"pointercancel\", this.onDocPointerDone, M), this.docMoveBound = !1);\n }\n getCropStyle() {\n const { crop: e } = this.props;\n if (e)\n return {\n top: `${e.y}${e.unit}`,\n left: `${e.x}${e.unit}`,\n width: `${e.width}${e.unit}`,\n height: `${e.height}${e.unit}`\n };\n }\n dragCrop() {\n const { evData: e } = this, n = this.getBox(), t = this.makePixelCrop(n), d = e.clientX - e.startClientX, r = e.clientY - e.startClientY;\n return t.x = b(e.startCropX + d, 0, n.width - t.width), t.y = b(e.startCropY + r, 0, n.height - t.height), t;\n }\n getPointRegion(e, n, t, d) {\n const { evData: r } = this, o = r.clientX - e.x, w = r.clientY - e.y;\n let i;\n d && n ? i = n === \"nw\" || n === \"n\" || n === \"ne\" : i = w < r.startCropY;\n let s;\n return t && n ? s = n === \"nw\" || n === \"w\" || n === \"sw\" : s = o < r.startCropX, s ? i ? \"nw\" : \"sw\" : i ? \"ne\" : \"se\";\n }\n resolveMinDimensions(e, n, t = 0, d = 0) {\n const r = Math.min(t, e.width), o = Math.min(d, e.height);\n return !n || !r && !o ? [r, o] : n > 1 ? r ? [r, r / n] : [o * n, o] : o ? [o * n, o] : [r, r / n];\n }\n resizeCrop() {\n const { evData: e } = this, { aspect: n = 0, maxWidth: t, maxHeight: d } = this.props, r = this.getBox(), [o, w] = this.resolveMinDimensions(r, n, this.props.minWidth, this.props.minHeight);\n let i = this.makePixelCrop(r);\n const s = this.getPointRegion(r, e.ord, o, w), c = e.ord || s;\n let g = e.clientX - e.startClientX, p = e.clientY - e.startClientY;\n (o && c === \"nw\" || c === \"w\" || c === \"sw\") && (g = Math.min(g, -o)), (w && c === \"nw\" || c === \"n\" || c === \"ne\") && (p = Math.min(p, -w));\n const l = {\n unit: \"px\",\n x: 0,\n y: 0,\n width: 0,\n height: 0\n };\n s === \"ne\" ? (l.x = e.startCropX, l.width = g, n ? (l.height = l.width / n, l.y = e.startCropY - l.height) : (l.height = Math.abs(p), l.y = e.startCropY - l.height)) : s === \"se\" ? (l.x = e.startCropX, l.y = e.startCropY, l.width = g, n ? l.height = l.width / n : l.height = p) : s === \"sw\" ? (l.x = e.startCropX + g, l.y = e.startCropY, l.width = Math.abs(g), n ? l.height = l.width / n : l.height = p) : s === \"nw\" && (l.x = e.startCropX + g, l.width = Math.abs(g), n ? (l.height = l.width / n, l.y = e.startCropY - l.height) : (l.height = Math.abs(p), l.y = e.startCropY + p));\n const C = k(\n l,\n n,\n s,\n r.width,\n r.height,\n o,\n w,\n t,\n d\n );\n return n || x.xyOrds.indexOf(c) > -1 ? i = C : x.xOrds.indexOf(c) > -1 ? (i.x = C.x, i.width = C.width) : x.yOrds.indexOf(c) > -1 && (i.y = C.y, i.height = C.height), i.x = b(i.x, 0, r.width - i.width), i.y = b(i.y, 0, r.height - i.height), i;\n }\n renderCropSelection() {\n const {\n ariaLabels: e = x.defaultProps.ariaLabels,\n disabled: n,\n locked: t,\n renderSelectionAddon: d,\n ruleOfThirds: r,\n crop: o\n } = this.props, w = this.getCropStyle();\n if (o)\n return /* @__PURE__ */ u.createElement(\n \"div\",\n {\n style: w,\n className: \"ReactCrop__crop-selection\",\n onPointerDown: this.onCropPointerDown,\n \"aria-label\": e.cropArea,\n tabIndex: 0,\n onKeyDown: this.onComponentKeyDown,\n role: \"group\"\n },\n !n && !t && /* @__PURE__ */ u.createElement(\"div\", { className: \"ReactCrop__drag-elements\", onFocus: this.onDragFocus }, /* @__PURE__ */ u.createElement(\"div\", { className: \"ReactCrop__drag-bar ord-n\", \"data-ord\": \"n\" }), /* @__PURE__ */ u.createElement(\"div\", { className: \"ReactCrop__drag-bar ord-e\", \"data-ord\": \"e\" }), /* @__PURE__ */ u.createElement(\"div\", { className: \"ReactCrop__drag-bar ord-s\", \"data-ord\": \"s\" }), /* @__PURE__ */ u.createElement(\"div\", { className: \"ReactCrop__drag-bar ord-w\", \"data-ord\": \"w\" }), /* @__PURE__ */ u.createElement(\n \"div\",\n {\n className: \"ReactCrop__drag-handle ord-nw\",\n \"data-ord\": \"nw\",\n tabIndex: 0,\n \"aria-label\": e.nwDragHandle,\n onKeyDown: (i) => this.onHandlerKeyDown(i, \"nw\"),\n role: \"button\"\n }\n ), /* @__PURE__ */ u.createElement(\n \"div\",\n {\n className: \"ReactCrop__drag-handle ord-n\",\n \"data-ord\": \"n\",\n tabIndex: 0,\n \"aria-label\": e.nDragHandle,\n onKeyDown: (i) => this.onHandlerKeyDown(i, \"n\"),\n role: \"button\"\n }\n ), /* @__PURE__ */ u.createElement(\n \"div\",\n {\n className: \"ReactCrop__drag-handle ord-ne\",\n \"data-ord\": \"ne\",\n tabIndex: 0,\n \"aria-label\": e.neDragHandle,\n onKeyDown: (i) => this.onHandlerKeyDown(i, \"ne\"),\n role: \"button\"\n }\n ), /* @__PURE__ */ u.createElement(\n \"div\",\n {\n className: \"ReactCrop__drag-handle ord-e\",\n \"data-ord\": \"e\",\n tabIndex: 0,\n \"aria-label\": e.eDragHandle,\n onKeyDown: (i) => this.onHandlerKeyDown(i, \"e\"),\n role: \"button\"\n }\n ), /* @__PURE__ */ u.createElement(\n \"div\",\n {\n className: \"ReactCrop__drag-handle ord-se\",\n \"data-ord\": \"se\",\n tabIndex: 0,\n \"aria-label\": e.seDragHandle,\n onKeyDown: (i) => this.onHandlerKeyDown(i, \"se\"),\n role: \"button\"\n }\n ), /* @__PURE__ */ u.createElement(\n \"div\",\n {\n className: \"ReactCrop__drag-handle ord-s\",\n \"data-ord\": \"s\",\n tabIndex: 0,\n \"aria-label\": e.sDragHandle,\n onKeyDown: (i) => this.onHandlerKeyDown(i, \"s\"),\n role: \"button\"\n }\n ), /* @__PURE__ */ u.createElement(\n \"div\",\n {\n className: \"ReactCrop__drag-handle ord-sw\",\n \"data-ord\": \"sw\",\n tabIndex: 0,\n \"aria-label\": e.swDragHandle,\n onKeyDown: (i) => this.onHandlerKeyDown(i, \"sw\"),\n role: \"button\"\n }\n ), /* @__PURE__ */ u.createElement(\n \"div\",\n {\n className: \"ReactCrop__drag-handle ord-w\",\n \"data-ord\": \"w\",\n tabIndex: 0,\n \"aria-label\": e.wDragHandle,\n onKeyDown: (i) => this.onHandlerKeyDown(i, \"w\"),\n role: \"button\"\n }\n )),\n d && /* @__PURE__ */ u.createElement(\"div\", { className: \"ReactCrop__selection-addon\", onPointerDown: (i) => i.stopPropagation() }, d(this.state)),\n r && /* @__PURE__ */ u.createElement(u.Fragment, null, /* @__PURE__ */ u.createElement(\"div\", { className: \"ReactCrop__rule-of-thirds-hz\" }), /* @__PURE__ */ u.createElement(\"div\", { className: \"ReactCrop__rule-of-thirds-vt\" }))\n );\n }\n makePixelCrop(e) {\n const n = { ...E, ...this.props.crop || {} };\n return D(n, e.width, e.height);\n }\n render() {\n const { aspect: e, children: n, circularCrop: t, className: d, crop: r, disabled: o, locked: w, style: i, ruleOfThirds: s } = this.props, { cropIsActive: c, newCropIsBeingDrawn: g } = this.state, p = r ? this.renderCropSelection() : null, l = H(\n \"ReactCrop\",\n d,\n c && \"ReactCrop--active\",\n o && \"ReactCrop--disabled\",\n w && \"ReactCrop--locked\",\n g && \"ReactCrop--new-crop\",\n r && e && \"ReactCrop--fixed-aspect\",\n r && t && \"ReactCrop--circular-crop\",\n r && s && \"ReactCrop--rule-of-thirds\",\n !this.dragStarted && r && !r.width && !r.height && \"ReactCrop--invisible-crop\",\n t && \"ReactCrop--no-animate\"\n );\n return /* @__PURE__ */ u.createElement(\"div\", { ref: this.componentRef, className: l, style: i }, /* @__PURE__ */ u.createElement(\"div\", { ref: this.mediaRef, className: \"ReactCrop__child-wrapper\", onPointerDown: this.onComponentPointerDown }, n), r ? /* @__PURE__ */ u.createElement(\"svg\", { className: \"ReactCrop__crop-mask\", width: \"100%\", height: \"100%\" }, /* @__PURE__ */ u.createElement(\"defs\", null, /* @__PURE__ */ u.createElement(\"mask\", { id: `hole-${this.instanceId}` }, /* @__PURE__ */ u.createElement(\"rect\", { width: \"100%\", height: \"100%\", fill: \"white\" }), t ? /* @__PURE__ */ u.createElement(\n \"ellipse\",\n {\n cx: `${r.x + r.width / 2}${r.unit}`,\n cy: `${r.y + r.height / 2}${r.unit}`,\n rx: `${r.width / 2}${r.unit}`,\n ry: `${r.height / 2}${r.unit}`,\n fill: \"black\"\n }\n ) : /* @__PURE__ */ u.createElement(\n \"rect\",\n {\n x: `${r.x}${r.unit}`,\n y: `${r.y}${r.unit}`,\n width: `${r.width}${r.unit}`,\n height: `${r.height}${r.unit}`,\n fill: \"black\"\n }\n ))), /* @__PURE__ */ u.createElement(\"rect\", { fill: \"black\", fillOpacity: 0.5, width: \"100%\", height: \"100%\", mask: `url(#hole-${this.instanceId})` })) : void 0, p);\n }\n};\nm(x, \"xOrds\", [\"e\", \"w\"]), m(x, \"yOrds\", [\"n\", \"s\"]), m(x, \"xyOrds\", [\"nw\", \"ne\", \"se\", \"sw\"]), m(x, \"nudgeStep\", 1), m(x, \"nudgeStepMedium\", 10), m(x, \"nudgeStepLarge\", 100), m(x, \"defaultProps\", {\n ariaLabels: {\n cropArea: \"Use the arrow keys to move the crop selection area\",\n nwDragHandle: \"Use the arrow keys to move the north west drag handle to change the crop selection area\",\n nDragHandle: \"Use the up and down arrow keys to move the north drag handle to change the crop selection area\",\n neDragHandle: \"Use the arrow keys to move the north east drag handle to change the crop selection area\",\n eDragHandle: \"Use the up and down arrow keys to move the east drag handle to change the crop selection area\",\n seDragHandle: \"Use the arrow keys to move the south east drag handle to change the crop selection area\",\n sDragHandle: \"Use the up and down arrow keys to move the south drag handle to change the crop selection area\",\n swDragHandle: \"Use the arrow keys to move the south west drag handle to change the crop selection area\",\n wDragHandle: \"Use the up and down arrow keys to move the west drag handle to change the crop selection area\"\n }\n});\nlet S = x;\nexport {\n S as Component,\n S as ReactCrop,\n X as areCropsEqual,\n L as centerCrop,\n b as clamp,\n H as cls,\n k as containCrop,\n v as convertToPercentCrop,\n D as convertToPixelCrop,\n S as default,\n E as defaultCrop,\n B as makeAspectCrop,\n I as nudgeCrop\n};\n","import {\n centerCrop,\n makeAspectCrop,\n type Crop,\n type PixelCrop,\n} from \"react-image-crop\";\n\n// ── Constants ────────────────────────────────────────────────────────────────\n\nexport const ASPECT_OPTIONS: readonly {\n label: string;\n value: \"free\" | number;\n}[] = [\n { label: \"Free\", value: \"free\" as const },\n { label: \"1:1\", value: 1 },\n { label: \"16:9\", value: 16 / 9 },\n { label: \"4:3\", value: 4 / 3 },\n];\n\nexport type AspectValue = \"free\" | number;\n\nexport const ROTATION_MIN = -180;\nexport const ROTATION_MAX = 180;\n\n// ── Crop helpers ─────────────────────────────────────────────────────────────\n\n/**\n * Compute initial crop + completedCrop values for given container dimensions\n * and optional aspect ratio.\n *\n * Used when the image first loads, when the user resets, and when the aspect\n * ratio changes via the dropdown.\n */\nexport function computeInitialCrop(\n displayedWidth: number,\n displayedHeight: number,\n aspect?: number,\n): { crop: Crop; completedCrop: PixelCrop } {\n if (aspect) {\n const imageAspect = displayedWidth / displayedHeight;\n const seed =\n aspect > imageAspect\n ? { unit: \"%\" as const, width: 100 }\n : { unit: \"%\" as const, height: 100 };\n\n const crop = centerCrop(\n makeAspectCrop(seed, aspect, displayedWidth, displayedHeight),\n displayedWidth,\n displayedHeight,\n );\n const completedCrop: PixelCrop = {\n x: (crop.x / 100) * displayedWidth,\n y: (crop.y / 100) * displayedHeight,\n width: (crop.width / 100) * displayedWidth,\n height: (crop.height / 100) * displayedHeight,\n unit: \"px\",\n };\n return { crop, completedCrop };\n }\n\n const crop: Crop = { unit: \"%\", x: 0, y: 0, width: 100, height: 100 };\n const completedCrop: PixelCrop = {\n x: 0,\n y: 0,\n width: displayedWidth,\n height: displayedHeight,\n unit: \"px\",\n };\n return { crop, completedCrop };\n}\n\n// ── Canvas export ────────────────────────────────────────────────────────────\n\n/**\n * Render the image onto a canvas at natural (full) resolution\n * (including rotation, zoom, flip) then extract the crop rectangle as a File.\n *\n * Uses createImageBitmap() from the original file to guarantee full-resolution\n * decoding. The <img> element may have been decoded at reduced resolution by the\n * browser when displayed at a small CSS size (downsample-on-decode).\n */\nexport async function getCroppedImgFromViewport(\n image: HTMLImageElement,\n vw: number,\n vh: number,\n crop: { x: number; y: number; width: number; height: number },\n zoom: number,\n rotationDeg: number,\n flipHorizontal: boolean,\n fileName: string,\n type: string = \"image/jpeg\",\n quality: number = 0.9,\n sourceFile?: File,\n): Promise<File> {\n // Decode the image at full resolution from the original file bytes,\n // bypassing any browser downsample-on-decode optimization on the <img>.\n const bitmap = sourceFile\n ? await createImageBitmap(sourceFile)\n : await createImageBitmap(image);\n\n const nw = bitmap.width;\n const nh = bitmap.height;\n\n // Scale factor from viewport coords to natural-resolution coords.\n // Cap canvas dimensions at MAX_CANVAS_DIM to avoid exceeding browser limits\n // (most browsers cap at 16384, but we use 4096 for safe memory usage).\n const MAX_CANVAS_DIM = 4096;\n const rawUpscale = Math.max(nw / vw, nh / vh);\n const rawW = Math.round(vw * rawUpscale);\n const rawH = Math.round(vh * rawUpscale);\n const downscale = Math.min(1, MAX_CANVAS_DIM / rawW, MAX_CANVAS_DIM / rawH);\n const upscale = rawUpscale * downscale;\n const canvasW = Math.round(rawW * downscale);\n const canvasH = Math.round(rawH * downscale);\n\n const scaleFit = Math.min(canvasW / nw, canvasH / nh);\n const scale = scaleFit * zoom;\n\n const source = document.createElement(\"canvas\");\n source.width = canvasW;\n source.height = canvasH;\n const ctx = source.getContext(\"2d\");\n if (!ctx) throw new Error(\"No 2d context\");\n ctx.imageSmoothingQuality = \"high\";\n ctx.imageSmoothingEnabled = true;\n\n ctx.save();\n ctx.translate(canvasW / 2, canvasH / 2);\n ctx.rotate((rotationDeg * Math.PI) / 180);\n if (flipHorizontal) ctx.scale(-1, 1);\n ctx.scale(scale, scale);\n ctx.translate(-nw / 2, -nh / 2);\n ctx.drawImage(bitmap, 0, 0);\n ctx.restore();\n\n bitmap.close();\n\n // Scale crop coordinates to natural resolution\n const cropX = Math.max(0, Math.floor(crop.x * upscale));\n const cropY = Math.max(0, Math.floor(crop.y * upscale));\n const pw = Math.floor(crop.width * upscale);\n const ph = Math.floor(crop.height * upscale);\n\n const out = document.createElement(\"canvas\");\n out.width = pw;\n out.height = ph;\n const outCtx = out.getContext(\"2d\");\n if (!outCtx) throw new Error(\"No 2d context\");\n outCtx.imageSmoothingQuality = \"high\";\n outCtx.imageSmoothingEnabled = true;\n outCtx.drawImage(source, cropX, cropY, pw, ph, 0, 0, pw, ph);\n\n return new Promise((resolve, reject) => {\n out.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error(\"Canvas is empty\"));\n return;\n }\n const file = new File([blob], fileName, { type });\n resolve(file);\n },\n type,\n quality,\n );\n });\n}\n","import type React from \"react\";\nimport type { DamAsset } from \"../types\";\n\nexport function getAssetCardClassName(isSelected: boolean): string {\n return isSelected ? \"bg-blue-50 ring-2 ring-blue-500\" : \"bg-white\";\n}\n\nexport function createAssetClickHandlers(\n pendingClickTimeoutRef: React.MutableRefObject<ReturnType<\n typeof setTimeout\n > | null>,\n handleAssetClick: (\n e: React.MouseEvent,\n asset: DamAsset,\n index: number,\n ) => void,\n setPreviewAsset: (asset: DamAsset | null) => void,\n asset: DamAsset,\n index: number,\n): {\n onClick: (e: React.MouseEvent) => void;\n onDoubleClick: (e: React.MouseEvent) => void;\n} {\n const delayMs = 250;\n return {\n onClick: (e: React.MouseEvent) => {\n if (pendingClickTimeoutRef.current) {\n clearTimeout(pendingClickTimeoutRef.current);\n }\n pendingClickTimeoutRef.current = setTimeout(() => {\n handleAssetClick(e, asset, index);\n pendingClickTimeoutRef.current = null;\n }, delayMs);\n },\n onDoubleClick: (e: React.MouseEvent) => {\n e.stopPropagation();\n if (pendingClickTimeoutRef.current) {\n clearTimeout(pendingClickTimeoutRef.current);\n pendingClickTimeoutRef.current = null;\n }\n setPreviewAsset(asset);\n },\n };\n}\n","/**\n * Get the URL for a DAM asset, preferring the CDN URL if available.\n * Falls back to the API proxy URL when CDN URL is not ready.\n *\n * @param asset - The asset object with code and variant info\n * @param apiBaseUrl - Base URL for fallback API proxy (e.g. \"https://api.fluid.app/api\")\n */\nexport function getDamAssetUrl(\n asset: {\n code: string;\n default_variant_id: string | number;\n default_variant_url?: string | null;\n },\n apiBaseUrl?: string,\n): string {\n if (asset.default_variant_url) {\n return asset.default_variant_url;\n }\n const base = apiBaseUrl || \"\";\n return `${base}/dam/assets/${asset.code}/variants/${asset.default_variant_id}/content`;\n}\n","/**\n * Sanitizes a filename for safe upload by:\n * 1. Removing or replacing problematic characters\n * 2. Ensuring proper encoding for spaces and special characters\n * 3. Maintaining file extension\n */\nexport function sanitizeFilename(filename: string): string {\n if (!filename) return filename;\n\n // Split filename into name and extension\n const lastDotIndex = filename.lastIndexOf(\".\");\n const name =\n lastDotIndex > 0 ? filename.substring(0, lastDotIndex) : filename;\n const rawExtension = lastDotIndex > 0 ? filename.substring(lastDotIndex) : \"\";\n const extension = (rawExtension.split(\"?\")[0] ?? \"\").split(\"#\")[0] ?? \"\";\n\n // Clean the name part\n let sanitizedName = name\n // Replace problematic characters with underscores\n .replace(/[<>:\"/\\\\|?*]/g, \"_\")\n // Replace multiple spaces with single space\n .replace(/\\s+/g, \" \")\n // Trim whitespace\n .trim()\n // Replace spaces with underscores for better compatibility\n .replace(/\\s/g, \"_\");\n\n // If the name is empty after sanitization, use a default\n if (!sanitizedName) {\n sanitizedName = \"file\";\n }\n\n // Return the sanitized filename with extension\n return sanitizedName + extension;\n}\n\n/**\n * Extracts the base name (without extension) from a filename\n * and sanitizes it for use as an asset name\n * Server validation only allows: word characters, spaces, and hyphens\n */\nexport function getSanitizedAssetName(filename: string): string {\n if (!filename) return \"asset\";\n\n // Split filename into name and extension\n const lastDotIndex = filename.lastIndexOf(\".\");\n const name =\n lastDotIndex > 0 ? filename.substring(0, lastDotIndex) : filename;\n\n // Clean the name part for asset naming\n // Only allow word characters (letters, numbers, underscore), spaces, and hyphens\n let sanitizedName = name\n // Replace any characters that are not word characters, spaces, or hyphens with spaces\n .replace(/[^\\w\\s-]/g, \" \")\n // Replace multiple spaces with single space\n .replace(/\\s+/g, \" \")\n // Trim whitespace\n .trim();\n\n // If the name is empty after sanitization, use a default\n if (!sanitizedName) {\n sanitizedName = \"asset\";\n }\n\n return sanitizedName;\n}\n","import type { FetchClientInstance } from \"@fluid-app/api-client-core\";\nimport {\n damAssetCreateResponseSchema,\n damAssetPathCreateResponseSchema,\n getFileMimeType,\n type DamAssetCreateResponse,\n type DamAssetCreateWithPlaceholderRequest,\n type DamAssetPathCreateRequest,\n type DamAssetPathCreateResponse,\n} from \"@fluid-app/file-picker-core\";\nimport type { DamUploadStrategy } from \"../client\";\n\nexport interface CreateDamAssetParams {\n file: File;\n name: string;\n description?: string;\n tags?: string[];\n onProgress?: (progress: number) => void;\n /** Used for constructing asset URLs/paths; the API derives company from the auth token. */\n companyId?: number;\n}\n\nexport interface CreateDamAssetPathParams {\n name: string;\n description?: string;\n tags?: string[];\n companyId?: number;\n mimeType?: string;\n textContent?: string;\n fileName?: string;\n skipAutotagging?: boolean;\n}\n\n/**\n * Create a DAM asset. Text files use FormData upload; non-text files\n * delegate to the provided uploadStrategy (e.g. ImageKit).\n * If no uploadStrategy is provided, all files use FormData upload.\n */\nexport async function createDamAsset(\n fetchClient: FetchClientInstance,\n params: CreateDamAssetParams,\n uploadStrategy?: DamUploadStrategy,\n): Promise<DamAssetCreateResponse> {\n const mimeType = getFileMimeType(params.file);\n const isTextFile =\n mimeType.startsWith(\"text/\") ||\n mimeType === \"application/json\" ||\n mimeType === \"application/xml\" ||\n params.file.name.endsWith(\".txt\") ||\n params.file.name.endsWith(\".json\") ||\n params.file.name.endsWith(\".xml\") ||\n params.file.name.endsWith(\".csv\");\n\n // For text files, use traditional FormData upload\n if (isTextFile) {\n return createDamAssetViaFormData(fetchClient, params);\n }\n\n // For non-text files, use upload strategy if available\n if (uploadStrategy) {\n return uploadStrategy.uploadFile(params);\n }\n\n // Fallback to FormData upload\n return createDamAssetViaFormData(fetchClient, params);\n}\n\nasync function createDamAssetViaFormData(\n fetchClient: FetchClientInstance,\n params: CreateDamAssetParams,\n): Promise<DamAssetCreateResponse> {\n const formData = new FormData();\n formData.append(\"asset[file]\", params.file);\n formData.append(\"asset[name]\", params.name);\n\n if (params.description) {\n formData.append(\"asset[description]\", params.description);\n }\n\n if (params.tags && params.tags.length > 0) {\n formData.append(\"asset[tags]\", params.tags.join(\",\"));\n }\n\n const response = await fetchClient.requestWithFormData<unknown>(\n \"/dam/assets\",\n formData,\n { method: \"POST\" },\n );\n return damAssetCreateResponseSchema.parse(response);\n}\n\n/**\n * Create DAM asset with placeholder for direct upload strategies\n */\nexport async function createDamAssetWithPlaceholder(\n fetchClient: FetchClientInstance,\n params: CreateDamAssetPathParams,\n): Promise<DamAssetPathCreateResponse> {\n if (!params.mimeType) {\n throw new Error(\"mimeType is required for createDamAssetWithPlaceholder\");\n }\n const requestData: DamAssetCreateWithPlaceholderRequest = {\n placeholder_asset: {\n mime_type: params.mimeType,\n name: params.name,\n description: params.description,\n },\n };\n\n if (params.skipAutotagging !== undefined) {\n requestData.skip_autotagging = params.skipAutotagging;\n }\n\n const response = await fetchClient.post<unknown>(\"/dam/assets\", requestData);\n return damAssetPathCreateResponseSchema.parse(response);\n}\n\n/**\n * Create DAM asset path without file upload (legacy)\n */\nexport async function createDamAssetPath(\n fetchClient: FetchClientInstance,\n params: CreateDamAssetPathParams,\n): Promise<DamAssetPathCreateResponse> {\n const requestData: DamAssetPathCreateRequest = {};\n\n if (params.textContent && params.mimeType && params.fileName) {\n requestData.text_asset = {\n file_name: params.fileName,\n mime_type: params.mimeType,\n text: params.textContent,\n name: params.name,\n description: params.description,\n tags: params.tags?.join(\",\"),\n };\n } else if (params.mimeType) {\n requestData.placeholder_asset = {\n mime_type: params.mimeType,\n name: params.name,\n description: params.description,\n };\n } else {\n requestData.asset = {\n file: null,\n name: params.name,\n description: params.description,\n tags: params.tags?.join(\",\"),\n };\n }\n\n if (params.skipAutotagging !== undefined) {\n requestData.skip_autotagging = params.skipAutotagging;\n }\n\n const response = await fetchClient.post<unknown>(\"/dam/assets\", requestData);\n return damAssetPathCreateResponseSchema.parse(response);\n}\n\nexport interface CreateDamAssetPathForAssetsParams {\n code: string;\n asset_paths: string[];\n}\n\nexport async function createDamAssetPathForAssets(\n fetchClient: FetchClientInstance,\n { asset_paths, code }: CreateDamAssetPathForAssetsParams,\n): Promise<DamAssetPathCreateResponse> {\n const response = await fetchClient.post<unknown>(\n `/dam/assets/${code}/asset_paths`,\n { asset_paths },\n );\n return damAssetPathCreateResponseSchema.parse(response);\n}\n","import type { FetchClientInstance } from \"@fluid-app/api-client-core\";\nimport {\n damQueryResponseSchema,\n type DamQueryParams,\n type DamQueryResponse,\n} from \"@fluid-app/file-picker-core\";\n\nexport async function queryDamAssets(\n fetchClient: FetchClientInstance,\n params: DamQueryParams,\n): Promise<DamQueryResponse> {\n const response = await fetchClient.post<unknown>(\"/dam/query\", params);\n return damQueryResponseSchema.parse(response);\n}\n\nexport async function deleteDamAsset(\n fetchClient: FetchClientInstance,\n code: string,\n): Promise<unknown> {\n return fetchClient.delete(`/dam/assets/${code}`);\n}\n\nexport async function discardDamAsset(\n fetchClient: FetchClientInstance,\n code: string,\n): Promise<unknown> {\n return fetchClient.patch(`/dam/assets/${code}/discard`);\n}\n","import { z } from \"zod\";\n\nexport type UrlProxyResponse = {\n data: string;\n contentType: string;\n size: number;\n};\n\nconst urlProxyResponseSchema: z.ZodType<UrlProxyResponse> = z.object({\n data: z.string(),\n contentType: z.string(),\n size: z.number(),\n});\n\n/**\n * Proxy a URL fetch through the backend to bypass CORS restrictions.\n * The backend fetches the file and returns it as base64-encoded data.\n *\n * @param url - The URL to fetch\n * @param proxyEndpoint - The proxy endpoint (defaults to \"/api/proxy-url\")\n */\nexport async function proxyUrlFetch(\n url: string,\n proxyEndpoint: string = \"/api/proxy-url\",\n): Promise<UrlProxyResponse> {\n const response = await fetch(proxyEndpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ url }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({\n error: \"Failed to proxy URL fetch\",\n }));\n throw new Error(\n (errorData as { error?: string }).error || `HTTP ${response.status}`,\n );\n }\n\n const data: unknown = await response.json();\n return urlProxyResponseSchema.parse(data);\n}\n","import { z } from \"zod\";\n\nexport interface UnsplashImage {\n id: string;\n urls: {\n raw: string;\n full: string;\n regular: string;\n small: string;\n thumb: string;\n };\n alt_description: string | null;\n description: string | null;\n user: {\n name: string;\n username: string;\n };\n width: number;\n height: number;\n}\n\nexport interface UnsplashSearchResponse {\n results: UnsplashImage[];\n total: number;\n total_pages: number;\n}\n\nconst unsplashImageSchema: z.ZodType<UnsplashImage> = z.object({\n id: z.string(),\n urls: z.object({\n raw: z.string(),\n full: z.string(),\n regular: z.string(),\n small: z.string(),\n thumb: z.string(),\n }),\n alt_description: z.string().nullable(),\n description: z.string().nullable(),\n user: z.object({ name: z.string(), username: z.string() }),\n width: z.number(),\n height: z.number(),\n});\n\nconst unsplashSearchResponseSchema: z.ZodType<UnsplashSearchResponse> =\n z.object({\n results: z.array(unsplashImageSchema),\n total: z.number(),\n total_pages: z.number(),\n });\n\n/**\n * Search Unsplash for photos matching a query.\n */\nexport async function searchUnsplash(\n query: string,\n accessKey: string,\n page: number = 1,\n perPage: number = 20,\n): Promise<UnsplashSearchResponse> {\n const response = await fetch(\n `https://api.unsplash.com/search/photos?query=${encodeURIComponent(query)}&page=${page}&per_page=${perPage}&client_id=${accessKey}`,\n );\n\n if (!response.ok) {\n throw new Error(\"Failed to search Unsplash\");\n }\n\n return unsplashSearchResponseSchema.parse(await response.json());\n}\n","export const damQueryKeys: {\n all: readonly [\"dam\"];\n assets: () => readonly [\"dam\", \"assets\"];\n query: (params: unknown) => readonly [\"dam\", \"query\", unknown];\n} = {\n all: [\"dam\"] as const,\n assets: (): readonly [\"dam\", \"assets\"] => [\"dam\", \"assets\"] as const,\n query: (params: unknown): readonly [\"dam\", \"query\", unknown] =>\n [\"dam\", \"query\", params] as const,\n};\n","import type React from \"react\";\nimport { useState, useEffect } from \"react\";\n\nexport const useDebouncedSearch = (\n delay: number = 300,\n): {\n searchQuery: string;\n setSearchQuery: React.Dispatch<React.SetStateAction<string>>;\n debouncedSearchQuery: string;\n} => {\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [debouncedSearchQuery, setDebouncedSearchQuery] = useState(\"\");\n\n useEffect(() => {\n const timer = setTimeout(() => {\n setDebouncedSearchQuery(searchQuery);\n }, delay);\n\n return () => clearTimeout(timer);\n }, [searchQuery, delay]);\n\n return {\n searchQuery,\n setSearchQuery,\n debouncedSearchQuery,\n };\n};\n","import { useState, useCallback, useMemo, useEffect } from \"react\";\nimport { useInfiniteQuery } from \"@tanstack/react-query\";\nimport {\n queryDamAssets,\n damQueryKeys,\n} from \"@fluid-app/file-picker-api-client\";\nimport type {\n DamAsset,\n DamTree,\n FilePickerResult,\n DamAssetApi,\n DamQueryResponse,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\nimport { useDebouncedSearch } from \"./use-debounced-search\";\n\nexport type UseDamLibraryControlledSearch = {\n searchQuery: string;\n setSearchQuery: (value: string) => void;\n};\n\nconst convertApiAssetToInternal = (apiAsset: DamAssetApi): DamAsset => {\n const defaultUrl =\n (apiAsset as { default_variant_url?: string }).default_variant_url ?? \"\";\n return {\n id: apiAsset.id,\n // Fallback to `asset_code` for legacy API responses that used that field name instead of `code`\n asset_code:\n apiAsset.code ||\n (apiAsset as unknown as { asset_code: string }).asset_code,\n name: apiAsset.name,\n category: apiAsset.category,\n default_variant_url: defaultUrl || undefined,\n variants: (Array.isArray(apiAsset.variants) ? apiAsset.variants : []).map(\n (variant) => {\n const variantAny = variant as unknown as {\n metadata?: Record<string, unknown>;\n ready?: boolean;\n };\n return {\n id: String(variant.id || \"\"),\n url: variant.url || defaultUrl || \"\",\n file_name: variant.file_name || \"\",\n mime_type: variant.mime_type || \"\",\n metadata: variantAny.metadata || {},\n ready: variantAny.ready ?? variant.processing_status === \"completed\",\n tags: variant.tags || [],\n };\n },\n ),\n };\n};\n\nconst isApiDamAsset = (value: unknown): value is DamAssetApi => {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (\"code\" in value || \"asset_code\" in value) &&\n \"name\" in value &&\n \"category\" in value\n );\n};\n\nconst normalizeAsset = (asset: DamAsset | DamAssetApi): DamAsset => {\n // Always run through convertApiAssetToInternal for DamAssetApi objects.\n // We can't rely on \"asset_code\" as a discriminator because legacy API\n // responses also carry that field — using \"variants\" with mapped `ready`\n // boolean is a more reliable signal that conversion already happened.\n if (\n \"asset_code\" in asset &&\n Array.isArray((asset as DamAsset).variants) &&\n (asset as DamAsset).variants.length > 0 &&\n typeof (asset as DamAsset).variants[0]?.ready === \"boolean\"\n ) {\n return asset as DamAsset;\n }\n return convertApiAssetToInternal(asset as DamAssetApi);\n};\n\nconst flattenTree = (\n tree: DamTree,\n basePath = \"\",\n depth = 0,\n): { folders: string[]; assets: DamAsset[] } => {\n if (depth > 10) return { folders: [], assets: [] };\n\n const folders: string[] = [];\n const assets: DamAsset[] = [];\n\n for (const [key, value] of Object.entries(tree)) {\n const fullPath = basePath ? `${basePath}.${key}` : key;\n\n if (isApiDamAsset(value)) {\n assets.push(convertApiAssetToInternal(value));\n } else if (typeof value === \"object\" && value !== null) {\n if (Object.keys(value ?? {})?.length === 0) continue;\n folders.push(fullPath);\n const nested = flattenTree(value as DamTree, fullPath, depth + 1);\n folders.push(...nested.folders);\n assets.push(...nested.assets);\n }\n }\n\n return { folders, assets };\n};\n\nexport const useDamLibrary = (\n fileTypeFilter?: string[],\n controlledSearch?: UseDamLibraryControlledSearch,\n): {\n currentPath: string;\n searchQuery: string;\n setSearchQuery: (value: string) => void;\n navigateToPath: (path: string) => void;\n navigateUp: () => void;\n selectAsset: (asset: DamAsset | DamAssetApi, variantId?: string) => void;\n deselectAsset: (assetCode: string) => void;\n clearSelection: () => void;\n clearAndSelectAsset: (\n asset: DamAsset | DamAssetApi,\n variantId?: string,\n ) => void;\n getSelectedResults: () => FilePickerResult[];\n getSelectionIndex: (assetCode: string) => number;\n selectedAssets: string[];\n selectedAssetsMap: Map<string, { asset: DamAsset; variantId?: string }>;\n folders: string[];\n assets: DamAsset[];\n isLoading: boolean;\n error: Error | null;\n isSearching: boolean;\n hasNextPage: boolean;\n loadMore: () => void;\n isFetching: boolean;\n} => {\n const { apiClient, companyId } = useFilePickerContext();\n const [currentPath, setCurrentPath] = useState(\"*\");\n\n const internalSearch = useDebouncedSearch(300);\n const isControlled = controlledSearch !== undefined;\n const searchQuery = isControlled\n ? controlledSearch!.searchQuery\n : internalSearch.searchQuery;\n const setSearchQuery = isControlled\n ? controlledSearch!.setSearchQuery\n : internalSearch.setSearchQuery;\n\n const [controlledDebounced, setControlledDebounced] = useState(\n controlledSearch?.searchQuery ?? \"\",\n );\n useEffect(() => {\n if (!isControlled) return;\n const timer = setTimeout(() => setControlledDebounced(searchQuery), 300);\n return () => clearTimeout(timer);\n }, [isControlled, searchQuery]);\n\n const debouncedSearchQuery = isControlled\n ? controlledDebounced\n : internalSearch.debouncedSearchQuery;\n\n const [selectedAssets, setSelectedAssets] = useState<\n Map<string, { asset: DamAsset; variantId?: string }>\n >(new Map());\n\n const canonicalPathFilters = useMemo(() => {\n if (!fileTypeFilter || !companyId) return fileTypeFilter;\n return fileTypeFilter.map((filter) =>\n filter.includes(\".\") ? filter : `${companyId}.${filter}.*`,\n );\n }, [fileTypeFilter, companyId]);\n\n const ltreeQueryPath = useMemo(() => {\n if (!canonicalPathFilters?.length) return currentPath;\n if (canonicalPathFilters?.length === 1) return canonicalPathFilters[0];\n if (!companyId) return currentPath;\n\n const categories = canonicalPathFilters\n .map((path) => path.split(\".\")[1])\n .filter(Boolean);\n\n return categories?.length\n ? `${companyId}.${categories.join(\"|\")}.*`\n : currentPath;\n }, [canonicalPathFilters, currentPath, companyId]);\n\n const {\n data,\n isLoading,\n error,\n isFetching,\n fetchNextPage,\n hasNextPage,\n isFetchingNextPage,\n } = useInfiniteQuery({\n queryKey: damQueryKeys.query({\n path: ltreeQueryPath,\n search: debouncedSearchQuery,\n filters: canonicalPathFilters,\n }),\n queryFn: ({ pageParam }) =>\n queryDamAssets(apiClient.fetchClient, {\n path: ltreeQueryPath || \"*\",\n search: debouncedSearchQuery,\n tags_string: \"*\",\n exclude_tags: \"file_resource\",\n exclude_categories: \"text,other\",\n cursor: pageParam as string | undefined,\n }),\n getNextPageParam: (lastPage) =>\n (lastPage as DamQueryResponse)?.meta?.next_cursor || undefined,\n initialPageParam: undefined as string | undefined,\n refetchOnWindowFocus: false,\n });\n\n const navigateUp = useCallback(() => {\n if (currentPath === \"*\") return;\n\n const pathParts = currentPath.split(\".\");\n if (pathParts?.length <= 1) {\n setCurrentPath(\"*\");\n return;\n }\n if (pathParts?.[pathParts?.length - 1] === \"*\") {\n const parentParts = pathParts.slice(0, -2);\n if (parentParts?.length === 0) {\n setCurrentPath(\"*\");\n } else {\n setCurrentPath(parentParts.join(\".\") + \".*\");\n }\n } else {\n const parentParts = pathParts.slice(0, -1);\n setCurrentPath(parentParts.join(\".\") + \".*\");\n }\n }, [currentPath]);\n\n const selectAsset = useCallback(\n (asset: DamAsset | DamAssetApi, variantId?: string) => {\n const normalized = normalizeAsset(asset);\n setSelectedAssets((prev) => {\n const next = new Map(prev);\n next.set(normalized.asset_code, { asset: normalized, variantId });\n return next;\n });\n },\n [],\n );\n\n const deselectAsset = useCallback((assetCode: string) => {\n setSelectedAssets((prev) => {\n if (!prev.has(assetCode)) return prev;\n const next = new Map(prev);\n next.delete(assetCode);\n return next;\n });\n }, []);\n\n const clearSelection = useCallback(() => {\n setSelectedAssets(new Map());\n }, []);\n\n const clearAndSelectAsset = useCallback(\n (asset: DamAsset | DamAssetApi, variantId?: string) => {\n const normalized = normalizeAsset(asset);\n setSelectedAssets(\n new Map([[normalized.asset_code, { asset: normalized, variantId }]]),\n );\n },\n [],\n );\n\n const getSelectedResults = useCallback((): FilePickerResult[] => {\n return Array.from(selectedAssets.values()).map(({ asset, variantId }) => {\n const variant = variantId\n ? asset.variants.find((v) => v.id === variantId)\n : asset.variants.find((v) => v.ready) || asset.variants[0];\n const defaultVariantUrl = asset.default_variant_url ?? \"\";\n const fileUrl = variant?.url || defaultVariantUrl || \"\";\n\n const metadata = variant?.metadata || {};\n const dimensions =\n typeof metadata.width === \"number\" &&\n typeof metadata.height === \"number\"\n ? { width: metadata.width, height: metadata.height }\n : undefined;\n\n return {\n asset_id: asset.id,\n asset_code: asset.asset_code,\n file_path: fileUrl,\n file_url: fileUrl,\n metadata: {\n file_name: variant?.file_name || asset.name,\n mime_type: variant?.mime_type || \"\",\n dimensions,\n },\n upload_status: \"ready\",\n variant_id: variant?.id || undefined,\n };\n });\n }, [selectedAssets]);\n\n const getSelectionIndex = useCallback(\n (assetCode: string): number => {\n const keys = Array.from(selectedAssets.keys());\n const index = keys.indexOf(assetCode);\n return index === -1 ? -1 : index + 1;\n },\n [selectedAssets],\n );\n\n const selectedAssetCodes = useMemo(\n () => Array.from(selectedAssets.keys()),\n [selectedAssets],\n );\n\n const getCompanyTree = useCallback(\n (tree: DamTree): DamTree => {\n const companyIdStr = companyId?.toString();\n return companyIdStr && tree[companyIdStr]\n ? { [companyIdStr]: tree[companyIdStr] }\n : tree;\n },\n [companyId],\n );\n\n const { folders, allAssets } = useMemo(() => {\n if (!data?.pages?.length) {\n return { folders: [], allAssets: [] };\n }\n\n const companyIdStr = companyId?.toString();\n const excluded: string[] = companyIdStr ? [companyIdStr] : [];\n if (fileTypeFilter?.length && companyIdStr) {\n fileTypeFilter.forEach((filter) => {\n if (!filter.includes(\".\")) {\n excluded.push(`${companyIdStr}.${filter}`);\n }\n });\n }\n\n const folderSet = new Set<string>();\n const assetMap = new Map<string, DamAsset>();\n for (const page of data.pages) {\n const response = page as DamQueryResponse;\n const flat = flattenTree(getCompanyTree(response.tree));\n for (const folder of flat.folders) {\n if (!excluded.includes(folder)) {\n folderSet.add(folder);\n }\n }\n for (const asset of flat.assets) {\n assetMap.set(asset.asset_code, asset);\n }\n }\n\n return {\n folders: Array.from(folderSet),\n allAssets: Array.from(assetMap.values()),\n };\n }, [data, getCompanyTree, companyId, fileTypeFilter]);\n\n return {\n currentPath,\n searchQuery,\n setSearchQuery,\n navigateToPath: setCurrentPath,\n navigateUp,\n selectAsset,\n deselectAsset,\n clearSelection,\n clearAndSelectAsset,\n getSelectedResults,\n getSelectionIndex,\n selectedAssets: selectedAssetCodes,\n selectedAssetsMap: selectedAssets,\n folders: folders ?? [],\n assets: allAssets ?? [],\n isLoading,\n error,\n isSearching: searchQuery !== debouncedSearchQuery,\n hasNextPage: Boolean(hasNextPage),\n loadMore: fetchNextPage,\n isFetching: isFetching || isFetchingNextPage,\n };\n};\n","import type React from \"react\";\nimport type { UseFormReturn } from \"react-hook-form\";\nimport * as z from \"zod\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n Input,\n Button,\n} from \"@fluid-app/ui-primitives\";\n\nexport const folderSchema: z.ZodObject<{\n folderName: z.ZodString;\n}> = z.object({\n folderName: z\n .string()\n .min(1, \"Folder name is required\")\n .min(3, \"Folder name must be at least 3 characters\")\n .max(50, \"Folder name must be at most 50 characters\")\n .regex(\n /^[a-zA-Z0-9_]+$/,\n \"Folder name may only contain letters, numbers, and underscores\",\n ),\n});\n\ntype FolderFormValues = z.infer<typeof folderSchema>;\n\ninterface AddFolderModalProps {\n open: boolean;\n setOpen(b: boolean): void;\n onSubmit(v: FolderFormValues): void;\n form: UseFormReturn<FolderFormValues>;\n isLoading?: boolean;\n}\n\nexport function AddFolderModal({\n open,\n setOpen,\n onSubmit,\n form,\n isLoading,\n}: AddFolderModalProps): React.JSX.Element {\n const handleClose = () => {\n setOpen(false);\n form.reset();\n };\n\n return (\n <Dialog open={open} onOpenChange={handleClose} modal>\n <DialogContent title=\"Add Folder\" className=\"z-[300] sm:max-w-[425px]\">\n <DialogHeader>\n <DialogTitle>Add Folder</DialogTitle>\n <DialogDescription>\n Create a new folder by entering a name below.\n </DialogDescription>\n </DialogHeader>\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n <FormField\n control={form.control}\n name=\"folderName\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Folder Name</FormLabel>\n <FormControl>\n <Input\n placeholder=\"Enter folder name\"\n {...field}\n disabled={isLoading}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button variant=\"default\" type=\"submit\" disabled={isLoading}>\n {isLoading ? \"Creating...\" : \"Add Folder\"}\n </Button>\n </form>\n </Form>\n </DialogContent>\n </Dialog>\n );\n}\n","import type React from \"react\";\nimport {\n cn,\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n Button,\n} from \"@fluid-app/ui-primitives\";\nimport { XIcon, Trash2Icon, AlertTriangleIcon } from \"lucide-react\";\nimport type { LucideIcon } from \"lucide-react\";\n\ntype AssetActionsProps = {\n onDiscard(): void;\n onDelete(): void;\n};\n\ntype HoverIconProps = {\n label: string;\n icon: LucideIcon;\n onClick(): void;\n className?: string;\n};\n\nconst HoverIcon = ({\n icon: Icon,\n label,\n onClick,\n className,\n}: HoverIconProps) => {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n className={cn(\n \"mr-0.5 rounded-md border border-gray-300 bg-white p-2 text-gray-600 transition-colors hover:bg-gray-50 hover:text-gray-900\",\n className,\n )}\n onClick={(e) => {\n e.stopPropagation();\n e.preventDefault();\n onClick();\n }}\n >\n <Icon className=\"h-3.5 w-3.5\" />\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <p>{label}</p>\n </TooltipContent>\n </Tooltip>\n );\n};\n\nexport function AssetActions({\n onDiscard,\n onDelete,\n}: AssetActionsProps): React.JSX.Element {\n return (\n <div className={\"absolute top-1 right-0.5 z-1 hidden group-hover:flex\"}>\n <TooltipProvider>\n <HoverIcon label=\"Discard\" icon={XIcon} onClick={onDiscard} />\n\n <HoverIcon\n label=\"Delete\"\n icon={Trash2Icon}\n onClick={onDelete}\n className=\"border-red-300 text-red-600 hover:bg-red-50 hover:text-red-700\"\n />\n </TooltipProvider>\n </div>\n );\n}\n\ntype ActionModalProps = {\n open: boolean;\n title: string;\n description: string;\n cancelLabel?: string;\n actionLabel?: string;\n onCancel?(): void;\n onAction(): void;\n actionVariant?:\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\"\n | \"link\";\n loading?: boolean;\n};\n\nexport function ActionModal({\n title,\n description,\n actionLabel = \"Delete\",\n cancelLabel = \"Cancel\",\n open,\n onAction,\n onCancel,\n actionVariant = \"destructive\",\n loading,\n}: ActionModalProps): React.JSX.Element {\n return (\n <Dialog open={open} onOpenChange={onCancel}>\n <DialogContent className=\"z-[400] flex flex-col gap-5\">\n <DialogHeader className=\"flex-shrink-0\">\n <DialogTitle className=\"flex items-center gap-2\">\n <AlertTriangleIcon className=\"h-5 w-5 text-blue-600\" />\n {title}\n </DialogTitle>\n </DialogHeader>\n <div className=\"sm:flex sm:items-start\">\n <div className=\"mt-2\">\n <p className=\"text-sm text-gray-500\">{description}</p>\n </div>\n </div>\n\n <div className=\"mt-5 flex w-full justify-end gap-3 sm:mt-4\">\n <Button\n type=\"button\"\n data-autofocus\n onClick={onCancel}\n variant=\"secondary\"\n disabled={loading}\n className=\"cursor-pointer\"\n >\n {cancelLabel}\n </Button>\n <Button\n disabled={loading}\n type=\"button\"\n onClick={onAction}\n variant={actionVariant}\n className=\"cursor-pointer\"\n >\n {actionLabel}\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n","import type { LucideIcon } from \"lucide-react\";\nimport { ImageIcon, VideoIcon, FileIcon } from \"lucide-react\";\n\nexport function getCategoryIcon(category: string): LucideIcon {\n switch (category) {\n case \"images\":\n return ImageIcon;\n case \"videos\":\n return VideoIcon;\n default:\n return FileIcon;\n }\n}\n","\"use client\";\n\nimport React, { useState, useRef, useEffect } from \"react\";\nimport { cn } from \"@fluid-app/ui-primitives\";\nimport type { DamAsset, DamVariant } from \"@fluid-app/file-picker-core\";\nimport { getCategoryIcon } from \"../../utils/icons\";\n\nfunction formatDuration(seconds: number): string {\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = Math.floor(seconds % 60);\n if (h > 0)\n return `${h}:${String(m).padStart(2, \"0\")}:${String(s).padStart(2, \"0\")}`;\n return `${m}:${String(s).padStart(2, \"0\")}`;\n}\n\nfunction VideoDurationBadge({ src }: { src: string }) {\n const [duration, setDuration] = useState<string | null>(null);\n const videoRef = useRef<HTMLVideoElement | null>(null);\n\n useEffect(() => {\n const video = document.createElement(\"video\");\n video.preload = \"metadata\";\n video.src = src;\n video.onloadedmetadata = () => {\n if (video.duration && isFinite(video.duration)) {\n setDuration(formatDuration(video.duration));\n }\n };\n videoRef.current = video;\n return () => {\n video.onloadedmetadata = null;\n video.src = \"\";\n };\n }, [src]);\n\n if (!duration) return null;\n\n return (\n <span className=\"absolute right-1.5 bottom-1.5 rounded bg-black/70 px-1.5 py-0.5 text-[10px] font-medium text-white\">\n {duration}\n </span>\n );\n}\n\nexport type ThumbnailLayout = \"list\" | \"grid\" | \"preview\";\n\nexport interface AssetThumbnailProps {\n asset: DamAsset;\n variant: DamVariant | undefined;\n layout: ThumbnailLayout;\n imageSize?: number;\n className?: string;\n}\n\nexport function AssetThumbnail({\n asset,\n variant,\n layout,\n imageSize = 100,\n className,\n}: AssetThumbnailProps): React.ReactElement {\n const IconComponent = getCategoryIcon(asset.category);\n\n if (layout === \"list\") {\n return (\n <div\n className={cn(\n \"relative h-12 w-12 shrink-0 overflow-hidden rounded bg-gray-100\",\n className,\n )}\n >\n {variant?.url && asset.category === \"images\" ? (\n <img\n src={variant.url}\n alt={asset.name}\n width={48}\n height={48}\n className=\"h-full w-full object-cover\"\n />\n ) : variant?.url && asset.category === \"videos\" ? (\n <img\n src={`${variant.url.split(\"?\")[0]}/ik-thumbnail.jpg`}\n alt={variant.url || \"Video\"}\n width={48}\n height={48}\n className=\"h-full w-full object-cover\"\n />\n ) : variant?.url && asset.category === \"documents\" ? (\n <img\n src={`${variant.url.split(\"?\")[0]}/ik-thumbnail.jpg`}\n alt={asset.name || \"Document\"}\n width={48}\n height={48}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center\">\n <IconComponent className=\"h-5 w-5 text-gray-400\" />\n </div>\n )}\n </div>\n );\n }\n\n if (layout === \"preview\") {\n return (\n <div\n className={cn(\n \"relative aspect-video w-full overflow-hidden bg-gray-100\",\n className,\n )}\n >\n {variant?.url && asset.category === \"images\" ? (\n <img\n src={variant.url}\n alt={asset.name}\n width={960}\n height={540}\n className=\"h-full w-full object-contain\"\n />\n ) : variant?.url && asset.category === \"videos\" ? (\n <video\n src={variant.url}\n autoPlay\n muted\n controls\n className=\"h-full w-full object-contain\"\n />\n ) : variant?.url && asset.category === \"documents\" ? (\n <img\n src={`${variant.url.split(\"?\")[0]}/ik-thumbnail.jpg`}\n alt={asset.name}\n width={960}\n height={540}\n className=\"h-full w-full object-contain\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center\">\n <IconComponent className=\"h-16 w-16 text-gray-400\" />\n </div>\n )}\n </div>\n );\n }\n\n // grid\n return (\n <div\n className={cn(\n \"relative aspect-square overflow-hidden rounded-t-lg bg-gray-100\",\n className,\n )}\n >\n {variant?.url && asset.category === \"images\" ? (\n <img\n src={variant.url}\n alt={asset.name}\n width={imageSize}\n height={imageSize}\n className=\"h-full w-full object-contain\"\n />\n ) : variant?.url && asset.category === \"videos\" ? (\n <>\n <img\n src={`${variant.url.split(\"?\")[0]}/ik-thumbnail.jpg`}\n alt={asset.name}\n width={imageSize}\n height={imageSize}\n className=\"h-full w-full object-contain\"\n />\n <VideoDurationBadge src={variant.url} />\n </>\n ) : variant?.url && asset.category === \"documents\" ? (\n <img\n src={`${variant.url.split(\"?\")[0]}/ik-thumbnail.jpg`}\n alt={asset.name}\n width={imageSize}\n height={imageSize}\n className=\"h-full w-full object-contain\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center\">\n <IconComponent className=\"h-6 w-6 text-gray-400\" />\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { cn } from \"@fluid-app/ui-primitives\";\n\nexport interface SelectionIndexBadgeProps {\n index: number;\n className?: string;\n}\n\nexport function SelectionIndexBadge({\n index,\n className,\n}: SelectionIndexBadgeProps): React.ReactElement {\n const baseClasses =\n \"flex items-center justify-center rounded-full bg-blue-600 text-xs font-medium text-white shadow-sm z-10\";\n return (\n <div\n className={cn(\"absolute top-2 left-2 h-5 w-5\", baseClasses, className)}\n >\n {index}\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { EllipsisVerticalIcon, XIcon, Trash2Icon } from \"lucide-react\";\n\nimport {\n cn,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"@fluid-app/ui-primitives\";\nimport type { DamAsset } from \"@fluid-app/file-picker-core\";\n\nexport type ActionsMenuPosition = \"list\" | \"grid\";\n\nexport interface AssetActionsMenuProps {\n asset: DamAsset;\n position: ActionsMenuPosition;\n onDiscard: () => void;\n onDelete: () => void;\n}\n\nexport function AssetActionsMenu({\n position,\n onDiscard,\n onDelete,\n}: AssetActionsMenuProps): React.ReactElement {\n const wrapperClassName =\n position === \"list\"\n ? \"absolute top-1/2 right-2 z-10 hidden -translate-y-1/2 group-hover:block has-data-[state=open]:block\"\n : \"absolute top-2 right-2 z-10 hidden group-hover:block has-data-[state=open]:block\";\n\n return (\n <div\n className={cn(wrapperClassName)}\n onClick={(e) => e.stopPropagation()}\n onDoubleClick={(e) => e.stopPropagation()}\n >\n <DropdownMenu>\n <DropdownMenuTrigger className=\"flex h-7 w-7 items-center justify-center rounded-md border border-gray-200 bg-white text-gray-500 shadow-sm transition-colors hover:bg-gray-50 hover:text-gray-700\">\n <EllipsisVerticalIcon className=\"h-3.5 w-3.5\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"z-301\">\n <DropdownMenuItem onClick={onDiscard}>\n <XIcon className=\"h-3.5 w-3.5\" />\n Discard\n </DropdownMenuItem>\n <DropdownMenuItem\n onClick={onDelete}\n className=\"text-red-600 focus:text-red-700\"\n >\n <Trash2Icon className=\"h-3.5 w-3.5\" />\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { cn } from \"@fluid-app/ui-primitives\";\nimport { getAssetCardClassName } from \"@fluid-app/file-picker-core\";\nimport { SelectionIndexBadge } from \"./DamLibraryAsset\";\n\nexport type LibraryAssetViewMode = \"grid\" | \"list\";\n\n/**\n * Normalized item for the shared library/media grid.\n * Consumers map DAM assets or media items to this shape and provide renderThumbnail.\n */\nexport interface LibraryAssetItem<T = unknown> {\n id: string;\n name: string;\n subtitle?: string;\n isSelected: boolean;\n /** 0 = not selected, 1-based when selected (for badge) */\n selectionIndex: number;\n raw: T;\n}\n\nexport interface LibraryAssetGridProps<T> {\n items: LibraryAssetItem<T>[];\n viewMode: LibraryAssetViewMode;\n thumbnailSize: number;\n showNamesOnMedia: boolean;\n renderThumbnail: (item: T, layout: LibraryAssetViewMode) => React.ReactNode;\n onClick: (e: React.MouseEvent, id: string, index: number) => void;\n onDoubleClick?: (e: React.MouseEvent, id: string, index: number) => void;\n renderTopLeftOverlay?: (item: LibraryAssetItem<T>) => React.ReactNode;\n renderTopRightOverlay?: (item: LibraryAssetItem<T>) => React.ReactNode;\n wrapItem?: (\n item: LibraryAssetItem<T>,\n content: React.ReactNode,\n ) => React.ReactNode;\n}\n\nfunction getGridClasses(\n viewMode: LibraryAssetViewMode,\n thumbnailSize: number,\n): string {\n if (viewMode === \"list\") {\n return \"grid-cols-1\";\n }\n if (thumbnailSize <= 35) {\n return \"grid-cols-4 sm:grid-cols-6 md:grid-cols-8 lg:grid-cols-12 xl:grid-cols-16\";\n }\n if (thumbnailSize <= 50) {\n return \"grid-cols-3 sm:grid-cols-5 md:grid-cols-7 lg:grid-cols-10 xl:grid-cols-14\";\n }\n if (thumbnailSize <= 70) {\n return \"grid-cols-3 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8\";\n }\n if (thumbnailSize <= 90) {\n return \"grid-cols-2 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-7\";\n }\n if (thumbnailSize <= 110) {\n return \"grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6\";\n }\n if (thumbnailSize <= 130) {\n return \"grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5\";\n }\n return \"grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4\";\n}\n\nexport function LibraryAssetGrid<T>({\n items,\n viewMode,\n thumbnailSize,\n showNamesOnMedia,\n renderThumbnail,\n onClick,\n onDoubleClick,\n renderTopLeftOverlay,\n renderTopRightOverlay,\n wrapItem,\n}: LibraryAssetGridProps<T>): React.ReactElement {\n const gridClasses = getGridClasses(viewMode, thumbnailSize);\n\n const renderCard = (item: LibraryAssetItem<T>, index: number) => {\n const handleClick = (e: React.MouseEvent) => {\n e.preventDefault();\n onClick(e, item.id, index);\n };\n const handleDoubleClick = onDoubleClick\n ? (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onDoubleClick(e, item.id, index);\n }\n : undefined;\n\n const content = (\n <>\n {renderTopRightOverlay?.(item)}\n {viewMode === \"list\" ? (\n <div className=\"flex items-start gap-4\">\n <div className=\"h-12 w-12 shrink-0 overflow-hidden rounded bg-gray-100\">\n {renderThumbnail(item.raw, \"list\")}\n </div>\n <div className=\"min-w-0 flex-1 text-start\">\n <p className=\"truncate text-sm font-medium text-gray-900\">\n {item.name}\n </p>\n {item.subtitle != null && item.subtitle !== \"\" && (\n <p className=\"truncate text-xs text-gray-500\">\n {item.subtitle}\n </p>\n )}\n </div>\n </div>\n ) : (\n <>\n <div className=\"relative aspect-square overflow-hidden rounded-t-lg bg-gray-100 p-2\">\n {renderThumbnail(item.raw, \"grid\")}\n </div>\n {showNamesOnMedia && (\n <div className=\"bg-gray-100 px-2 py-1.5\">\n <p className=\"truncate text-xs font-medium text-gray-900\">\n {item.name}\n </p>\n {item.subtitle && (\n <p className=\"truncate text-[10px] text-gray-500\">\n {item.subtitle}\n </p>\n )}\n </div>\n )}\n </>\n )}\n {item.isSelected && item.selectionIndex > 0 && (\n <SelectionIndexBadge index={item.selectionIndex} />\n )}\n {renderTopLeftOverlay?.(item)}\n </>\n );\n\n return (\n <div\n role=\"button\"\n tabIndex={0}\n className={cn(\n \"group relative cursor-pointer overflow-hidden rounded-lg transition-all\",\n getAssetCardClassName(item.isSelected),\n viewMode === \"list\" && \"flex items-start gap-4 p-3\",\n )}\n onClick={handleClick}\n onDoubleClick={handleDoubleClick}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onClick(e as unknown as React.MouseEvent, item.id, index);\n }\n }}\n >\n {content}\n </div>\n );\n };\n\n return (\n <div className={cn(\"grid gap-4\", gridClasses)}>\n {items.map((item, index) => {\n const card = renderCard(item, index);\n const wrapped = wrapItem ? wrapItem(item, card) : card;\n return <React.Fragment key={item.id}>{wrapped}</React.Fragment>;\n })}\n </div>\n );\n}\n","import React, {\n useState,\n useEffect,\n useCallback,\n useImperativeHandle,\n forwardRef,\n useRef,\n useMemo,\n} from \"react\";\nimport {\n Button,\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n cn,\n Dialog,\n DialogOverlay,\n DialogPortal,\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n useZodForm,\n} from \"@fluid-app/ui-primitives\";\nimport { FolderIcon, LoaderIcon, ChevronDownIcon } from \"lucide-react\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport {\n createDamAssetPathForAssets,\n deleteDamAsset,\n discardDamAsset,\n damQueryKeys,\n type CreateDamAssetPathForAssetsParams,\n} from \"@fluid-app/file-picker-api-client\";\nimport type {\n FilePickerConfig,\n FilePickerResult,\n DamAsset,\n DamAssetApi,\n SortOptionId,\n} from \"@fluid-app/file-picker-core\";\nimport {\n SORT_OPTIONS,\n createAssetClickHandlers,\n} from \"@fluid-app/file-picker-core\";\nimport { useDamLibrary } from \"../hooks/use-dam-library\";\nimport type { UseDamLibraryControlledSearch } from \"../hooks/use-dam-library\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\nimport { AddFolderModal, folderSchema } from \"./AddFolderModal\";\nimport { ActionModal } from \"./AssetActions\";\nimport type { ViewMode } from \"./FilePickerHeader\";\nimport { AssetThumbnail, AssetActionsMenu } from \"./DamLibraryAsset\";\nimport {\n LibraryAssetGrid,\n type LibraryAssetItem,\n type LibraryAssetViewMode,\n} from \"./LibraryAssetGrid\";\n\ninterface DamLibraryProps {\n config: FilePickerConfig;\n onAssetsSelected: (results: FilePickerResult[]) => void;\n onSelectionChange?: (\n count: number,\n files?: FilePickerResult[],\n variantCounts?: Record<string, number>,\n ) => void;\n onConfirmAndClose?: (results: FilePickerResult[]) => void;\n onSearchingChange?: (searching: boolean) => void;\n showVariants?: boolean;\n searchQuery?: string;\n onSearchChange?: (value: string) => void;\n thumbnailSize?: number;\n viewMode?: ViewMode;\n showNamesOnMedia?: boolean;\n sortOption?: SortOptionId;\n onFoldersChange?: (folders: string[]) => void;\n previewContainer?: HTMLDivElement | null;\n previewAsset?: DamAsset | null;\n onPreviewAssetChange?: (asset: DamAsset | null) => void;\n}\n\nexport interface DamLibraryRef {\n clearSelection: () => void;\n confirmSelection: () => void;\n confirmAndClose: () => void;\n getSelectedResults: () => FilePickerResult[];\n deselectAssetByCode: (assetCode: string) => void;\n selectAssetsByCodes: (assetCodes: string[]) => string[];\n addAssetToSelection: (asset: DamAsset | DamAssetApi) => void;\n openAddFolderModal: () => void;\n addToExistingFolder: (folderPath: string) => void;\n getVariantCount: (assetCode: string) => number;\n getAssetWithVariants: (assetCode: string) => DamAsset | null;\n setVariantForAsset: (assetCode: string, variantId: string) => void;\n}\n\ntype ActionType = {\n type: \"Discard\" | \"Delete\";\n item: DamAsset;\n};\n\ntype DamBreadcrumbItem = { label: string; path: string; isCurrent: boolean };\n\nfunction getBreadcrumbsFromPath(currentPath: string): DamBreadcrumbItem[] {\n if (currentPath === \"*\") {\n return [{ label: \"All assets\", path: \"*\", isCurrent: true }];\n }\n const normalized = currentPath.endsWith(\".*\")\n ? currentPath.slice(0, -3)\n : currentPath;\n const segments = normalized.split(\".\").filter((s): s is string => Boolean(s));\n if (segments.length === 0) {\n return [{ label: \"All assets\", path: \"*\", isCurrent: true }];\n }\n const crumbs: DamBreadcrumbItem[] = [\n { label: \"All assets\", path: \"*\", isCurrent: false },\n ];\n let pathAcc = \"\";\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i] as string;\n pathAcc = pathAcc ? `${pathAcc}.${segment}` : segment;\n crumbs.push({\n label: segment,\n path: `${pathAcc}.*`,\n isCurrent: i === segments.length - 1,\n });\n }\n return crumbs;\n}\n\nexport const DamLibrary: React.ForwardRefExoticComponent<\n DamLibraryProps & React.RefAttributes<DamLibraryRef>\n> = forwardRef<DamLibraryRef, DamLibraryProps>(\n (\n {\n config,\n onAssetsSelected,\n onSelectionChange,\n onConfirmAndClose,\n onSearchingChange,\n showVariants: _showVariants = false,\n searchQuery: controlledSearchQuery,\n onSearchChange: controlledSetSearchQuery,\n thumbnailSize = 100,\n viewMode = \"grid\",\n showNamesOnMedia = true,\n sortOption,\n onFoldersChange,\n previewContainer,\n previewAsset,\n onPreviewAssetChange,\n },\n ref,\n ) => {\n \"use no memo\";\n\n const { apiClient, companyId, toast } = useFilePickerContext();\n const queryClient = useQueryClient();\n\n const controlledSearch: UseDamLibraryControlledSearch | undefined =\n controlledSearchQuery !== undefined && controlledSetSearchQuery\n ? {\n searchQuery: controlledSearchQuery,\n setSearchQuery: controlledSetSearchQuery,\n }\n : undefined;\n\n const {\n currentPath,\n navigateToPath,\n selectAsset,\n deselectAsset,\n clearSelection,\n clearAndSelectAsset,\n getSelectedResults,\n getSelectionIndex,\n selectedAssets,\n selectedAssetsMap,\n folders,\n assets,\n isLoading,\n isSearching,\n hasNextPage,\n loadMore,\n isFetching,\n } = useDamLibrary(config.fileTypeFilter, controlledSearch);\n\n const breadcrumbs = useMemo(\n () => getBreadcrumbsFromPath(currentPath),\n [currentPath],\n );\n\n const sortedAssets = useMemo(() => {\n if (!sortOption || !assets?.length) return assets ?? [];\n const option = SORT_OPTIONS.find((o) => o.id === sortOption);\n if (!option) return assets ?? [];\n\n return [...(assets ?? [])].sort((a, b) => {\n if (option.sortBy === \"name\") {\n const cmp = (a.name ?? \"\").localeCompare(b.name ?? \"\", undefined, {\n sensitivity: \"base\",\n });\n return option.sortDirection === \"asc\" ? cmp : -cmp;\n }\n if (option.sortBy === \"created_at\") {\n const aId = a.id ?? 0;\n const bId = b.id ?? 0;\n const cmp = aId - bId;\n return option.sortDirection === \"asc\" ? cmp : -cmp;\n }\n return 0;\n });\n }, [assets, sortOption]);\n\n useEffect(() => {\n onSearchingChange?.(isSearching);\n }, [isSearching, onSearchingChange]);\n\n useEffect(() => {\n onFoldersChange?.(folders ?? []);\n }, [folders, onFoldersChange]);\n\n const [open, setOpen] = useState(false);\n\n const form = useZodForm(folderSchema, {\n defaultValues: {\n folderName: \"\",\n },\n });\n\n const { mutate, isPending } = useMutation({\n mutationFn: (data: CreateDamAssetPathForAssetsParams[]) => {\n const promises = data.map((params) =>\n createDamAssetPathForAssets(apiClient.fetchClient, params),\n );\n return Promise.all(promises);\n },\n onSuccess: () => {\n toast.success(\"Assets moved to folder successfully\");\n queryClient.invalidateQueries({ queryKey: damQueryKeys.all });\n form.reset();\n setOpen(false);\n clearSelection();\n },\n onError: () => {\n toast.error(\"Failed to move assets to folder.\");\n },\n });\n\n const [action, setAction] = useState<ActionType | null>(null);\n const lastClickedIndexRef = useRef<number>(-1);\n const pendingClickTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(\n null,\n );\n\n const { mutate: deleteAssetMutate, isPending: isDeleting } = useMutation({\n mutationFn: (assetCode: string) =>\n deleteDamAsset(apiClient.fetchClient, assetCode),\n onSuccess: () => {\n toast.success(\"Asset deleted successfully\");\n queryClient.invalidateQueries({ queryKey: damQueryKeys.all });\n },\n onError: () => {\n toast.error(\"Failed to delete asset.\");\n },\n });\n\n const { mutate: discardAssetMutate, isPending: isDiscarding } = useMutation(\n {\n mutationFn: (assetCode: string) =>\n discardDamAsset(apiClient.fetchClient, assetCode),\n onSuccess: () => {\n toast.success(\"Asset discarded successfully\");\n queryClient.invalidateQueries({ queryKey: damQueryKeys.all });\n },\n onError: () => {\n toast.error(\"Failed to discard asset.\");\n },\n },\n );\n\n const getAssetBaseName = (fileName: string): string => {\n const dotIdx = fileName.lastIndexOf(\".\");\n const rawName = dotIdx > 0 ? fileName.substring(0, dotIdx) : fileName;\n return rawName.replace(/[^a-zA-Z0-9_]/g, \"_\") || \"file\";\n };\n\n const handleNewFolder = useCallback(\n (data: { folderName: string }) => {\n if (!companyId) {\n toast.error(\"Company ID is required to create folders\");\n return;\n }\n const promises = getSelectedResults().map((item) => {\n const basePath = companyId.toString() + \".\";\n const name = getAssetBaseName(item.metadata.file_name);\n\n return {\n code: item.asset_code,\n asset_paths: [`${basePath}${data.folderName}.${name}`],\n };\n });\n\n mutate(promises);\n },\n [getSelectedResults, companyId, mutate, toast],\n );\n\n const handleAddToExistingFolder = useCallback(\n (folderPath: string) => {\n const promises = getSelectedResults().map((item) => {\n const name = getAssetBaseName(item.metadata.file_name);\n\n return {\n code: item.asset_code,\n asset_paths: [`${folderPath}.${name}`],\n };\n });\n\n mutate(promises);\n },\n [getSelectedResults, mutate],\n );\n\n // Expose methods to parent component\n useImperativeHandle(\n ref,\n () => ({\n clearSelection: () => {\n clearSelection();\n },\n confirmSelection: () => {\n const results = getSelectedResults();\n onAssetsSelected(results);\n },\n confirmAndClose: () => {\n const results = getSelectedResults();\n if (onConfirmAndClose) {\n onConfirmAndClose(results);\n } else {\n onAssetsSelected(results);\n }\n },\n getSelectedResults: () => {\n return getSelectedResults();\n },\n deselectAssetByCode: (assetCode: string) => {\n deselectAsset(assetCode);\n },\n addAssetToSelection: (asset: DamAsset | DamAssetApi) => {\n selectAsset(asset);\n },\n selectAssetsByCodes: (assetCodes: string[]): string[] => {\n const setToSelect = new Set(assetCodes);\n const selected: string[] = [];\n for (const asset of sortedAssets || []) {\n if (setToSelect.has(asset.asset_code)) {\n selectAsset(asset);\n selected.push(asset.asset_code);\n }\n }\n return selected;\n },\n openAddFolderModal: () => {\n setOpen(true);\n },\n addToExistingFolder: (folderPath: string) => {\n handleAddToExistingFolder(folderPath);\n },\n getVariantCount: (assetCode: string) => {\n const entry = selectedAssetsMap.get(assetCode);\n return entry?.asset?.variants?.length ?? 0;\n },\n getAssetWithVariants: (assetCode: string) => {\n const entry = selectedAssetsMap.get(assetCode);\n return entry?.asset ?? null;\n },\n setVariantForAsset: (assetCode: string, variantId: string) => {\n const entry = selectedAssetsMap.get(assetCode);\n const asset = entry?.asset;\n if (asset) {\n if (config.maxFiles === 1) {\n clearAndSelectAsset(asset, variantId);\n } else {\n selectAsset(asset, variantId);\n }\n }\n },\n }),\n [\n clearSelection,\n getSelectedResults,\n onAssetsSelected,\n onConfirmAndClose,\n sortedAssets,\n deselectAsset,\n selectAsset,\n clearAndSelectAsset,\n handleAddToExistingFolder,\n selectedAssetsMap,\n config.maxFiles,\n ],\n );\n\n // Keep a stable ref to onSelectionChange to avoid re-running the effect\n // when the parent passes an unstable callback reference.\n const onSelectionChangeRef = useRef(onSelectionChange);\n onSelectionChangeRef.current = onSelectionChange;\n\n // Notify parent when selection count changes (include variant counts so parent can enable \"Change variant\" without reading ref during render)\n useEffect(() => {\n const count = (selectedAssets ?? []).length;\n const files = getSelectedResults();\n const variantCounts: Record<string, number> = {};\n selectedAssetsMap.forEach((entry, assetCode) => {\n const n = entry?.asset?.variants?.length ?? 0;\n if (n > 0) variantCounts[assetCode] = n;\n });\n onSelectionChangeRef.current?.(count, files, variantCounts);\n }, [selectedAssets, getSelectedResults, selectedAssetsMap]);\n\n const handleAssetClick = (\n e: React.MouseEvent,\n asset: DamAsset,\n index: number,\n ) => {\n const originalVariant =\n asset.variants.find((v) => v.ready) || asset.variants[0];\n\n if (e.shiftKey && config.maxFiles !== 1) {\n // Range select: from last clicked to current index\n const last = lastClickedIndexRef.current;\n const from = last >= 0 ? Math.min(last, index) : index;\n const to = last >= 0 ? Math.max(last, index) : index;\n const toSelect = sortedAssets?.slice(from, to + 1) ?? [];\n const maxToAdd = config.maxFiles\n ? Math.max(0, config.maxFiles - selectedAssets.length)\n : toSelect.length;\n let added = 0;\n toSelect.forEach((a: DamAsset) => {\n if (selectedAssets.includes(a.asset_code)) return;\n if (added >= maxToAdd) return;\n const v =\n a.variants.find((v: DamAsset[\"variants\"][number]) => v.ready) ||\n a.variants[0];\n selectAsset(a, v?.id);\n added += 1;\n });\n lastClickedIndexRef.current = index;\n return;\n }\n\n if ((e.metaKey || e.ctrlKey) && config.maxFiles !== 1) {\n // Toggle selection (add only if under maxFiles)\n if (selectedAssets.includes(asset.asset_code)) {\n deselectAsset(asset.asset_code);\n } else if (\n !config.maxFiles ||\n selectedAssets.length < config.maxFiles\n ) {\n selectAsset(asset, originalVariant?.id);\n }\n lastClickedIndexRef.current = index;\n return;\n }\n\n lastClickedIndexRef.current = index;\n\n if (selectedAssets.includes(asset.asset_code)) {\n deselectAsset(asset.asset_code);\n } else {\n if (config.maxFiles === 1) {\n clearAndSelectAsset(asset, originalVariant?.id);\n } else {\n selectAsset(asset, originalVariant?.id);\n }\n }\n };\n\n const closeAction = () => setAction(null);\n\n const libraryItems = useMemo((): LibraryAssetItem<DamAsset>[] => {\n if (!sortedAssets?.length) return [];\n return sortedAssets.map((asset: DamAsset) => {\n const isSelected = selectedAssets.includes(asset.asset_code);\n const selectionIndex = getSelectionIndex(asset.asset_code);\n return {\n id: asset.asset_code,\n name: asset.name ?? \"\",\n subtitle: `${asset.category} • ${asset.variants?.length ?? 0} variant${(asset.variants?.length ?? 0) !== 1 ? \"s\" : \"\"}`,\n isSelected,\n selectionIndex,\n raw: asset,\n };\n });\n }, [sortedAssets, selectedAssets, getSelectionIndex]);\n\n const getImageSize = () => {\n const baseSize = 200;\n return Math.round(baseSize * (thumbnailSize / 100));\n };\n\n return (\n <>\n <AddFolderModal\n open={open}\n setOpen={setOpen}\n onSubmit={handleNewFolder}\n form={form}\n isLoading={isPending}\n />\n\n <ActionModal\n open={action?.type === \"Delete\"}\n description={`This asset **${action?.item.name}** will be permanently deleted.`}\n onCancel={closeAction}\n onAction={() =>\n deleteAssetMutate(action?.item.asset_code ?? \"\", {\n onSuccess: closeAction,\n })\n }\n title=\"Are you sure?\"\n loading={isDeleting}\n />\n\n <ActionModal\n actionLabel=\"Discard\"\n open={action?.type === \"Discard\"}\n description={`This asset **${action?.item.name}** will be discarded from your library and will be permanently deleted after a month!`}\n onAction={() =>\n discardAssetMutate(action?.item.asset_code ?? \"\", {\n onSuccess: closeAction,\n })\n }\n title=\"Are you sure?\"\n onCancel={closeAction}\n actionVariant=\"default\"\n loading={isDiscarding}\n />\n\n {/* Preview popup on double-click */}\n <Dialog\n open={previewAsset !== null}\n onOpenChange={(open) => !open && onPreviewAssetChange?.(null)}\n >\n <DialogPortal container={previewContainer ?? undefined}>\n <DialogOverlay className=\"z-9999 bg-black/70 backdrop-blur-none\" />\n <DialogPrimitive.Content\n className=\"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 fixed top-1/2 left-1/2 z-10000 max-h-[85vh] w-full max-w-4xl -translate-x-1/2 -translate-y-1/2 overflow-hidden bg-white shadow-xl\"\n style={{ zIndex: 10000 }}\n aria-label=\"Asset preview\"\n >\n {previewAsset && (\n <div className=\"flex max-h-[85vh] flex-col\">\n <AssetThumbnail\n asset={previewAsset}\n variant={\n previewAsset.variants.find((x) => x.ready) ||\n previewAsset.variants[0]\n }\n layout=\"preview\"\n />\n </div>\n )}\n </DialogPrimitive.Content>\n </DialogPortal>\n </Dialog>\n\n <div>\n <div className=\"space-y-4\">\n <Breadcrumb>\n <BreadcrumbList className=\"min-w-0 flex-wrap\">\n {breadcrumbs.map((crumb, index) => (\n <React.Fragment key={crumb.path}>\n <BreadcrumbItem className=\"inline-flex min-w-0\">\n {crumb.isCurrent ? (\n <BreadcrumbPage className=\"max-w-48 truncate sm:max-w-xs\">\n {crumb.label}\n </BreadcrumbPage>\n ) : (\n <Button\n variant=\"ghost\"\n type=\"button\"\n onClick={() => navigateToPath(crumb.path)}\n className={cn(\n \"hover:text-foreground max-w-48 truncate transition-colors sm:max-w-xs\",\n )}\n >\n {crumb.label}\n </Button>\n )}\n </BreadcrumbItem>\n {index < breadcrumbs.length - 1 && <BreadcrumbSeparator />}\n </React.Fragment>\n ))}\n </BreadcrumbList>\n </Breadcrumb>\n\n {isLoading || isSearching ? (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"text-center\">\n <div className=\"mx-auto mb-2 h-8 w-8 animate-spin rounded-full border-2 border-blue-600 border-t-transparent\"></div>\n <p className=\"text-sm text-gray-500\">\n {isSearching ? \"Searching...\" : \"Loading assets...\"}\n </p>\n </div>\n </div>\n ) : (\n <div className=\"space-y-6\">\n {folders?.length > 0 && (\n <div className=\"space-y-6\">\n <h3 className=\"text-sm font-medium text-gray-900\">\n Folders\n </h3>\n\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5\">\n {folders.map((folder) => (\n <Button\n variant=\"ghost\"\n key={folder}\n onClick={() => navigateToPath(folder + \".*\")}\n className=\"group flex items-center gap-3 rounded-lg border border-gray-200 bg-white p-3 text-left shadow-sm transition-colors hover:border-gray-300 hover:bg-gray-50\"\n >\n <div className=\"flex h-8 w-8 items-center justify-center rounded-md bg-blue-50\">\n <FolderIcon className=\"h-4 w-4 text-blue-600\" />\n </div>\n <span className=\"truncate text-sm font-medium text-gray-900 group-hover:text-gray-700\">\n {folder.split(\".\").pop()}\n </span>\n </Button>\n ))}\n </div>\n </div>\n )}\n\n {sortedAssets?.length > 0 && (\n <div>\n <div className=\"mb-4 flex items-center justify-between\">\n <h3 className=\"text-sm font-medium text-gray-900\">\n Assets\n </h3>\n </div>\n\n <LibraryAssetGrid<DamAsset>\n items={libraryItems}\n viewMode={viewMode as LibraryAssetViewMode}\n thumbnailSize={thumbnailSize}\n showNamesOnMedia={showNamesOnMedia}\n renderThumbnail={(raw, layout) => {\n const defaultVariant =\n raw.variants.find((v) => v.ready) || raw.variants[0];\n return (\n <AssetThumbnail\n asset={raw}\n variant={defaultVariant}\n layout={layout}\n imageSize={getImageSize()}\n />\n );\n }}\n onClick={(e, id, index) => {\n const asset = sortedAssets?.[index];\n if (!asset) return;\n const handlers = createAssetClickHandlers(\n pendingClickTimeoutRef,\n handleAssetClick,\n (a) => onPreviewAssetChange?.(a),\n asset,\n index,\n );\n handlers.onClick(e);\n }}\n onDoubleClick={(e, _id, index) => {\n const asset = sortedAssets?.[index];\n if (!asset) return;\n const handlers = createAssetClickHandlers(\n pendingClickTimeoutRef,\n handleAssetClick,\n (a) => onPreviewAssetChange?.(a),\n asset,\n index,\n );\n handlers.onDoubleClick(e);\n }}\n renderTopRightOverlay={(item) => (\n <AssetActionsMenu\n asset={item.raw}\n position={viewMode === \"list\" ? \"list\" : \"grid\"}\n onDiscard={() =>\n setAction({ item: item.raw, type: \"Discard\" })\n }\n onDelete={() =>\n setAction({ item: item.raw, type: \"Delete\" })\n }\n />\n )}\n wrapItem={(item, content) => (\n <TooltipProvider key={item.id}>\n <Tooltip>\n <TooltipTrigger asChild>{content}</TooltipTrigger>\n <TooltipContent>{item.name}</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n )}\n />\n {hasNextPage && (\n <div className=\"mt-4 flex justify-center\">\n <Button\n variant=\"secondary\"\n size=\"sm\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n loadMore();\n }}\n disabled={isFetching}\n >\n {isFetching ? (\n <>\n <LoaderIcon className=\"mr-2 h-3 w-3 animate-spin\" />\n Loading...\n </>\n ) : (\n <>\n <ChevronDownIcon className=\"mr-2 h-3 w-3\" />\n Load more\n </>\n )}\n </Button>\n </div>\n )}\n </div>\n )}\n\n {folders?.length === 0 &&\n sortedAssets?.length === 0 &&\n !isLoading && (\n <div className=\"flex flex-col items-center justify-center py-12\">\n <div className=\"mb-4 rounded-full bg-gray-100 p-3\">\n <FolderIcon className=\"h-6 w-6 text-gray-400\" />\n </div>\n <h3 className=\"mb-1 text-sm font-medium text-gray-900\">\n No assets found\n </h3>\n <p className=\"text-xs text-gray-500\">\n Try searching for something else or navigate to a\n different folder\n </p>\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n </>\n );\n },\n);\n\nDamLibrary.displayName = \"DamLibrary\";\n","import React from \"react\";\nimport type { UploadMethod } from \"@fluid-app/file-picker-core\";\nimport { Button } from \"@fluid-app/ui-primitives\";\n\ninterface FilePickerSidebarProps {\n currentStep: \"select\" | \"details\";\n activeMethod: UploadMethod;\n onMethodChange: (method: UploadMethod) => void;\n methods: Array<{\n id: UploadMethod;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n description: string;\n }>;\n}\n\nexport const FilePickerSidebar: React.FC<FilePickerSidebarProps> = ({\n currentStep,\n activeMethod,\n onMethodChange,\n methods,\n}) => {\n if (currentStep !== \"select\") {\n return null;\n }\n\n return (\n <div className=\"flex hidden h-full w-64 flex-col rounded-tl-lg border-r border-gray-200 bg-gray-50 md:flex\">\n <div className=\"flex min-h-0 flex-1 flex-col gap-2 overflow-auto p-2\">\n {/* Upload Files Title */}\n <div className=\"flex flex-col gap-2 pt-3\">\n <h3 className=\"text-sm font-semibold text-gray-900\">Select File</h3>\n </div>\n\n {/* Main Tabs */}\n <div className=\"flex w-full min-w-0 flex-col gap-1 p-2\">\n {methods\n .filter((method) =>\n [\"dam\", \"media\", \"upload\", \"url\", \"unsplash\"].includes(method.id),\n )\n .map((method) => (\n <Button\n variant=\"ghost\"\n key={method.id}\n onClick={() => onMethodChange(method.id)}\n className={`flex h-8 w-full cursor-pointer items-center justify-start gap-2 rounded-md p-2 text-left text-sm transition-colors hover:bg-gray-100 ${\n activeMethod === method.id\n ? \"bg-gray-100 font-medium text-gray-900\"\n : \"text-gray-700\"\n }`}\n >\n {React.createElement(method.icon, {\n className: \"h-4 w-4 text-gray-500\",\n })}\n <span className=\"font-semibold\">{method.label}</span>\n </Button>\n ))}\n </div>\n\n {/* Cloud Storage */}\n {/* <div className=\"flex w-full min-w-0 flex-col gap-1 p-2\">\n <div className=\"mb-1 px-2 text-xs font-medium text-gray-500\">\n Cloud Storage\n </div>\n {methods\n .filter((method) =>\n [\"google-drive\", \"dropbox\"].includes(method.id),\n )\n .map((method) => {\n const Icon = method.icon;\n return (\n <button\n key={method.id}\n onClick={() => onMethodChange(method.id)}\n className={`flex h-8 w-full cursor-pointer items-center gap-2 rounded-md p-2 text-left text-sm transition-colors hover:bg-blue-50 ${\n activeMethod === method.id\n ? \"bg-blue-50 font-medium text-blue-600\"\n : \"text-gray-700\"\n }`}\n >\n <Icon className=\"h-4 w-4 text-gray-500\" />\n <span className=\"font-semibold\">{method.label}</span>\n </button>\n );\n })}\n </div> */}\n\n {/* Social Media */}\n {/* <div className=\"flex w-full min-w-0 flex-col gap-1 p-2\">\n <div className=\"mb-1 px-2 text-xs font-medium text-gray-500\">\n Social Media\n </div>\n {methods\n .filter((method) =>\n [\"instagram\", \"facebook\", \"tiktok\"].includes(method.id),\n )\n .map((method) => {\n const Icon = method.icon;\n return (\n <button\n key={method.id}\n onClick={() => onMethodChange(method.id)}\n className={`flex h-8 w-full cursor-pointer items-center gap-2 rounded-md p-2 text-left text-sm transition-colors hover:bg-blue-50 ${\n activeMethod === method.id\n ? \"bg-blue-50 font-medium text-blue-600\"\n : \"text-gray-700\"\n }`}\n >\n <Icon className=\"h-4 w-4 text-gray-500\" />\n <span className=\"font-semibold\">{method.label}</span>\n </button>\n );\n })}\n </div> */}\n </div>\n </div>\n );\n};\n","import React from \"react\";\nimport {\n Button,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@fluid-app/ui-primitives\";\nimport {\n DAM_TYPE_FILTER_OPTIONS,\n FILTER_VALUE_ALL,\n} from \"@fluid-app/file-picker-core\";\n\nexport interface FilePickerFiltersState {\n type: string;\n date: string;\n uploadedBy: string;\n usageStatus: string;\n source: string;\n}\n\nconst defaultFilters: FilePickerFiltersState = {\n type: \"\",\n date: \"\",\n uploadedBy: \"\",\n usageStatus: \"\",\n source: \"\",\n};\n\ninterface FilePickerFiltersPanelProps {\n filters: FilePickerFiltersState;\n onFiltersChange: (filters: FilePickerFiltersState) => void;\n onClear: () => void;\n onDone: () => void;\n}\n\nexport const FilePickerFiltersPanel: React.FC<FilePickerFiltersPanelProps> = ({\n filters,\n onFiltersChange,\n onClear,\n onDone,\n}) => {\n const updateFilter = <K extends keyof FilePickerFiltersState>(\n key: K,\n value: FilePickerFiltersState[K],\n ) => {\n onFiltersChange({ ...filters, [key]: value });\n };\n\n const hasActiveFilters =\n filters.type !== \"\" ||\n filters.date !== \"\" ||\n filters.uploadedBy !== \"\" ||\n filters.usageStatus !== \"\" ||\n filters.source !== \"\";\n\n return (\n <div className=\"flex w-80 flex-col gap-4 p-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium text-gray-900\">Filters</span>\n {hasActiveFilters && (\n <button\n type=\"button\"\n onClick={onClear}\n className=\"text-sm text-blue-600 hover:underline\"\n >\n Clear\n </button>\n )}\n </div>\n\n <div className=\"grid gap-4\">\n <div className=\"grid gap-2\">\n <label className=\"text-sm font-medium text-gray-700\">Type</label>\n <Select\n value={filters.type || FILTER_VALUE_ALL}\n onValueChange={(value) =>\n updateFilter(\"type\", value === FILTER_VALUE_ALL ? \"\" : value)\n }\n >\n <SelectTrigger className=\"z-[350]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent className=\"z-[350]\">\n {DAM_TYPE_FILTER_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {/* Commented out until we have the date, source, uploaded by, and usage status filters implemented */}\n {/* <div className=\"grid gap-2\">\n <label className=\"text-sm font-medium text-gray-700\">Date</label>\n ...\n </div>\n\n <div className=\"grid gap-2\">\n <label className=\"text-sm font-medium text-gray-700\">\n Uploaded by\n </label>\n ...\n </div>\n\n <div className=\"grid gap-2\">\n <label className=\"text-sm font-medium text-gray-700\">\n Usage status\n </label>\n ...\n </div>\n\n <div className=\"grid gap-2\">\n <label className=\"text-sm font-medium text-gray-700\">Source</label>\n ...\n </div> */}\n </div>\n\n <div className=\"flex justify-end gap-2 border-t border-gray-100 pt-4\">\n <Button variant=\"secondary\" size=\"sm\" onClick={onDone}>\n Done\n </Button>\n </div>\n </div>\n );\n};\n\nFilePickerFiltersPanel.displayName = \"FilePickerFiltersPanel\";\n\nexport { defaultFilters };\n","import React, { useState } from \"react\";\nimport {\n XIcon,\n MinusIcon,\n PlusIcon,\n FolderIcon,\n FolderPlusIcon,\n TypeIcon,\n LayoutGridIcon,\n ListIcon,\n SlidersHorizontalIcon,\n ArrowUpDownIcon,\n CheckIcon,\n} from \"lucide-react\";\nimport {\n Button,\n Input,\n Slider,\n Tooltip,\n TooltipTrigger,\n TooltipContent,\n TooltipProvider,\n Popover,\n PopoverContent,\n PopoverTrigger,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n} from \"@fluid-app/ui-primitives\";\n\nimport type { UploadMethod } from \"@fluid-app/file-picker-core\";\nimport {\n FilePickerFiltersPanel,\n defaultFilters as defaultFiltersState,\n} from \"./FilePickerFiltersPanel\";\nimport type { FilePickerFiltersState } from \"./FilePickerFiltersPanel\";\nimport { SORT_OPTIONS } from \"@fluid-app/file-picker-core\";\nimport type { SortOptionId } from \"@fluid-app/file-picker-core\";\n\nexport type ViewMode = \"grid\" | \"list\";\n\ninterface FilePickerHeaderProps {\n currentStep: \"select\" | \"details\";\n activeMethod: UploadMethod;\n methods: Array<{\n id: UploadMethod;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n description: string;\n }>;\n onClose: () => void;\n damSearchQuery?: string;\n onDamSearchChange?: (value: string) => void;\n damSearching?: boolean;\n thumbnailSize?: number;\n onThumbnailSizeChange?: (size: number) => void;\n viewMode?: ViewMode;\n onViewModeChange?: (mode: ViewMode) => void;\n showNamesOnMedia?: boolean;\n onShowNamesOnMediaChange?: (value: boolean) => void;\n hasSelectedFiles?: boolean;\n damFolders?: string[];\n onAddToNewFolder?: () => void;\n onAddToExistingFolder?: (folderPath: string) => void;\n filters?: FilePickerFiltersState;\n onFiltersChange?: (filters: FilePickerFiltersState) => void;\n onFiltersClear?: () => void;\n sortOption?: SortOptionId;\n onSortChange?: (optionId: SortOptionId) => void;\n unsplashSearchQuery?: string;\n onUnsplashSearchChange?: (value: string) => void;\n unsplashSearching?: boolean;\n isUnsplashUploading?: boolean;\n}\n\nexport const FilePickerHeader: React.FC<FilePickerHeaderProps> = ({\n currentStep,\n activeMethod,\n methods,\n onClose,\n damSearchQuery = \"\",\n onDamSearchChange,\n damSearching = false,\n thumbnailSize = 100,\n onThumbnailSizeChange,\n viewMode = \"grid\",\n onViewModeChange,\n showNamesOnMedia = true,\n onShowNamesOnMediaChange,\n hasSelectedFiles = false,\n damFolders = [],\n onAddToNewFolder,\n onAddToExistingFolder,\n filters,\n onFiltersChange,\n onFiltersClear,\n sortOption,\n onSortChange,\n unsplashSearchQuery = \"\",\n onUnsplashSearchChange,\n unsplashSearching = false,\n isUnsplashUploading = false,\n}) => {\n const [filtersOpen, setFiltersOpen] = useState(false);\n const showMediaToolbar =\n currentStep === \"select\" &&\n (activeMethod === \"dam\" || activeMethod === \"media\");\n const showUnsplashToolbar =\n currentStep === \"select\" && activeMethod === \"unsplash\";\n\n return (\n <div className=\"sticky top-0 z-20 flex w-full flex-wrap items-center justify-between gap-3 border-b border-gray-100 bg-white px-6 py-3\">\n <div className=\"flex min-w-0 flex-1 items-center gap-4\">\n <span className=\"shrink-0 text-lg font-semibold text-gray-900\">\n {currentStep === \"select\"\n ? methods.find((method) => method.id === activeMethod)?.label ||\n \"Select Files\"\n : \"Media Details\"}\n </span>\n\n {showMediaToolbar && (\n <div className=\"relative max-w-md min-w-0 flex-1\">\n <Input\n type=\"text\"\n placeholder=\"Search assets...\"\n value={damSearchQuery}\n onChange={(e) => onDamSearchChange?.(e.target.value)}\n className=\"h-9\"\n />\n {damSearching && (\n <div className=\"absolute top-1/2 right-3 -translate-y-1/2\">\n <div className=\"h-4 w-4 animate-spin rounded-full border-2 border-blue-600 border-t-transparent\" />\n </div>\n )}\n </div>\n )}\n\n {showUnsplashToolbar && (\n <div className=\"flex min-w-0 flex-1 justify-center\">\n <div className=\"relative w-full max-w-md\">\n <Input\n type=\"text\"\n placeholder=\"Search for images...\"\n value={unsplashSearchQuery}\n onChange={(e) => onUnsplashSearchChange?.(e.target.value)}\n disabled={isUnsplashUploading}\n className=\"h-9\"\n />\n {unsplashSearching && (\n <div className=\"absolute top-1/2 right-3 -translate-y-1/2\">\n <div className=\"h-4 w-4 animate-spin rounded-full border-2 border-blue-600 border-t-transparent\" />\n </div>\n )}\n </div>\n </div>\n )}\n </div>\n\n {/* DAM / Media Toolbar Controls */}\n {showMediaToolbar && (\n <div className=\"flex items-center gap-3\">\n {/* Zoom Slider (disabled in list view) */}\n <div\n className={\n viewMode === \"list\"\n ? \"pointer-events-none flex items-center gap-1.5 opacity-50\"\n : \"flex items-center gap-1.5\"\n }\n >\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n type=\"button\"\n onClick={() => {\n const next = Math.max(25, thumbnailSize - 25);\n onThumbnailSizeChange?.(next);\n }}\n disabled={thumbnailSize <= 25 || viewMode === \"list\"}\n className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-transparent text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 disabled:opacity-40 disabled:hover:bg-transparent\"\n >\n <MinusIcon className=\"h-3 w-3\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Zoom out</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <Slider\n value={[thumbnailSize]}\n onValueChange={(value) => {\n if (value[0] !== undefined) {\n onThumbnailSizeChange?.(value[0]);\n }\n }}\n min={25}\n max={150}\n step={25}\n className=\"w-20\"\n disabled={viewMode === \"list\"}\n />\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n type=\"button\"\n onClick={() => {\n const next = Math.min(150, thumbnailSize + 25);\n onThumbnailSizeChange?.(next);\n }}\n disabled={thumbnailSize >= 150 || viewMode === \"list\"}\n className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-transparent text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 disabled:opacity-40 disabled:hover:bg-transparent\"\n >\n <PlusIcon className=\"h-3 w-3\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Zoom in</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n </div>\n\n <div className=\"h-5 w-px bg-gray-200\" />\n\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => onShowNamesOnMediaChange?.(!showNamesOnMedia)}\n disabled={viewMode === \"list\"}\n className={`flex h-8 w-8 items-center justify-center rounded-md bg-transparent transition-colors ${\n viewMode === \"list\"\n ? \"cursor-not-allowed opacity-50\"\n : showNamesOnMedia\n ? \"bg-blue-50 text-blue-600\"\n : \"text-gray-500 hover:bg-gray-100 hover:text-gray-700\"\n }`}\n >\n <TypeIcon className=\"h-4 w-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n {viewMode === \"list\"\n ? \"Not available in list view\"\n : showNamesOnMedia\n ? \"Hide names on media\"\n : \"Show names on media\"}\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n\n <div className=\"h-5 w-px bg-gray-200\" />\n\n {/* View Mode Toggles - Grid and List */}\n <div className=\"flex items-center gap-1 rounded-md border border-gray-200 bg-gray-50 p-0.5\">\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => onViewModeChange?.(\"grid\")}\n className={`flex h-7 w-7 items-center justify-center rounded bg-transparent transition-colors ${\n viewMode === \"grid\"\n ? \"bg-white text-blue-600 shadow-sm\"\n : \"text-gray-500 hover:bg-gray-100 hover:text-gray-700\"\n }`}\n >\n <LayoutGridIcon className=\"h-3.5 w-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Grid View</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => onViewModeChange?.(\"list\")}\n className={`flex h-7 w-7 items-center justify-center rounded bg-transparent transition-colors ${\n viewMode === \"list\"\n ? \"bg-white text-blue-600 shadow-sm\"\n : \"text-gray-500 hover:text-gray-700\"\n }`}\n >\n <ListIcon className=\"h-3.5 w-3.5\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>List View</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n </div>\n\n <div className=\"h-5 w-px bg-gray-200\" />\n\n {activeMethod === \"dam\" && (\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"inline-flex\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n disabled={!hasSelectedFiles}\n className=\"flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700 disabled:opacity-40\"\n >\n <FolderPlusIcon className=\"h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"z-350 min-w-[200px]\">\n <DropdownMenuItem\n onClick={() => onAddToNewFolder?.()}\n className=\"cursor-pointer\"\n >\n <FolderPlusIcon className=\"mr-2 h-4 w-4\" />\n Add to New Folder\n </DropdownMenuItem>\n {damFolders.length > 0 && (\n <DropdownMenuSub>\n <DropdownMenuSubTrigger className=\"cursor-pointer\">\n <FolderPlusIcon className=\"mr-2 h-4 w-4\" />\n Add to Existing Folder\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"z-350 max-h-64 overflow-y-auto\">\n {damFolders.map((folder) => (\n <DropdownMenuItem\n key={folder}\n onClick={() =>\n onAddToExistingFolder?.(folder)\n }\n className=\"cursor-pointer\"\n >\n <FolderIcon className=\"mr-2 h-4 w-4\" />\n {folder.split(\".\").pop() || folder}\n </DropdownMenuItem>\n ))}\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n {hasSelectedFiles\n ? \"Add selected files to a folder\"\n : \"Select files to add to a folder\"}\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n )}\n\n {activeMethod === \"dam\" && <div className=\"h-5 w-px bg-gray-200\" />}\n\n {/* Filters */}\n <Popover open={filtersOpen} onOpenChange={setFiltersOpen}>\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n type=\"button\"\n className={`flex h-8 w-8 items-center justify-center rounded-md bg-transparent transition-colors ${\n filters &&\n (filters.type ||\n filters.date ||\n filters.uploadedBy ||\n filters.usageStatus ||\n filters.source)\n ? \"bg-blue-50 text-blue-600\"\n : \"text-gray-500 hover:bg-gray-100 hover:text-gray-700\"\n }`}\n >\n <SlidersHorizontalIcon className=\"h-4 w-4\" />\n </Button>\n </PopoverTrigger>\n </TooltipTrigger>\n <TooltipContent>Filters</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <PopoverContent align=\"end\" className=\"w-auto p-0\">\n <FilePickerFiltersPanel\n filters={filters ?? defaultFiltersState}\n onFiltersChange={(f) => onFiltersChange?.(f)}\n onClear={() => {\n onFiltersClear?.();\n setFiltersOpen(false);\n }}\n onDone={() => setFiltersOpen(false)}\n />\n </PopoverContent>\n </Popover>\n\n {/* Sort */}\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"inline-flex\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-700\"\n >\n <ArrowUpDownIcon className=\"h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"z-350 min-w-[180px]\">\n {SORT_OPTIONS.map((option) => (\n <DropdownMenuItem\n key={option.id}\n onClick={() => onSortChange?.(option.id)}\n >\n <span\n className={\n sortOption === option.id\n ? \"font-medium\"\n : undefined\n }\n >\n {option.label}\n </span>\n {sortOption === option.id && (\n <CheckIcon className=\"ml-auto h-4 w-4 text-blue-600\" />\n )}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n </span>\n </TooltipTrigger>\n <TooltipContent>Sort</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n </div>\n )}\n\n {/* Close button - always visible */}\n <Button variant=\"ghost\" size=\"sm\" onClick={onClose} className=\"ml-2\">\n <XIcon className=\"h-4 w-4\" />\n </Button>\n </div>\n );\n};\n","import React, { useState, useCallback, useEffect, useMemo } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport {\n ImageIcon,\n VideoIcon,\n FileIcon,\n LoaderIcon,\n SearchIcon,\n ChevronDownIcon,\n} from \"lucide-react\";\nimport {\n Button,\n Input,\n Dialog,\n DialogOverlay,\n DialogPortal,\n DialogContent,\n} from \"@fluid-app/ui-primitives\";\nimport type {\n FilePickerResult,\n SortOptionId,\n} from \"@fluid-app/file-picker-core\";\nimport { SORT_OPTIONS } from \"@fluid-app/file-picker-core\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\nimport {\n LibraryAssetGrid,\n type LibraryAssetItem,\n type LibraryAssetViewMode,\n} from \"./LibraryAssetGrid\";\nimport type { MediumResponse } from \"../types/media\";\n\ninterface MediaTabProps {\n config: {\n maxFiles?: number;\n fileTypeFilter?: string[];\n };\n onMediaSelected: (results: FilePickerResult[]) => void;\n onSelectionChange?: (count: number) => void;\n onMediaDataChange?: (data: FilePickerResult[]) => void;\n selectedMediaIds: number[];\n onMediaIdsChange: (ids: number[]) => void;\n searchQuery?: string;\n onSearchChange?: (value: string) => void;\n thumbnailSize?: number;\n viewMode?: LibraryAssetViewMode;\n showNamesOnMedia?: boolean;\n mediaTypeFilter?: string;\n sortOption?: SortOptionId;\n previewContainer?: HTMLDivElement | null;\n previewMedia?: MediumResponse | null;\n onPreviewMediaChange?: (media: MediumResponse | null) => void;\n}\n\nconst DAM_CATEGORY_TO_MEDIA_TYPE: Record<string, string> = {\n images: \"image\",\n videos: \"video\",\n documents: \"document\",\n audio: \"audio\",\n text: \"document\",\n other: \"image\",\n presentations: \"presentation\",\n};\n\nexport const MediaTab: React.FC<MediaTabProps> = ({\n config,\n onSelectionChange,\n onMediaDataChange,\n selectedMediaIds,\n onMediaIdsChange,\n searchQuery: controlledSearchQuery,\n onSearchChange: controlledSetSearchQuery,\n thumbnailSize = 100,\n viewMode = \"grid\",\n showNamesOnMedia = true,\n mediaTypeFilter: headerTypeFilter,\n sortOption,\n previewContainer,\n previewMedia,\n onPreviewMediaChange,\n}) => {\n const { shareablesClient } = useFilePickerContext();\n\n const [internalSearchQuery, setInternalSearchQuery] = useState(\"\");\n const isSearchControlled =\n controlledSearchQuery !== undefined && controlledSetSearchQuery != null;\n const searchQuery = isSearchControlled\n ? controlledSearchQuery\n : internalSearchQuery;\n const setSearchQuery = isSearchControlled\n ? controlledSetSearchQuery\n : setInternalSearchQuery;\n const [allMediaItems, setAllMediaItems] = useState<MediumResponse[]>([]);\n const [currentPage, setCurrentPage] = useState(1);\n const [loadingMore, setLoadingMore] = useState(false);\n\n const mediaTypeFilter = useMemo(() => {\n if (headerTypeFilter && DAM_CATEGORY_TO_MEDIA_TYPE[headerTypeFilter]) {\n return DAM_CATEGORY_TO_MEDIA_TYPE[headerTypeFilter];\n }\n if (!config.fileTypeFilter || config.fileTypeFilter.length === 0) {\n return undefined;\n }\n for (const filter of config.fileTypeFilter) {\n if (DAM_CATEGORY_TO_MEDIA_TYPE[filter]) {\n return DAM_CATEGORY_TO_MEDIA_TYPE[filter];\n }\n }\n return undefined;\n }, [headerTypeFilter, config.fileTypeFilter]);\n\n // Fetch all media (same as media index table)\n const {\n data: mediaResponse,\n isLoading,\n error,\n } = useQuery({\n queryKey: [\"media-tab\", searchQuery, mediaTypeFilter],\n queryFn: () => {\n if (!shareablesClient) {\n throw new Error(\"Shareables client is not configured\");\n }\n return shareablesClient.media.list({\n search_query: searchQuery,\n page: 1,\n per_page: 30,\n active: true,\n with_type: mediaTypeFilter,\n }) as Promise<{\n media: MediumResponse[];\n meta?: { next: string | null };\n }>;\n },\n enabled: !!shareablesClient,\n staleTime: 5 * 60 * 1000, // 5 minutes\n });\n\n // Reset pagination when search or media type filter changes\n useEffect(() => {\n setAllMediaItems([]);\n setCurrentPage(1);\n }, [searchQuery, mediaTypeFilter]);\n\n // Update accumulated media items when new data comes in\n useEffect(() => {\n if (!mediaResponse) return;\n\n const newMediaItems = (mediaResponse.media || []) as MediumResponse[];\n if (newMediaItems.length > 0) {\n setAllMediaItems((prev) => {\n // Merge without duplicates by id\n const map = new Map(prev.map((item) => [item.id, item]));\n for (const item of newMediaItems) {\n map.set(item.id, item);\n }\n return Array.from(map.values());\n });\n }\n }, [mediaResponse]);\n\n const hasNextPage = mediaResponse?.meta?.next !== null;\n\n // Load more media items\n const loadMore = useCallback(async () => {\n if (!hasNextPage || !shareablesClient) return;\n\n try {\n setLoadingMore(true);\n const nextPage = currentPage + 1;\n const response = (await shareablesClient.media.list({\n search_query: searchQuery,\n page: nextPage,\n per_page: 30,\n active: true,\n with_type: mediaTypeFilter,\n })) as { media: MediumResponse[]; meta?: { next: string | null } };\n\n const newMediaItems = (response.media || []) as MediumResponse[];\n if (newMediaItems.length > 0) {\n setAllMediaItems((prev) => {\n // Merge without duplicates by id\n const map = new Map(prev.map((item) => [item.id, item]));\n for (const item of newMediaItems) {\n map.set(item.id, item);\n }\n return Array.from(map.values());\n });\n setCurrentPage(nextPage);\n }\n } finally {\n setLoadingMore(false);\n }\n }, [\n hasNextPage,\n currentPage,\n searchQuery,\n mediaTypeFilter,\n shareablesClient,\n ]);\n\n // Convert MediumResponse to FilePickerResult\n const convertToFilePickerResult = useCallback(\n (media: MediumResponse): FilePickerResult => {\n // Determine the file URL based on media type\n let fileUrl = \"\";\n let mimeType = \"\";\n let fileName = media.title;\n let thumbnailUrl = \"\";\n let description = \"\";\n\n // Almost all media have image_url that stores the url for thumbnail/preview\n // so, we'll check the presence of image_url at the end of the logic\n if (media.video_url) {\n fileUrl = media.video_url;\n mimeType = \"video/mp4\"; // Default, could be improved with actual detection\n fileName = `${media.title}.mp4`;\n thumbnailUrl = media.image_url || \"\";\n description = media.description?.body || \"\";\n } else if (media.pdf_url) {\n fileUrl = media.pdf_url;\n mimeType = \"application/pdf\";\n fileName = `${media.title}.pdf`;\n } else if (media.image_url) {\n fileUrl = media.image_url;\n mimeType = \"image/jpeg\"; // Default, could be improved with actual detection\n fileName = `${media.title}.jpg`;\n }\n\n return {\n asset_id: media.id,\n asset_code: `media_${media.id}`,\n file_path: fileUrl,\n file_url: fileUrl,\n ...(thumbnailUrl ? { thumbnail_url: thumbnailUrl } : {}),\n ...(description ? { description: description } : {}),\n metadata: {\n file_name: fileName,\n mime_type: mimeType,\n },\n upload_status: \"ready\" as const,\n };\n },\n [],\n );\n\n const handleMediaClick = useCallback(\n (_e: React.MouseEvent, id: string, _index: number) => {\n const mediaId = parseInt(id.replace(/^media_/, \"\"), 10);\n if (Number.isNaN(mediaId)) return;\n\n const isSelected = selectedMediaIds.includes(mediaId);\n let newSelectedIds: number[];\n\n if (isSelected) {\n newSelectedIds = selectedMediaIds.filter((id) => id !== mediaId);\n } else {\n if (config.maxFiles === 1) {\n newSelectedIds = [mediaId];\n } else if (\n config.maxFiles &&\n selectedMediaIds.length >= config.maxFiles\n ) {\n return;\n } else {\n newSelectedIds = [...selectedMediaIds, mediaId];\n }\n }\n\n onMediaIdsChange(newSelectedIds);\n\n const idToMedia = new Map(allMediaItems.map((m) => [m.id, m]));\n const orderedMedia = newSelectedIds\n .map((id) => idToMedia.get(id))\n .filter((m): m is MediumResponse => m != null);\n const filePickerResults = orderedMedia.map(convertToFilePickerResult);\n onMediaDataChange?.(filePickerResults);\n },\n [\n selectedMediaIds,\n config.maxFiles,\n allMediaItems,\n convertToFilePickerResult,\n onMediaIdsChange,\n onMediaDataChange,\n ],\n );\n\n // Update selection count when selectedMediaIds changes\n React.useEffect(() => {\n onSelectionChange?.(selectedMediaIds.length);\n }, [selectedMediaIds.length, onSelectionChange]);\n\n const sortedMediaItems = useMemo(() => {\n if (!sortOption || !allMediaItems.length) return allMediaItems;\n const option = SORT_OPTIONS.find((o) => o.id === sortOption);\n if (!option) return allMediaItems;\n return [...allMediaItems].sort((a, b) => {\n if (option.sortBy === \"name\") {\n const cmp = (a.title ?? \"\").localeCompare(b.title ?? \"\", undefined, {\n sensitivity: \"base\",\n });\n return option.sortDirection === \"asc\" ? cmp : -cmp;\n }\n if (option.sortBy === \"created_at\") {\n const aId = a.id ?? 0;\n const bId = b.id ?? 0;\n const cmp = aId - bId;\n return option.sortDirection === \"asc\" ? cmp : -cmp;\n }\n return 0;\n });\n }, [allMediaItems, sortOption]);\n\n const getMediaIcon = (media: MediumResponse) => {\n if (media.image_url) return ImageIcon;\n if (media.video_url) return VideoIcon;\n if (media.pdf_url) return FileIcon;\n return FileIcon;\n };\n\n const renderMediaThumbnail = useCallback(\n (media: MediumResponse, _layout: LibraryAssetViewMode) => {\n if (media.image_url) {\n return (\n <img\n src={media.image_url}\n alt={media.title}\n width={200}\n height={150}\n className=\"h-full w-full object-cover\"\n />\n );\n }\n const MediaIcon = getMediaIcon(media);\n return (\n <div className=\"flex h-full w-full items-center justify-center bg-gray-100\">\n <MediaIcon className=\"h-8 w-8 text-gray-400\" />\n </div>\n );\n },\n [],\n );\n\n const libraryItems = useMemo((): LibraryAssetItem<MediumResponse>[] => {\n return sortedMediaItems.map((media) => {\n const id = `media_${media.id}`;\n const indexInSelection = selectedMediaIds.indexOf(media.id);\n const isSelected = indexInSelection >= 0;\n const selectionIndex = isSelected ? indexInSelection + 1 : 0;\n return {\n id,\n name: media.title ?? \"\",\n subtitle: media.kind ?? \"Media\",\n isSelected,\n selectionIndex,\n raw: media,\n };\n });\n }, [sortedMediaItems, selectedMediaIds]);\n\n const handleMediaDoubleClick = useCallback(\n (_e: React.MouseEvent, _id: string, index: number) => {\n const media = sortedMediaItems[index];\n if (media) onPreviewMediaChange?.(media);\n },\n [sortedMediaItems, onPreviewMediaChange],\n );\n\n if (error) {\n return (\n <div className=\"flex flex-col items-center justify-center py-12\">\n <div className=\"text-center\">\n <p className=\"text-sm text-red-600\">Failed to load media</p>\n <p className=\"mt-1 text-xs text-gray-500\">Please try again later</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4\">\n <Dialog\n open={!!previewMedia}\n onOpenChange={(open) => !open && onPreviewMediaChange?.(null)}\n >\n <DialogPortal container={previewContainer ?? undefined}>\n <DialogOverlay className=\"z-9999 bg-black/70 backdrop-blur-none\" />\n <DialogContent\n className=\"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 fixed top-1/2 left-1/2 z-10000 max-h-[85vh] w-full max-w-4xl -translate-x-1/2 -translate-y-1/2 overflow-hidden bg-white shadow-xl\"\n style={{ zIndex: 10000 }}\n >\n {previewMedia && (\n <div className=\"flex max-h-[85vh] flex-col\">\n {previewMedia.image_url ? (\n <div className=\"relative flex min-h-0 justify-center overflow-hidden bg-gray-100\">\n <img\n src={previewMedia.image_url}\n alt={previewMedia.title}\n className=\"max-h-[85vh] w-auto max-w-full object-contain\"\n />\n </div>\n ) : previewMedia.video_url ? (\n <div className=\"relative aspect-video max-h-[85vh] overflow-hidden bg-black\">\n <video\n src={previewMedia.video_url}\n autoPlay\n muted\n controls\n className=\"h-full w-full object-contain\"\n />\n </div>\n ) : (\n <div className=\"flex aspect-video min-h-[200px] items-center justify-center bg-gray-100 p-8\">\n {(() => {\n const PreviewIcon = getMediaIcon(previewMedia);\n return (\n <PreviewIcon className=\"h-16 w-16 text-gray-400\" />\n );\n })()}\n <p className=\"ml-4 text-sm text-gray-600\">\n {previewMedia.title}\n </p>\n </div>\n )}\n </div>\n )}\n </DialogContent>\n </DialogPortal>\n </Dialog>\n\n {/* Search (only when not controlled from header) */}\n {!isSearchControlled && (\n <div className=\"relative\">\n <Input\n type=\"text\"\n placeholder=\"Search media...\"\n value={searchQuery}\n onChange={(e) => {\n setSearchQuery(e.target.value);\n }}\n className=\"h-9\"\n />\n <div className=\"absolute top-1/2 right-3 -translate-y-1/2\">\n <SearchIcon className=\"h-4 w-4 text-gray-400\" />\n </div>\n </div>\n )}\n\n {/* Media Grid */}\n {isLoading ? (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"text-center\">\n <LoaderIcon className=\"mx-auto mb-2 h-8 w-8 animate-spin text-blue-600\" />\n <p className=\"text-sm text-gray-500\">Loading media...</p>\n </div>\n </div>\n ) : allMediaItems.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12\">\n <div className=\"mb-4 rounded-full bg-gray-100 p-3\">\n <ImageIcon className=\"h-6 w-6 text-gray-400\" />\n </div>\n <h3 className=\"mb-1 text-sm font-medium text-gray-900\">\n No media found\n </h3>\n <p className=\"text-xs text-gray-500\">\n {searchQuery\n ? \"Try adjusting your search terms\"\n : \"Create some media to get started\"}\n </p>\n </div>\n ) : (\n <div className=\"space-y-4\">\n <LibraryAssetGrid<MediumResponse>\n items={libraryItems}\n viewMode={viewMode ?? \"grid\"}\n thumbnailSize={thumbnailSize}\n showNamesOnMedia={showNamesOnMedia ?? true}\n renderThumbnail={renderMediaThumbnail}\n onClick={handleMediaClick}\n onDoubleClick={handleMediaDoubleClick}\n />\n\n {/* Load More Button */}\n {hasNextPage && (\n <div className=\"flex justify-center\">\n <Button\n variant=\"secondary\"\n size=\"sm\"\n onClick={loadMore}\n disabled={loadingMore}\n >\n {loadingMore ? (\n <>\n <LoaderIcon className=\"mr-2 h-3 w-3 animate-spin\" />\n Loading...\n </>\n ) : (\n <>\n <ChevronDownIcon className=\"mr-2 h-3 w-3\" />\n Load more\n </>\n )}\n </Button>\n </div>\n )}\n </div>\n )}\n </div>\n );\n};\n\nexport default MediaTab;\n","import { useState, useCallback } from \"react\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport {\n createDamAsset,\n damQueryKeys,\n searchUnsplash,\n type UnsplashImage,\n} from \"@fluid-app/file-picker-api-client\";\nimport {\n getDamAssetUrl,\n getSanitizedAssetName,\n type FilePickerResult,\n type UploadProgress,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\n\ninterface UseUnsplashPickerOptions {\n controlledSearchQuery?: string;\n onControlledSearchQueryChange?: (value: string) => void;\n}\n\nexport const useUnsplashPicker = (\n options?: UseUnsplashPickerOptions,\n): {\n searchQuery: string;\n setSearchQuery: (value: string) => void;\n searchResults: UnsplashImage[];\n isSearching: boolean;\n searchImages: (query: string, page?: number) => void;\n downloadAndUpload: (image: UnsplashImage) => Promise<FilePickerResult>;\n uploadProgress: UploadProgress[];\n isUploading: boolean;\n clearProgress: () => void;\n hasMoreResults: boolean;\n loadMore: () => void;\n isLoadingMore: boolean;\n} => {\n const { controlledSearchQuery, onControlledSearchQueryChange } =\n options ?? {};\n const [internalSearchQuery, setInternalSearchQuery] = useState(\"\");\n const searchQuery = controlledSearchQuery ?? internalSearchQuery;\n const setSearchQuery =\n onControlledSearchQueryChange ?? setInternalSearchQuery;\n const [searchResults, setSearchResults] = useState<UnsplashImage[]>([]);\n const [isSearching, setIsSearching] = useState(false);\n const [isLoadingMore, setIsLoadingMore] = useState(false);\n const [currentPage, setCurrentPage] = useState(1);\n const [hasMoreResults, setHasMoreResults] = useState(true);\n const [uploadProgress, setUploadProgress] = useState<\n Map<string, UploadProgress>\n >(new Map());\n const queryClient = useQueryClient();\n const { apiClient, unsplashAccessKey, apiBaseUrl, toast } =\n useFilePickerContext();\n\n const uploadMutation = useMutation({\n mutationFn: (params: Parameters<typeof createDamAsset>[1]) =>\n createDamAsset(apiClient.fetchClient, params, apiClient.uploadStrategy),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: damQueryKeys.all });\n },\n });\n\n const searchImages = useCallback(\n async (query: string, page = 1) => {\n if (!query.trim()) {\n setSearchResults([]);\n setCurrentPage(1);\n setHasMoreResults(true);\n return;\n }\n\n if (page === 1) {\n setIsSearching(true);\n setCurrentPage(1);\n } else {\n setIsLoadingMore(true);\n }\n\n try {\n if (!unsplashAccessKey) {\n throw new Error(\"Unsplash Access Key is not configured\");\n }\n\n const data = await searchUnsplash(query, unsplashAccessKey, page);\n\n setSearchResults((prevResults) =>\n page === 1 ? data.results : [...prevResults, ...data.results],\n );\n\n setCurrentPage(page);\n setHasMoreResults(page < data.total_pages);\n } catch (err) {\n if (page === 1) {\n setSearchResults([]);\n }\n toast.error(\n err instanceof Error ? err.message : \"Unsplash search failed\",\n );\n } finally {\n setIsSearching(false);\n setIsLoadingMore(false);\n }\n },\n [unsplashAccessKey, toast],\n );\n\n const downloadAndUpload = useCallback(\n async (image: UnsplashImage): Promise<FilePickerResult> => {\n const uploadId = `unsplash-${image.id}`;\n const fileName = `unsplash-${image.id}.jpg`;\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: fileName,\n progress: 0,\n status: \"uploading\",\n });\n return next;\n });\n\n try {\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: fileName,\n progress: 25,\n status: \"uploading\",\n });\n return next;\n });\n\n const downloadUrlObj = new URL(image.urls.regular);\n downloadUrlObj.searchParams.set(\"fm\", \"jpg\");\n downloadUrlObj.searchParams.set(\"q\", \"80\");\n const downloadUrl = downloadUrlObj.toString();\n const response = await fetch(downloadUrl);\n\n if (!response.ok) {\n throw new Error(`Failed to download image: ${response.statusText}`);\n }\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: fileName,\n progress: 50,\n status: \"uploading\",\n });\n return next;\n });\n\n const blob = await response.blob();\n const file = new File([blob], fileName, { type: \"image/jpeg\" });\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: fileName,\n progress: 75,\n status: \"uploading\",\n });\n return next;\n });\n\n const damResponse = await uploadMutation.mutateAsync({\n file,\n name: getSanitizedAssetName(\n image.alt_description ||\n image.description ||\n `Unsplash ${image.id}`,\n ),\n description: `Downloaded from Unsplash. Photo by ${image.user.name} (@${image.user.username})`,\n tags: [\"unsplash\", image.user.username],\n });\n\n if (!damResponse.asset) {\n throw new Error(\n \"Invalid DAM response structure - missing asset property\",\n );\n }\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: fileName,\n progress: 100,\n status: \"ready\",\n });\n return next;\n });\n\n const asset = damResponse.asset;\n const fileUrl = getDamAssetUrl(asset, apiBaseUrl);\n\n return {\n asset_id: asset.id,\n asset_code: asset.code,\n file_path: fileUrl,\n file_url: fileUrl,\n metadata: {\n file_name: fileName,\n mime_type: \"image/jpeg\",\n dimensions: {\n width: image.width,\n height: image.height,\n },\n },\n upload_status: \"ready\",\n variant_id: asset.default_variant_id\n ? asset.default_variant_id.toString()\n : undefined,\n };\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : \"Upload failed\";\n toast.error(errorMessage);\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: fileName,\n progress: 0,\n status: \"error\",\n error: errorMessage,\n });\n return next;\n });\n\n return {\n asset_id: 0,\n asset_code: \"\",\n file_path: \"\",\n file_url: \"\",\n metadata: {\n file_name: fileName,\n mime_type: \"image/jpeg\",\n dimensions: {\n width: image.width,\n height: image.height,\n },\n },\n upload_status: \"error\",\n };\n }\n },\n [uploadMutation, apiBaseUrl, toast],\n );\n\n const clearProgress = useCallback(() => {\n setUploadProgress(new Map());\n }, []);\n\n const loadMore = useCallback(() => {\n if (searchQuery.trim() && hasMoreResults && !isLoadingMore) {\n searchImages(searchQuery, currentPage + 1);\n }\n }, [searchQuery, hasMoreResults, isLoadingMore, currentPage, searchImages]);\n\n return {\n searchQuery,\n setSearchQuery,\n searchResults,\n isSearching,\n searchImages,\n downloadAndUpload,\n uploadProgress: Array.from(uploadProgress.values()),\n isUploading: uploadMutation.isPending,\n clearProgress,\n hasMoreResults,\n loadMore,\n isLoadingMore,\n };\n};\n","import React, { useState, useEffect } from \"react\";\nimport { Button } from \"@fluid-app/ui-primitives\";\nimport {\n DownloadIcon,\n CheckIcon,\n AlertTriangleIcon,\n UserIcon,\n LoaderIcon,\n} from \"lucide-react\";\nimport { useUnsplashPicker } from \"../hooks/use-unsplash-picker\";\nimport type {\n FilePickerConfig,\n FilePickerResult,\n} from \"@fluid-app/file-picker-core\";\n\ninterface UnsplashImage {\n id: string;\n urls: {\n raw: string;\n full: string;\n regular: string;\n small: string;\n thumb: string;\n };\n alt_description: string | null;\n description: string | null;\n user: {\n name: string;\n username: string;\n };\n width: number;\n height: number;\n}\n\ninterface UnsplashPickerProps {\n config: FilePickerConfig;\n onFileSelected: (result: FilePickerResult) => void;\n searchQuery?: string;\n onSearchQueryChange?: (value: string) => void;\n onSearchingChange?: (searching: boolean) => void;\n onUploadingChange?: (uploading: boolean) => void;\n}\n\nexport const UnsplashPicker: React.FC<UnsplashPickerProps> = ({\n config,\n onFileSelected,\n searchQuery: controlledSearchQuery,\n onSearchQueryChange: onControlledSearchQueryChange,\n onSearchingChange,\n onUploadingChange,\n}) => {\n const {\n searchQuery,\n searchResults,\n isSearching,\n searchImages,\n downloadAndUpload,\n uploadProgress,\n isUploading,\n clearProgress,\n hasMoreResults,\n loadMore,\n isLoadingMore,\n } = useUnsplashPicker(\n controlledSearchQuery !== undefined && onControlledSearchQueryChange\n ? {\n controlledSearchQuery,\n onControlledSearchQueryChange,\n }\n : undefined,\n );\n\n const [selectedImageId, setSelectedImageId] = useState<string | null>(null);\n\n useEffect(() => {\n const timeoutId = setTimeout(() => {\n if (searchQuery.trim()) {\n searchImages(searchQuery);\n }\n }, 500);\n\n return () => clearTimeout(timeoutId);\n }, [searchQuery, searchImages]);\n\n useEffect(() => {\n onSearchingChange?.(isSearching);\n }, [isSearching, onSearchingChange]);\n\n useEffect(() => {\n onUploadingChange?.(isUploading);\n }, [isUploading, onUploadingChange]);\n\n const handleImageSelect = async (image: UnsplashImage) => {\n setSelectedImageId(image.id);\n clearProgress();\n\n const result = await downloadAndUpload(image);\n\n if (result && result.upload_status === \"ready\") {\n setSelectedImageId(null);\n onFileSelected(result);\n } else {\n setSelectedImageId(null);\n }\n };\n\n return (\n <div className=\"space-y-4\">\n {isSearching && (\n <div className=\"py-4 text-center\">\n <p className=\"text-gray-500\">Searching Unsplash...</p>\n </div>\n )}\n\n {searchResults.length > 0 && (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4\">\n {searchResults.map((image) => (\n <div\n key={image.id}\n className={`group relative cursor-pointer overflow-hidden rounded-lg border-2 transition-all ${selectedImageId === image.id ? \"border-blue-500\" : \"border-transparent hover:border-gray-300\"} ${isUploading && selectedImageId === image.id ? \"opacity-50\" : \"\"} `}\n onClick={() => !isUploading && handleImageSelect(image)}\n >\n <img\n src={image.urls.small}\n alt={\n image.alt_description || image.description || \"Unsplash image\"\n }\n width={400}\n height={300}\n className=\"h-32 w-full object-cover\"\n onError={(e) => {\n const target = e.target as HTMLImageElement;\n target.src = image.urls.thumb;\n }}\n />\n\n <div\n className=\"group-hover:bg-opacity-30 absolute inset-0 flex items-center justify-center transition-all\"\n style={{\n backgroundColor: \"rgba(0, 0, 0, 0)\",\n }}\n >\n <div className=\"opacity-0 transition-opacity group-hover:opacity-100\">\n <DownloadIcon className=\"h-6 w-6 text-white\" />\n </div>\n </div>\n\n <div className=\"absolute right-0 bottom-0 left-0 bg-linear-to-t from-black to-transparent p-2\">\n <div className=\"flex items-center space-x-1 text-xs text-white\">\n <UserIcon className=\"h-3 w-3\" />\n <span>\n Photo by{\" \"}\n <a\n href={`https://unsplash.com/@${image.user.username}?utm_source=fluid&utm_medium=referral`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"underline hover:text-gray-200\"\n onClick={(e) => e.stopPropagation()}\n >\n {image.user.name}\n </a>{\" \"}\n on{\" \"}\n <a\n href=\"https://unsplash.com/?utm_source=fluid&utm_medium=referral\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"underline hover:text-gray-200\"\n onClick={(e) => e.stopPropagation()}\n >\n Unsplash\n </a>\n </span>\n </div>\n </div>\n\n {selectedImageId === image.id && isUploading && (\n <div className=\"bg-opacity-75 absolute inset-0 flex items-center justify-center bg-blue-500\">\n <div className=\"text-center text-white\">\n <DownloadIcon className=\"mb-1 h-6 w-6 animate-pulse\" />\n <p className=\"text-xs\">Uploading...</p>\n </div>\n </div>\n )}\n </div>\n ))}\n </div>\n )}\n\n {searchQuery && !isSearching && searchResults.length === 0 && (\n <div className=\"py-8 text-center\">\n <p className=\"text-gray-500\">\n No images found for "{searchQuery}"\n </p>\n </div>\n )}\n\n {hasMoreResults && searchResults.length > 0 && (\n <div className=\"flex justify-center pt-4\">\n <Button onClick={loadMore} disabled={isLoadingMore} variant=\"outline\">\n {isLoadingMore ? (\n <>\n <LoaderIcon className=\"mr-2 animate-spin\" />\n Loading...\n </>\n ) : (\n <>\n <DownloadIcon className=\"mr-2\" />\n Load More\n </>\n )}\n </Button>\n </div>\n )}\n\n {config.showProgress && uploadProgress.length > 0 && (\n <div className=\"space-y-2\">\n <h4 className=\"text-sm font-medium text-gray-900\">Upload Progress</h4>\n {uploadProgress.map((progress) => (\n <div\n key={`${progress.file_name}-${progress.status}`}\n className=\"rounded-lg bg-gray-50 p-3\"\n >\n <div className=\"mb-2 flex items-center justify-between\">\n <div className=\"flex items-center space-x-2\">\n {(() => {\n const Icon =\n progress.status === \"ready\"\n ? CheckIcon\n : progress.status === \"error\"\n ? AlertTriangleIcon\n : DownloadIcon;\n return (\n <Icon\n className={`h-4 w-4 ${\n progress.status === \"ready\"\n ? \"text-green-500\"\n : progress.status === \"error\"\n ? \"text-red-500\"\n : \"text-blue-500\"\n }`}\n />\n );\n })()}\n <span className=\"text-sm font-medium text-gray-900\">\n {progress.file_name}\n </span>\n </div>\n <span className=\"text-xs text-gray-500 capitalize\">\n {progress.status}\n </span>\n </div>\n\n {progress.status === \"uploading\" && (\n <div className=\"h-2 w-full rounded-full bg-gray-200\">\n <div\n className=\"h-2 rounded-full bg-blue-600 transition-all duration-300\"\n style={{ width: `${progress.progress}%` }}\n />\n </div>\n )}\n\n {progress.error && (\n <p className=\"mt-1 text-xs text-red-600\">{progress.error}</p>\n )}\n </div>\n ))}\n </div>\n )}\n\n <div className=\"text-center text-xs text-gray-400\">\n Photos provided by{\" \"}\n <a\n href=\"https://unsplash.com\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"underline hover:text-gray-600\"\n >\n Unsplash\n </a>\n </div>\n </div>\n );\n};\n","import { useState, useCallback } from \"react\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport {\n createDamAsset,\n damQueryKeys,\n proxyUrlFetch,\n} from \"@fluid-app/file-picker-api-client\";\nimport {\n getDamAssetUrl,\n sanitizeFilename,\n getSanitizedAssetName,\n type FilePickerResult,\n type UploadProgress,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\n\nexport const useUrlUpload = (): {\n uploadFromUrl: (url: string, fileName?: string) => Promise<FilePickerResult>;\n uploadProgress: UploadProgress[];\n isUploading: boolean;\n clearProgress: () => void;\n} => {\n const [uploadProgress, setUploadProgress] = useState<\n Map<string, UploadProgress>\n >(new Map());\n const queryClient = useQueryClient();\n const { apiClient, companyId, apiBaseUrl, toast } = useFilePickerContext();\n\n const uploadMutation = useMutation({\n mutationFn: (params: Parameters<typeof createDamAsset>[1]) =>\n createDamAsset(apiClient.fetchClient, params, apiClient.uploadStrategy),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: damQueryKeys.all });\n },\n });\n\n const uploadFromUrl = useCallback(\n async (url: string, fileName?: string): Promise<FilePickerResult> => {\n const uploadId = `url-${Date.now()}`;\n const displayName =\n fileName ||\n url.split(\"/\").pop()?.split(\"?\")[0]?.split(\"#\")[0] ||\n \"url-upload\";\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: displayName,\n progress: 0,\n status: \"uploading\",\n });\n return next;\n });\n\n try {\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: displayName,\n progress: 25,\n status: \"uploading\",\n });\n return next;\n });\n\n let blob: Blob;\n let contentType: string;\n\n try {\n const response = await fetch(url, {\n mode: \"cors\",\n credentials: \"omit\",\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch file: ${response.statusText}`);\n }\n\n blob = await response.blob();\n contentType =\n response.headers.get(\"content-type\") || \"application/octet-stream\";\n } catch (fetchError) {\n const isCorsError =\n fetchError instanceof TypeError &&\n (fetchError.message.includes(\"CORS\") ||\n fetchError.message.includes(\"Failed to fetch\") ||\n fetchError.message.includes(\"NetworkError\"));\n\n if (isCorsError) {\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: displayName,\n progress: 30,\n status: \"uploading\",\n });\n return next;\n });\n\n try {\n const proxyResponse = await proxyUrlFetch(\n url,\n apiClient.proxyEndpoint,\n );\n const binaryString = atob(proxyResponse.data);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n blob = new Blob([bytes], { type: proxyResponse.contentType });\n contentType = proxyResponse.contentType;\n } catch (proxyError) {\n let origin = url;\n try {\n origin = new URL(url).origin;\n } catch {\n // fallback to raw url\n }\n throw new Error(\n `CORS error: The server at ${origin} does not allow cross-origin requests, and the proxy also failed: ${\n proxyError instanceof Error\n ? proxyError.message\n : \"Unknown error\"\n }`,\n );\n }\n } else {\n throw fetchError;\n }\n }\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: displayName,\n progress: 50,\n status: \"uploading\",\n });\n return next;\n });\n const rawFileName = displayName;\n const finalFileName = sanitizeFilename(rawFileName);\n\n const file = new File([blob], finalFileName, { type: contentType });\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: finalFileName,\n progress: 75,\n status: \"uploading\",\n });\n return next;\n });\n\n const damResponse = await uploadMutation.mutateAsync({\n file,\n name: getSanitizedAssetName(finalFileName),\n description: `Uploaded from URL: ${url}`,\n companyId,\n });\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: finalFileName,\n progress: 100,\n status: \"ready\",\n });\n return next;\n });\n\n const asset = damResponse.asset;\n const fileUrl = getDamAssetUrl(asset, apiBaseUrl);\n\n return {\n asset_id: asset.id,\n asset_code: asset.code,\n file_path: fileUrl,\n file_url: fileUrl,\n metadata: {\n file_name: finalFileName,\n mime_type: contentType,\n file_size: blob.size,\n },\n upload_status: \"ready\",\n variant_id: asset.default_variant_id\n ? asset.default_variant_id.toString()\n : undefined,\n };\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : \"Upload failed\";\n toast.error(errorMessage);\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(uploadId, {\n file_name: displayName,\n progress: 0,\n status: \"error\",\n error: errorMessage,\n });\n return next;\n });\n\n return {\n asset_id: 0,\n asset_code: \"\",\n file_path: \"\",\n file_url: \"\",\n metadata: {\n file_name: displayName,\n mime_type: \"\",\n },\n upload_status: \"error\",\n };\n }\n },\n [uploadMutation, companyId, apiBaseUrl, apiClient.proxyEndpoint, toast],\n );\n\n const clearProgress = useCallback(() => {\n setUploadProgress(new Map());\n }, []);\n\n return {\n uploadFromUrl,\n uploadProgress: Array.from(uploadProgress.values()),\n isUploading: uploadMutation.isPending,\n clearProgress,\n };\n};\n","import React, { useState } from \"react\";\nimport { Input, Switch, Button } from \"@fluid-app/ui-primitives\";\nimport {\n LinkIcon,\n CheckIcon,\n AlertTriangleIcon,\n LoaderIcon,\n FileIcon,\n ArrowRightIcon,\n} from \"lucide-react\";\nimport { useUrlUpload } from \"../hooks/use-url-upload\";\nimport {\n isFileTypeAccepted,\n getAcceptedTypesDescription,\n} from \"@fluid-app/file-picker-core\";\nimport type {\n FilePickerConfig,\n FilePickerResult,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\n\ninterface UrlUploadProps {\n config: FilePickerConfig;\n onFileSelected: (result: FilePickerResult) => void;\n enableShareableOption?: boolean;\n shareableEnabled?: boolean;\n onShareableChange?: (enabled: boolean) => void;\n /** When provided (e.g. FilePicker with selection panel), progress is only shown when there are selected files or an upload is in progress */\n selectedFilesCount?: number;\n}\n\nexport const UrlUpload: React.FC<UrlUploadProps> = ({\n config,\n onFileSelected,\n enableShareableOption = false,\n shareableEnabled = false,\n onShareableChange,\n selectedFilesCount,\n}) => {\n const { toast } = useFilePickerContext();\n const [url, setUrl] = useState(\"\");\n const [fileName, setFileName] = useState(\"\");\n const { uploadFromUrl, uploadProgress, isUploading, clearProgress } =\n useUrlUpload();\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!url.trim()) return;\n\n clearProgress();\n\n try {\n // Validate file type if restrictions are configured\n if (config.accept && config.accept.length > 0) {\n try {\n // Fetch the file to check its MIME type\n const response = await fetch(url.trim(), {\n method: \"HEAD\",\n mode: \"cors\",\n credentials: \"omit\",\n });\n const contentType = response.headers.get(\"content-type\") || \"\";\n const finalFileName =\n fileName.trim() || getFileNameFromUrl(url.trim());\n\n if (!isFileTypeAccepted(contentType, finalFileName, config.accept)) {\n const acceptedTypesDesc = getAcceptedTypesDescription(\n config.accept,\n );\n toast.error(\n `This file type is not allowed. Please upload ${acceptedTypesDesc.toLowerCase()}.`,\n );\n return;\n }\n } catch (error) {\n // If we can't check the file type due to CORS, proceed with upload\n // The server will handle validation, but warn the user about potential CORS issues\n if (\n error instanceof TypeError &&\n (error.message.includes(\"CORS\") ||\n error.message.includes(\"Failed to fetch\") ||\n error.message.includes(\"NetworkError\"))\n ) {\n // CORS error - we'll proceed but the actual upload might also fail\n // The uploadFromUrl function will handle the error more gracefully\n }\n // For other errors, proceed with upload - the server will handle validation\n }\n }\n\n const result = await uploadFromUrl(\n url.trim(),\n fileName.trim() || undefined,\n );\n\n // Only call onFileSelected if the upload was successful\n // If there's an error, the error will be shown in the progress section\n // and the modal should stay open so the user can see it\n if (result.upload_status === \"ready\") {\n onFileSelected(result);\n setUrl(\"\");\n setFileName(\"\");\n } else {\n // Explicitly do nothing on error - the error is already shown in progress\n // This ensures the modal stays open\n return;\n }\n } catch (error) {\n // Catch any unexpected errors to prevent the modal from closing\n // The error should already be handled by uploadFromUrl and shown in progress\n // This is just a safety net to ensure the modal stays open\n toast.error(error instanceof Error ? error.message : \"Unknown error\");\n }\n };\n\n const isValidUrl = (urlString: string): boolean => {\n try {\n new URL(urlString);\n return true;\n } catch {\n return false;\n }\n };\n\n const getFileNameFromUrl = (urlString: string): string => {\n try {\n const urlObj = new URL(urlString);\n const pathname = urlObj.pathname;\n const filename = pathname.split(\"/\").pop() || \"\";\n return filename.split(\"?\")[0] || \"\";\n } catch {\n return \"\";\n }\n };\n\n const handleUrlChange = (value: string) => {\n setUrl(value);\n if (value && isValidUrl(value) && !fileName) {\n const suggestedName = getFileNameFromUrl(value);\n if (suggestedName) {\n setFileName(suggestedName);\n }\n }\n };\n\n return (\n <div className=\"mx-auto max-w-5xl space-y-6 overflow-y-auto md:px-10 md:py-8\">\n {/* Enhanced header with better visual hierarchy */}\n <div className=\"text-center\">\n <div className=\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-gray-100\">\n <LinkIcon className=\"h-6 w-6 text-blue-600\" />\n </div>\n <h2 className=\"text-xl font-semibold text-gray-900\">Upload from URL</h2>\n </div>\n\n {/* Enhanced form with better interaction design */}\n\n <form onSubmit={handleSubmit} className=\"mx-auto w-1/2 space-y-5\">\n <div className=\"flex gap-2\">\n <div className=\"relative flex-1\">\n <Input\n id=\"url\"\n type=\"url\"\n placeholder=\"https://example.com/image.jpg\"\n value={url}\n onChange={(e) => handleUrlChange(e.target.value)}\n disabled={isUploading}\n required\n className={`w-full pr-10 transition-all duration-200 ${\n url && !isValidUrl(url)\n ? \"border-red-300 bg-red-50/30 focus:border-red-500 focus:ring-red-500/20\"\n : \"focus:border-blue-500 focus:ring-blue-500/20\"\n }`}\n />\n {url && (\n <div className=\"absolute top-1/2 right-3 -translate-y-1/2\">\n {isValidUrl(url) ? (\n <div className=\"flex h-5 w-5 items-center justify-center rounded-full bg-green-100\">\n <CheckIcon className=\"h-3 w-3 text-green-600\" />\n </div>\n ) : (\n <div className=\"flex h-5 w-5 items-center justify-center rounded-full bg-red-100\">\n <AlertTriangleIcon className=\"h-3 w-3 text-red-600\" />\n </div>\n )}\n </div>\n )}\n </div>\n {url.trim() && isValidUrl(url) && (\n <Button variant=\"default\" type=\"submit\" disabled={isUploading}>\n {isUploading ? (\n <>\n <LoaderIcon className=\"h-3 w-3 animate-spin\" />\n </>\n ) : (\n <>\n <ArrowRightIcon className=\"h-3 w-3\" />\n </>\n )}\n </Button>\n )}\n </div>\n {url && !isValidUrl(url) && (\n <p className=\"text-xs text-red-600\">Please enter a valid URL</p>\n )}\n\n {/* Auto-suggest filename section */}\n {url && isValidUrl(url) && (\n <div className=\"space-y-2\">\n <label\n htmlFor=\"fileName\"\n className=\"flex items-center gap-2 text-sm font-medium text-gray-900\"\n >\n <FileIcon className=\"h-3.5 w-3.5 text-gray-400\" />\n File Name\n <span className=\"text-xs font-normal text-gray-500\">\n (optional)\n </span>\n </label>\n <Input\n id=\"fileName\"\n type=\"text\"\n placeholder={\n getFileNameFromUrl(url) ||\n \"Leave empty to use original filename\"\n }\n value={fileName}\n onChange={(e) => setFileName(e.target.value)}\n disabled={isUploading}\n className=\"w-full transition-all duration-200 focus:border-blue-500 focus:ring-blue-500/20\"\n />\n {getFileNameFromUrl(url) && !fileName && (\n <p className=\"text-xs text-gray-500\">\n Will use filename:{\" \"}\n <span className=\"font-medium\">{getFileNameFromUrl(url)}</span>\n </p>\n )}\n </div>\n )}\n </form>\n\n {/* Enhanced progress feedback */}\n {config.showProgress &&\n uploadProgress.length > 0 &&\n (selectedFilesCount === undefined ||\n selectedFilesCount > 0 ||\n isUploading) && (\n <div className=\"space-y-4\">\n {uploadProgress.map((progress, index) => (\n <div\n key={index}\n className=\"mx-auto w-1/2 overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm\"\n >\n <div className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div\n className={`flex h-10 w-10 items-center justify-center rounded-xl ${\n progress.status === \"ready\"\n ? \"bg-green-100\"\n : progress.status === \"error\"\n ? \"bg-red-100\"\n : \"bg-blue-100\"\n }`}\n >\n {(() => {\n const Icon =\n progress.status === \"ready\"\n ? CheckIcon\n : progress.status === \"error\"\n ? AlertTriangleIcon\n : LoaderIcon;\n return (\n <Icon\n className={`h-5 w-5 ${\n progress.status === \"ready\"\n ? \"text-green-600\"\n : progress.status === \"error\"\n ? \"text-red-600\"\n : \"animate-spin text-blue-600\"\n }`}\n />\n );\n })()}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-gray-900\">\n {progress.file_name}\n </p>\n <div className=\"mt-1 flex items-center gap-2\">\n <p className=\"text-xs text-gray-500 capitalize\">\n {progress.status === \"uploading\"\n ? \"Uploading...\"\n : progress.status === \"ready\"\n ? \"Complete\"\n : progress.status}\n </p>\n {progress.status === \"uploading\" && (\n <span className=\"text-xs font-medium text-blue-600\">\n {progress.progress}%\n </span>\n )}\n </div>\n </div>\n </div>\n\n {progress.status === \"uploading\" && (\n <div className=\"mt-3\">\n <div className=\"h-2 w-full overflow-hidden rounded-full bg-gray-100\">\n <div\n className=\"h-2 rounded-full bg-linear-to-r from-blue-500 to-blue-600 transition-all duration-500 ease-out\"\n style={{ width: `${progress.progress}%` }}\n />\n </div>\n </div>\n )}\n\n {progress.error && (\n <div className=\"mt-3 rounded-lg bg-red-50 p-3\">\n <p className=\"text-sm text-red-700\">{progress.error}</p>\n </div>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n\n {/* Footer with Shareable Option */}\n {enableShareableOption && (\n <div className=\"mx-auto flex w-1/2 items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Switch\n checked={shareableEnabled}\n onCheckedChange={onShareableChange}\n id=\"shareable-toggle-url\"\n />\n <label\n htmlFor=\"shareable-toggle-url\"\n className=\"text-sm text-gray-700\"\n >\n Create as shareable media\n </label>\n </div>\n </div>\n )}\n </div>\n );\n};\n","import { useState, useCallback } from \"react\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport {\n createDamAsset,\n damQueryKeys,\n} from \"@fluid-app/file-picker-api-client\";\nimport {\n getDamAssetUrl,\n getSanitizedAssetName,\n getFileMimeType,\n type FilePickerResult,\n type UploadProgress,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\n\nexport const useComputerUpload = (): {\n uploadFiles: (files: File[]) => Promise<FilePickerResult[]>;\n uploadProgress: UploadProgress[];\n isUploading: boolean;\n clearProgress: () => void;\n} => {\n const [uploadProgress, setUploadProgress] = useState<\n Map<string, UploadProgress>\n >(new Map());\n const queryClient = useQueryClient();\n const { apiClient, companyId, toast, apiBaseUrl } = useFilePickerContext();\n\n const uploadMutation = useMutation({\n mutationFn: (params: Parameters<typeof createDamAsset>[1]) =>\n createDamAsset(apiClient.fetchClient, params, apiClient.uploadStrategy),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: damQueryKeys.all });\n },\n });\n\n const uploadFiles = useCallback(\n async (files: File[]): Promise<FilePickerResult[]> => {\n const results: FilePickerResult[] = [];\n\n for (const file of files) {\n const fileId = `${file.name}-${Date.now()}`;\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(fileId, {\n file_name: file.name,\n progress: 0,\n status: \"uploading\",\n });\n return next;\n });\n\n try {\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(fileId, {\n file_name: file.name,\n progress: 50,\n status: \"uploading\",\n });\n return next;\n });\n\n const response = await uploadMutation.mutateAsync({\n file,\n name: getSanitizedAssetName(file.name),\n description: `Uploaded via FilePicker: ${file.name}`,\n companyId,\n });\n\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(fileId, {\n file_name: file.name,\n progress: 100,\n status: \"ready\",\n });\n return next;\n });\n\n const asset = response.asset;\n const fileUrl = getDamAssetUrl(asset, apiBaseUrl);\n\n results.push({\n asset_id: asset.id,\n asset_code: asset.code,\n file_path: fileUrl,\n file_url: fileUrl,\n metadata: {\n file_name: file.name,\n mime_type: getFileMimeType(file),\n file_size: file.size,\n },\n upload_status: \"ready\",\n variant_id: asset.default_variant_id\n ? asset.default_variant_id.toString()\n : undefined,\n });\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : \"Upload failed\";\n const status =\n error instanceof Error && \"status\" in error\n ? (error as { status: number }).status\n : undefined;\n const isFileSizeError =\n status === 413 ||\n (status === 400 &&\n errorMessage.toLowerCase().includes(\"file size\"));\n if (isFileSizeError) {\n toast.error(\n \"File size too large: Please compress or choose another file\",\n );\n } else {\n toast.error(errorMessage);\n }\n setUploadProgress((prev) => {\n const next = new Map(prev);\n next.set(fileId, {\n file_name: file.name,\n progress: 0,\n status: \"error\",\n error: errorMessage,\n });\n return next;\n });\n\n results.push({\n asset_id: 0,\n asset_code: \"\",\n file_path: \"\",\n file_url: \"\",\n metadata: {\n file_name: file.name,\n mime_type: getFileMimeType(file),\n file_size: file.size,\n },\n upload_status: \"error\",\n });\n }\n }\n\n return results;\n },\n [uploadMutation, companyId, toast, apiBaseUrl],\n );\n\n const clearProgress = useCallback(() => {\n setUploadProgress(new Map());\n }, []);\n\n return {\n uploadFiles,\n uploadProgress: Array.from(uploadProgress.values()),\n isUploading: uploadMutation.isPending,\n clearProgress,\n };\n};\n","import React, {\n useState,\n useRef,\n useCallback,\n useEffect,\n useMemo,\n} from \"react\";\nimport ReactCrop, { type Crop, type PixelCrop } from \"react-image-crop\";\nimport \"react-image-crop/dist/ReactCrop.css\";\nimport {\n Button,\n Dialog,\n DialogContent,\n DialogTitle,\n Slider,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"@fluid-app/ui-primitives\";\nimport {\n MoveHorizontalIcon,\n RotateCwIcon,\n ChevronDownIcon,\n MinusIcon,\n PlusIcon,\n} from \"lucide-react\";\nimport type {\n ImageCropModalProps,\n AspectValue,\n} from \"@fluid-app/file-picker-core\";\nimport {\n ASPECT_OPTIONS,\n ROTATION_MIN,\n ROTATION_MAX,\n computeInitialCrop,\n getCroppedImgFromViewport,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\n\nexport const ImageCropModal: React.FC<ImageCropModalProps> = ({\n isOpen,\n onClose,\n onCrop,\n imageFile,\n aspectRatio: aspectRatioProp,\n minWidth = 50,\n minHeight = 50,\n maxWidth = 2000,\n maxHeight = 2000,\n}) => {\n const { toast } = useFilePickerContext();\n const [crop, setCrop] = useState<Crop>();\n const [completedCrop, setCompletedCrop] = useState<PixelCrop>();\n const [imgSrc, setImgSrc] = useState<string>(\"\");\n const [isProcessing, setIsProcessing] = useState(false);\n const [aspectOption, setAspectOption] = useState<AspectValue>(\n aspectRatioProp ?? \"free\",\n );\n const [rotation, setRotation] = useState(0);\n const [flipHorizontal, setFlipHorizontal] = useState(false);\n const [zoom, setZoom] = useState(1);\n const [cropModified, setCropModified] = useState(false);\n const [cropViewportWidthPx, setCropViewportWidthPx] = useState<number | null>(\n null,\n );\n const imgRef = useRef<HTMLImageElement>(null);\n const cropContainerRef = useRef<HTMLDivElement>(null);\n const cropWrapperRef = useRef<HTMLDivElement>(null);\n\n const effectiveAspect = aspectOption === \"free\" ? undefined : aspectOption;\n\n const outputDimensions = useMemo(() => {\n const img = imgRef.current;\n if (!completedCrop || !img) return null;\n const nw = img.naturalWidth;\n const nh = img.naturalHeight;\n const vw = img.clientWidth;\n const vh = img.clientHeight;\n if (!vw || !vh) return null;\n const upscale = Math.max(nw / vw, nh / vh);\n return {\n width: Math.floor(completedCrop.width * upscale),\n height: Math.floor(completedCrop.height * upscale),\n };\n }, [completedCrop]);\n\n const initialAspect = aspectRatioProp ?? \"free\";\n const hasChanges =\n rotation !== 0 ||\n flipHorizontal !== false ||\n zoom !== 1 ||\n aspectOption !== initialAspect ||\n cropModified;\n\n useEffect(() => {\n if (isOpen && imageFile) {\n const reader = new FileReader();\n reader.addEventListener(\"load\", () => {\n setImgSrc(reader.result as string);\n });\n reader.readAsDataURL(imageFile);\n }\n if (!isOpen) {\n setRotation(0);\n setFlipHorizontal(false);\n setZoom(1);\n setCropModified(false);\n setCropViewportWidthPx(null);\n setAspectOption(aspectRatioProp ?? \"free\");\n }\n }, [isOpen, imageFile, aspectRatioProp]);\n\n /** Apply crop + completedCrop for given aspect, reading container dimensions. */\n const applyCropForAspect = useCallback((aspect: number | undefined) => {\n const img = imgRef.current;\n if (!img) return;\n const { crop: newCrop, completedCrop: newCompleted } = computeInitialCrop(\n img.clientWidth,\n img.clientHeight,\n aspect,\n );\n setCrop(newCrop);\n setTimeout(() => setCompletedCrop(newCompleted), 0);\n }, []);\n\n const onImageLoad = useCallback(() => {\n if (!imgRef.current) return;\n applyCropForAspect(effectiveAspect);\n }, [effectiveAspect, applyCropForAspect]);\n\n const handleCrop = async () => {\n if (!imgRef.current) return;\n setIsProcessing(true);\n try {\n const vw = imgRef.current.clientWidth;\n const vh = imgRef.current.clientHeight;\n\n let px: number, py: number, pw: number, ph: number;\n if (completedCrop) {\n px = completedCrop.x;\n py = completedCrop.y;\n pw = completedCrop.width;\n ph = completedCrop.height;\n } else if (crop) {\n if (crop.unit === \"%\") {\n px = (crop.x / 100) * vw;\n py = (crop.y / 100) * vh;\n pw = (crop.width / 100) * vw;\n ph = (crop.height / 100) * vh;\n } else {\n px = (crop as PixelCrop).x;\n py = (crop as PixelCrop).y;\n pw = (crop as PixelCrop).width;\n ph = (crop as PixelCrop).height;\n }\n } else {\n px = 0;\n py = 0;\n pw = vw;\n ph = vh;\n }\n\n // Use viewport-based export so rotation, flip, and zoom match the preview.\n // Pass sourceFile so the bitmap is decoded at full resolution from the\n // original bytes, not from the <img> element (which the browser may have\n // decoded at a reduced resolution due to its small CSS display size).\n const croppedFile = await getCroppedImgFromViewport(\n imgRef.current,\n vw,\n vh,\n { x: px, y: py, width: pw, height: ph },\n zoom,\n rotation,\n flipHorizontal,\n imageFile.name,\n imageFile.type,\n 1,\n imageFile,\n );\n await Promise.resolve(onCrop(croppedFile));\n onClose();\n } catch (error) {\n toast.error(\n error instanceof Error ? error.message : \"Failed to crop image\",\n );\n } finally {\n setIsProcessing(false);\n }\n };\n\n const resetCrop = () => {\n setRotation(0);\n setFlipHorizontal(false);\n setZoom(1);\n setCropModified(false);\n setAspectOption(aspectRatioProp ?? \"free\");\n applyCropForAspect(aspectRatioProp ?? undefined);\n };\n\n const aspectLabel =\n ASPECT_OPTIONS.find(\n (o) =>\n o.value === aspectOption ||\n (typeof o.value === \"number\" && o.value === aspectOption),\n )?.label ?? \"Free\";\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()} modal>\n <DialogContent\n overlayClassName=\"bg-gray-500/75\"\n showCloseButton={false}\n className=\"border-border text-foreground z-400 flex h-[90vh]! w-[95vw]! max-w-[1600px]! flex-col overflow-auto border bg-white p-0\"\n >\n <DialogTitle className=\"sr-only\">Crop Image</DialogTitle>\n <div className=\"bg-muted/30 flex shrink-0 flex-wrap items-center justify-center gap-2 px-4 py-2\">\n <DropdownMenu>\n <DropdownMenuTrigger className=\"inline-flex items-center gap-1 rounded-md border border-gray-200 bg-white px-3 py-1.5 text-sm text-gray-700 shadow-sm transition-colors hover:bg-gray-50\">\n {aspectLabel}\n <ChevronDownIcon className=\"h-3 w-3\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"z-410\">\n {ASPECT_OPTIONS.map((opt) => (\n <DropdownMenuItem\n key={String(opt.value)}\n onClick={() => {\n setAspectOption(opt.value);\n applyCropForAspect(\n opt.value === \"free\" ? undefined : opt.value,\n );\n }}\n >\n {opt.label}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n onClick={() => setFlipHorizontal((f) => !f)}\n aria-label=\"Flip horizontal\"\n className=\"gap-1 bg-transparent\"\n >\n <MoveHorizontalIcon className=\"h-4 w-4\" />\n <span className=\"text-xs\">Flip</span>\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n onClick={() => {\n setRotation((r) => {\n const next = r + 90;\n if (next > ROTATION_MAX) return next - 360;\n if (next < ROTATION_MIN) return next + 360;\n return next;\n });\n }}\n aria-label=\"Rotate 90deg\"\n className=\"gap-1 bg-transparent\"\n >\n <RotateCwIcon className=\"h-4 w-4\" />\n <span className=\"text-xs\">Rotate</span>\n </Button>\n\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n type=\"button\"\n onClick={() =>\n setZoom((z) => Math.max(0.5, Math.round((z - 0.1) * 10) / 10))\n }\n className=\"bg-transparent\"\n aria-label=\"Zoom out\"\n >\n <MinusIcon className=\"h-3 w-3\" />\n </Button>\n <Slider\n min={0.5}\n max={2}\n step={0.1}\n value={[zoom]}\n onValueChange={([value]) => {\n if (value !== undefined) setZoom(value);\n }}\n className=\"accent-contrast h-2 w-24 shrink-0\"\n aria-label=\"Zoom\"\n />\n <Button\n variant=\"ghost\"\n size=\"icon\"\n type=\"button\"\n onClick={() =>\n setZoom((z) => Math.min(2, Math.round((z + 0.1) * 10) / 10))\n }\n className=\"bg-transparent\"\n aria-label=\"Zoom in\"\n >\n <PlusIcon className=\"h-3 w-3\" />\n </Button>\n <span\n className=\"text-muted-foreground min-w-10 text-xs\"\n aria-hidden\n >\n {Math.round(zoom * 100)}%\n </span>\n </div>\n </div>\n\n <div className=\"relative flex w-full flex-1 items-center justify-center overflow-auto p-4\">\n {imgSrc && (\n <div\n ref={cropWrapperRef}\n className=\"mx-auto\"\n style={{\n width:\n cropViewportWidthPx != null\n ? `${cropViewportWidthPx}px`\n : \"100%\",\n }}\n >\n <div\n ref={cropContainerRef}\n className=\"image-crop-modal-crop [&_.ReactCrop__crop-selection]:border-contrast [&_.ReactCrop__rule]:border-border flex w-full items-center justify-center [&_.ReactCrop__crop-selection]:border-2 [&_.ReactCrop__crop-selection]:border-dashed [&_.ReactCrop__crop-selection]:shadow-[0_0_0_9999px_rgba(0,0,0,0.4)]\"\n >\n <ReactCrop\n crop={crop}\n onChange={(pixelCrop, percentCrop) => {\n setCrop(percentCrop);\n setCompletedCrop(pixelCrop);\n setCropModified(true);\n }}\n onComplete={(c) => setCompletedCrop(c)}\n aspect={effectiveAspect}\n minWidth={minWidth}\n minHeight={minHeight}\n maxWidth={maxWidth}\n maxHeight={maxHeight}\n >\n <img\n ref={imgRef}\n alt=\"Crop\"\n src={imgSrc}\n className=\"block max-h-full max-w-full object-contain sm:h-[300px] xl:h-[500px]\"\n style={{\n transform: [\n `rotate(${rotation}deg)`,\n `scale(${zoom})`,\n flipHorizontal ? \"scaleX(-1)\" : null,\n ]\n .filter(Boolean)\n .join(\" \"),\n transformOrigin: \"center center\",\n }}\n onLoad={onImageLoad}\n />\n </ReactCrop>\n </div>\n </div>\n )}\n </div>\n\n <div className=\"bg-muted/30 flex shrink-0 flex-col gap-3 px-4 py-3\">\n <div className=\"flex justify-center\">\n <span className=\"text-muted-foreground text-xs\" aria-hidden>\n {outputDimensions\n ? `${outputDimensions.width} x ${outputDimensions.height} px · ${rotation}deg`\n : `${rotation}deg`}\n </span>\n </div>\n <div className=\"flex items-center justify-end gap-2\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={resetCrop}\n disabled={isProcessing || !hasChanges}\n className=\"border-0 shadow-none\"\n >\n Reset to Default\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={onClose}\n disabled={isProcessing}\n className=\"border-0 shadow-none\"\n >\n Cancel\n </Button>\n <Button\n variant=\"default\"\n size=\"sm\"\n onClick={handleCrop}\n disabled={!crop || isProcessing}\n className=\"border-0\"\n >\n {isProcessing ? \"Processing...\" : \"Apply Crop\"}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n );\n};\n","import React, { useCallback, useState } from \"react\";\nimport { useDropzone, type FileRejection } from \"react-dropzone\";\nimport { Button, Card, CardContent, Switch } from \"@fluid-app/ui-primitives\";\nimport {\n CheckIcon,\n AlertTriangleIcon,\n LoaderIcon,\n ArrowUpIcon,\n} from \"lucide-react\";\nimport { useComputerUpload } from \"../hooks/use-computer-upload\";\nimport { ImageCropModal } from \"./ImageCropModal\";\nimport { getFileMimeType } from \"@fluid-app/file-picker-core\";\nimport type {\n FilePickerConfig,\n FilePickerResult,\n} from \"@fluid-app/file-picker-core\";\nimport { useFilePickerContext } from \"../context/FilePickerContext\";\n\nfunction formatFileSize(bytes: number): string {\n if (bytes === 0) return \"0 Bytes\";\n const k = 1024;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + \" \" + sizes[i];\n}\n\ninterface ComputerUploadProps {\n config: FilePickerConfig;\n onFilesSelected: (results: FilePickerResult[]) => void;\n enableShareableOption?: boolean;\n shareableEnabled?: boolean;\n onShareableChange?: (enabled: boolean) => void;\n skipCropper?: boolean;\n selectedFilesCount?: number;\n}\n\nexport const ComputerUpload: React.FC<ComputerUploadProps> = ({\n config,\n onFilesSelected,\n enableShareableOption = false,\n shareableEnabled = false,\n onShareableChange,\n skipCropper = false,\n selectedFilesCount,\n}) => {\n const { toast } = useFilePickerContext();\n const { uploadFiles, uploadProgress, isUploading, clearProgress } =\n useComputerUpload();\n const [dragActive, setDragActive] = useState(false);\n const [cropModalOpen, setCropModalOpen] = useState(false);\n const [pendingImageFile, setPendingImageFile] = useState<File | null>(null);\n const [pendingFiles, setPendingFiles] = useState<File[]>([]);\n\n const handleDrop = useCallback(\n async (acceptedFiles: File[]) => {\n setDragActive(false);\n clearProgress();\n\n const filesToUpload = config.maxFiles\n ? acceptedFiles.slice(0, config.maxFiles)\n : acceptedFiles;\n\n // Check if any files are images that can be cropped\n // HEIC/HEIF are excluded because <canvas> cannot render them natively\n const UNCROPABLE_TYPES = [\n \"image/heic\",\n \"image/heic-sequence\",\n \"image/heif\",\n \"image/svg+xml\",\n ];\n const isCropableImage = (file: File) => {\n const mime = getFileMimeType(file);\n return mime.startsWith(\"image/\") && !UNCROPABLE_TYPES.includes(mime);\n };\n const imageFiles = filesToUpload.filter(isCropableImage);\n const nonImageFiles = filesToUpload.filter((f) => !isCropableImage(f));\n\n if (imageFiles.length === 1 && !skipCropper) {\n // Only show crop tool for single image uploads\n // Store non-image files for later upload\n setPendingFiles(nonImageFiles);\n // Open crop modal for the single image\n setPendingImageFile(imageFiles[0] || null);\n setCropModalOpen(true);\n } else {\n // Multiple files or no images to crop or cropper is disabled, upload directly\n const results = await uploadFiles(filesToUpload);\n onFilesSelected(results);\n }\n },\n [uploadFiles, onFilesSelected, config.maxFiles, clearProgress, skipCropper],\n );\n\n const handleCropComplete = useCallback(\n async (croppedFile: File) => {\n setCropModalOpen(false);\n\n // Combine cropped file with any pending non-image files\n const allFiles = [croppedFile, ...pendingFiles];\n const results = await uploadFiles(allFiles);\n onFilesSelected(results);\n\n // Reset state\n setPendingImageFile(null);\n setPendingFiles([]);\n },\n [uploadFiles, onFilesSelected, pendingFiles],\n );\n\n const handleCropCancel = useCallback(() => {\n setCropModalOpen(false);\n setPendingImageFile(null);\n\n // Upload non-image files without cropping\n if (pendingFiles.length > 0) {\n uploadFiles(pendingFiles).then(onFilesSelected);\n }\n setPendingFiles([]);\n }, [uploadFiles, onFilesSelected, pendingFiles]);\n\n const handleDropRejected = useCallback(\n (fileRejections: FileRejection[]) => {\n fileRejections.forEach(({ file, errors }) => {\n errors.forEach((error) => {\n if (error.code === \"file-too-large\") {\n toast.error(\n `File size too large: \"${file.name}\" (${formatFileSize(file.size)}) exceeds the maximum allowed size.`,\n );\n } else if (error.code === \"file-invalid-type\") {\n toast.error(\n `Invalid file type: \"${file.name}\" is not an accepted file type.`,\n );\n } else if (error.code === \"too-many-files\") {\n toast.error(\n `Too many files: You can only upload ${config.maxFiles} file${config.maxFiles === 1 ? \"\" : \"s\"} at a time.`,\n );\n } else {\n toast.error(error.message);\n }\n });\n });\n },\n [config.maxFiles, toast],\n );\n\n const { getRootProps, getInputProps, isDragActive } = useDropzone({\n onDrop: handleDrop,\n onDropRejected: (fileRejections: FileRejection[]) =>\n handleDropRejected(fileRejections),\n accept: config.accept\n ? config.accept.reduce(\n (acc, type) => {\n // Handle both MIME types (e.g., \"image/*\") and file extensions (e.g., \".jpg\")\n if (type.startsWith(\".\")) {\n // File extension - add to a generic MIME type\n acc[\"*/*\"] = acc[\"*/*\"] || [];\n acc[\"*/*\"].push(type);\n } else {\n // MIME type - add as is\n acc[type] = [];\n }\n return acc;\n },\n {} as Record<string, string[]>,\n )\n : undefined,\n maxSize: config.maxSize,\n maxFiles: config.maxFiles,\n onDragEnter: () => setDragActive(true),\n onDragLeave: () => setDragActive(false),\n });\n\n return (\n <div className=\"mx-auto max-w-7xl space-y-6 overflow-y-auto md:px-10 md:py-8\">\n {/* Enhanced dropzone card */}\n <Card className=\"mx-auto w-1/2 border-none shadow-none\">\n <CardContent className=\"p-6\">\n <div\n {...getRootProps()}\n className={`group cursor-pointer rounded-lg border-2 border-dashed p-8 text-center transition-all duration-200 ${\n isDragActive || dragActive\n ? \"scale-[1.02] border-blue-500 bg-blue-50/50\"\n : \"border-gray-300 hover:border-blue-400 hover:bg-blue-50/20\"\n } ${isUploading ? \"pointer-events-none opacity-50\" : \"\"}`}\n >\n <input {...getInputProps()} />\n <div className=\"space-y-4\">\n {/* Header with icon moved inside dropzone */}\n <div className=\"text-center\">\n <div className=\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-gray-100 group-hover:bg-blue-200\">\n <ArrowUpIcon className=\"h-10 w-10 text-gray-600 group-hover:text-blue-500\" />\n </div>\n </div>\n\n {/* File constraints */}\n <p className=\"text-center text-xs text-gray-500\">\n 50MB for Images and Documents. 2GB for Videos.\n </p>\n\n <Button\n variant=\"default\"\n disabled={isUploading}\n className=\"transition-all duration-200\"\n >\n {isUploading ? (\n <>\n <LoaderIcon className=\"mr-2 h-4 w-4 animate-spin\" />\n Uploading...\n </>\n ) : (\n \"Choose Files\"\n )}\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n\n {/* Enhanced progress feedback */}\n {config.showProgress &&\n uploadProgress.length > 0 &&\n (selectedFilesCount === undefined ||\n selectedFilesCount > 0 ||\n isUploading) && (\n <div className=\"space-y-4\">\n {uploadProgress.map((progress) => (\n <div\n key={`${progress.file_name}-${progress.status}`}\n className=\"mx-auto w-1/2 overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm\"\n >\n <div className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <div\n className={`flex h-10 w-10 items-center justify-center rounded-xl ${\n progress.status === \"ready\"\n ? \"bg-green-100\"\n : progress.status === \"error\"\n ? \"bg-red-100\"\n : \"bg-blue-100\"\n }`}\n >\n {(() => {\n const Icon =\n progress.status === \"ready\"\n ? CheckIcon\n : progress.status === \"error\"\n ? AlertTriangleIcon\n : LoaderIcon;\n return (\n <Icon\n className={`h-5 w-5 ${\n progress.status === \"ready\"\n ? \"text-green-600\"\n : progress.status === \"error\"\n ? \"text-red-600\"\n : \"animate-spin text-blue-600\"\n }`}\n />\n );\n })()}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-gray-900\">\n {progress.file_name}\n </p>\n <div className=\"mt-1 flex items-center gap-2\">\n <p className=\"text-xs text-gray-500 capitalize\">\n {progress.status === \"uploading\"\n ? \"Uploading...\"\n : progress.status === \"ready\"\n ? \"Complete\"\n : progress.status}\n </p>\n {progress.status === \"uploading\" && (\n <span className=\"text-xs font-medium text-blue-600\">\n {progress.progress}%\n </span>\n )}\n </div>\n </div>\n </div>\n\n {progress.status === \"uploading\" && (\n <div className=\"mt-3\">\n <div className=\"h-2 w-full overflow-hidden rounded-full bg-gray-100\">\n <div\n className=\"h-2 rounded-full bg-linear-to-r from-blue-500 to-blue-600 transition-all duration-500 ease-out\"\n style={{ width: `${progress.progress}%` }}\n />\n </div>\n </div>\n )}\n\n {progress.error && (\n <div className=\"mt-3 rounded-lg bg-red-50 p-3\">\n <p className=\"text-sm text-red-700\">{progress.error}</p>\n </div>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n\n {/* Footer with Shareable Option */}\n {enableShareableOption && (\n <div className=\"mx-auto flex w-1/2 items-center justify-between px-4\">\n <div className=\"flex items-center gap-2\">\n <Switch\n checked={shareableEnabled}\n onCheckedChange={onShareableChange}\n id=\"shareable-toggle-computer\"\n />\n <label\n htmlFor=\"shareable-toggle-computer\"\n className=\"text-sm text-gray-700\"\n >\n Create as shareable media\n </label>\n </div>\n </div>\n )}\n\n {/* Image Crop Modal */}\n {pendingImageFile && (\n <ImageCropModal\n isOpen={cropModalOpen}\n onClose={handleCropCancel}\n onCrop={handleCropComplete}\n imageFile={pendingImageFile}\n minWidth={50}\n minHeight={50}\n maxWidth={2000}\n maxHeight={2000}\n />\n )}\n </div>\n );\n};\n","import React from \"react\";\nimport {\n GoogleIcon,\n InstagramIcon,\n FacebookIcon,\n TiktokIcon,\n DropboxIcon,\n} from \"../utils/brand-icons\";\nimport { DamLibrary, type DamLibraryRef } from \"./DamLibrary\";\nimport { MediaTab } from \"./MediaTab\";\nimport { UnsplashPicker } from \"./UnsplashPicker\";\nimport type {\n FilePickerConfig,\n FilePickerResult,\n UploadMethod,\n DamAsset,\n} from \"@fluid-app/file-picker-core\";\nimport type { ViewMode } from \"./FilePickerHeader\";\nimport type { SortOptionId } from \"@fluid-app/file-picker-core\";\nimport { UrlUpload } from \"./UrlUpload\";\nimport { ComputerUpload } from \"./ComputerUpload\";\nimport type { MediumResponse } from \"../types/media\";\n\nexport type { MediumResponse };\n\ninterface FilePickerContentProps {\n activeMethod: UploadMethod;\n config: FilePickerConfig;\n damTypeFilter?: string;\n sortOption?: SortOptionId;\n onFilesSelected: (results: FilePickerResult[]) => void;\n onUploadConfirmAndMaybeCreate: (results: FilePickerResult[]) => void;\n onSingleFileSelected: (result: FilePickerResult) => void;\n enableShareableOption?: boolean;\n shareableEnabled?: boolean;\n onShareableChange?: (enabled: boolean) => void;\n damLibraryRef: React.RefObject<DamLibraryRef | null>;\n onDamConfirmAndMaybeCreate: (results: FilePickerResult[]) => void;\n selectedMediaIds: number[];\n onMediaIdsChange: (ids: number[]) => void;\n onMediaSelectionChange: (count: number) => void;\n onMediaDataChange: (data: FilePickerResult[]) => void;\n onDamSelectionChange: (\n count: number,\n files?: FilePickerResult[],\n variantCounts?: Record<string, number>,\n ) => void;\n onDamSearchingChange?: (searching: boolean) => void;\n skipCropper?: boolean;\n damSearchQuery?: string;\n onDamSearchChange?: (value: string) => void;\n mediaSearchQuery?: string;\n onMediaSearchChange?: (value: string) => void;\n unsplashSearchQuery?: string;\n onUnsplashSearchChange?: (value: string) => void;\n onUnsplashSearchingChange?: (searching: boolean) => void;\n onUnsplashUploadingChange?: (uploading: boolean) => void;\n thumbnailSize?: number;\n viewMode?: ViewMode;\n showNamesOnMedia?: boolean;\n onDamFoldersChange?: (folders: string[]) => void;\n previewContainer?: HTMLDivElement | null;\n previewMedia?: MediumResponse | null;\n onPreviewMediaChange?: (media: MediumResponse | null) => void;\n previewAsset?: DamAsset | null;\n onPreviewAssetChange?: (asset: DamAsset | null) => void;\n uploadSelectionCount?: number;\n}\n\nexport const FilePickerContent: React.FC<FilePickerContentProps> = ({\n activeMethod,\n config,\n damTypeFilter,\n sortOption,\n onFilesSelected,\n onSingleFileSelected,\n enableShareableOption,\n shareableEnabled,\n onShareableChange,\n damLibraryRef,\n onDamConfirmAndMaybeCreate,\n selectedMediaIds,\n onMediaIdsChange,\n onMediaSelectionChange,\n onMediaDataChange,\n onDamSelectionChange,\n onDamSearchingChange,\n skipCropper = false,\n damSearchQuery = \"\",\n onDamSearchChange,\n mediaSearchQuery,\n onMediaSearchChange,\n unsplashSearchQuery,\n onUnsplashSearchChange,\n onUnsplashSearchingChange,\n onUnsplashUploadingChange,\n thumbnailSize = 100,\n viewMode = \"grid\",\n showNamesOnMedia = true,\n onDamFoldersChange,\n previewContainer,\n previewMedia,\n onPreviewMediaChange,\n previewAsset,\n onPreviewAssetChange,\n uploadSelectionCount,\n}) => {\n const damConfig = React.useMemo(\n () => ({\n ...config,\n fileTypeFilter: damTypeFilter ? [damTypeFilter] : config.fileTypeFilter,\n }),\n [config, damTypeFilter],\n );\n return (\n <div className=\"flex-1 overflow-x-hidden overflow-y-auto p-6\">\n {(activeMethod === \"upload\" || activeMethod === \"computer\") && (\n <ComputerUpload\n config={config}\n onFilesSelected={onFilesSelected}\n enableShareableOption={enableShareableOption}\n shareableEnabled={shareableEnabled}\n onShareableChange={onShareableChange}\n skipCropper={skipCropper}\n selectedFilesCount={uploadSelectionCount}\n />\n )}\n {activeMethod === \"url\" && (\n <UrlUpload\n config={config}\n onFileSelected={onSingleFileSelected}\n enableShareableOption={enableShareableOption}\n shareableEnabled={shareableEnabled}\n onShareableChange={onShareableChange}\n selectedFilesCount={uploadSelectionCount}\n />\n )}\n\n {activeMethod === (\"dam\" as UploadMethod) && (\n <DamLibrary\n ref={damLibraryRef}\n config={damConfig}\n sortOption={sortOption}\n onFoldersChange={onDamFoldersChange}\n onAssetsSelected={onFilesSelected}\n onSelectionChange={onDamSelectionChange}\n onConfirmAndClose={(results) => {\n void onDamConfirmAndMaybeCreate(results);\n }}\n onSearchingChange={onDamSearchingChange}\n showVariants={config.showVariants}\n searchQuery={damSearchQuery}\n onSearchChange={onDamSearchChange}\n thumbnailSize={thumbnailSize}\n viewMode={viewMode}\n showNamesOnMedia={showNamesOnMedia}\n previewContainer={previewContainer}\n previewAsset={previewAsset}\n onPreviewAssetChange={onPreviewAssetChange}\n />\n )}\n\n {activeMethod === (\"media\" as UploadMethod) && (\n <MediaTab\n config={config}\n onMediaSelected={() => {}} // Not used for media tab\n onSelectionChange={onMediaSelectionChange}\n onMediaDataChange={onMediaDataChange}\n selectedMediaIds={selectedMediaIds}\n onMediaIdsChange={onMediaIdsChange}\n searchQuery={mediaSearchQuery}\n onSearchChange={onMediaSearchChange}\n thumbnailSize={thumbnailSize}\n viewMode={viewMode}\n showNamesOnMedia={showNamesOnMedia}\n mediaTypeFilter={damTypeFilter}\n sortOption={sortOption}\n previewContainer={previewContainer}\n previewMedia={previewMedia}\n onPreviewMediaChange={onPreviewMediaChange}\n />\n )}\n\n {activeMethod === \"unsplash\" && (\n <UnsplashPicker\n config={config}\n onFileSelected={onSingleFileSelected}\n searchQuery={unsplashSearchQuery}\n onSearchQueryChange={onUnsplashSearchChange}\n onSearchingChange={onUnsplashSearchingChange}\n onUploadingChange={onUnsplashUploadingChange}\n />\n )}\n\n {activeMethod === \"google-drive\" && (\n <div className=\"mx-auto max-w-7xl space-y-6 md:px-10 md:py-8\">\n <div className=\"text-center\">\n <div className=\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-linear-to-br from-blue-50 to-indigo-50 ring-4 ring-blue-50/50\">\n <GoogleIcon className=\"h-6 w-6 text-blue-600\" />\n </div>\n <h2 className=\"text-xl font-semibold text-gray-900\">\n Import from Google Drive\n </h2>\n <p className=\"mt-2 text-sm text-gray-600\">\n Connect your Google Drive account to import files\n </p>\n </div>\n <div className=\"rounded-lg border-2 border-dashed border-gray-300 p-8 text-center\">\n <p className=\"text-gray-500\">\n Google Drive integration coming soon...\n </p>\n </div>\n </div>\n )}\n\n {activeMethod === \"instagram\" && (\n <div className=\"mx-auto max-w-7xl space-y-6 md:px-10 md:py-8\">\n <div className=\"text-center\">\n <div className=\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-linear-to-br from-pink-50 to-purple-50 ring-4 ring-pink-50/50\">\n <InstagramIcon className=\"h-6 w-6 text-pink-600\" />\n </div>\n <h2 className=\"text-xl font-semibold text-gray-900\">\n Import from Instagram\n </h2>\n <p className=\"mt-2 text-sm text-gray-600\">\n Connect your Instagram account to import your posts\n </p>\n </div>\n <div className=\"rounded-lg border-2 border-dashed border-gray-300 p-8 text-center\">\n <p className=\"text-gray-500\">\n Instagram integration coming soon...\n </p>\n </div>\n </div>\n )}\n\n {activeMethod === \"facebook\" && (\n <div className=\"mx-auto max-w-7xl space-y-6 md:px-10 md:py-8\">\n <div className=\"text-center\">\n <div className=\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-linear-to-br from-blue-50 to-indigo-50 ring-4 ring-blue-50/50\">\n <FacebookIcon className=\"h-6 w-6 text-blue-600\" />\n </div>\n <h2 className=\"text-xl font-semibold text-gray-900\">\n Import from Facebook\n </h2>\n <p className=\"mt-2 text-sm text-gray-600\">\n Connect your Facebook account to import your photos\n </p>\n </div>\n <div className=\"rounded-lg border-2 border-dashed border-gray-300 p-8 text-center\">\n <p className=\"text-gray-500\">Facebook integration coming soon...</p>\n </div>\n </div>\n )}\n\n {activeMethod === \"tiktok\" && (\n <div className=\"mx-auto max-w-7xl space-y-6 md:px-10 md:py-8\">\n <div className=\"text-center\">\n <div className=\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-linear-to-br from-gray-50 to-gray-100 ring-4 ring-gray-50/50\">\n <TiktokIcon className=\"h-6 w-6 text-gray-900\" />\n </div>\n <h2 className=\"text-xl font-semibold text-gray-900\">\n Import from TikTok\n </h2>\n <p className=\"mt-2 text-sm text-gray-600\">\n Connect your TikTok account to import your videos\n </p>\n </div>\n <div className=\"rounded-lg border-2 border-dashed border-gray-300 p-8 text-center\">\n <p className=\"text-gray-500\">TikTok integration coming soon...</p>\n </div>\n </div>\n )}\n\n {activeMethod === \"dropbox\" && (\n <div className=\"mx-auto max-w-7xl space-y-6 md:px-10 md:py-8\">\n <div className=\"text-center\">\n <div className=\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-linear-to-br from-blue-50 to-indigo-50 ring-4 ring-blue-50/50\">\n <DropboxIcon className=\"h-6 w-6 text-blue-600\" />\n </div>\n <h2 className=\"text-xl font-semibold text-gray-900\">\n Import from Dropbox\n </h2>\n <p className=\"mt-2 text-sm text-gray-600\">\n Connect your Dropbox account to import files\n </p>\n </div>\n <div className=\"rounded-lg border-2 border-dashed border-gray-300 p-8 text-center\">\n <p className=\"text-gray-500\">Dropbox integration coming soon...</p>\n </div>\n </div>\n )}\n </div>\n );\n};\n","import React, { useState, useCallback, useEffect } from \"react\";\nimport {\n XIcon,\n FileIcon,\n ImageIcon,\n VideoIcon,\n EllipsisVerticalIcon,\n CropIcon,\n LayersIcon,\n} from \"lucide-react\";\nimport type { LucideIcon } from \"lucide-react\";\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n Dialog,\n DialogPortal,\n DialogOverlay,\n} from \"@fluid-app/ui-primitives\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport type {\n FilePickerResult,\n DamAsset,\n DamVariant,\n} from \"@fluid-app/file-picker-core\";\nimport { ImageCropModal } from \"./ImageCropModal\";\nimport { getCategoryIcon } from \"../utils/icons\";\n\ninterface SelectionPanelProps {\n selectedFiles: FilePickerResult[];\n onRemoveFile: (assetCode: string) => void;\n onClear: () => void;\n onConfirm: () => void;\n shareableEnabled?: boolean;\n isVisible: boolean;\n hasVariants?: (assetCode: string) => boolean;\n /** Get full asset with variants for the variant picker modal (used with onVariantChange). */\n getAssetWithVariants?: (assetCode: string) => DamAsset | null;\n /** Called when user selects a different variant in the modal. */\n onVariantChange?: (assetCode: string, variantId: string) => void;\n /** @deprecated Use getAssetWithVariants + onVariantChange and open modal from SelectionPanel. */\n onOpenVariantModal?: (assetCode: string) => void;\n onCropImage?: (assetCode: string, croppedFile: File) => void;\n}\n\nconst getFileIcon = (mimeType: string | undefined): LucideIcon => {\n if (!mimeType) return FileIcon;\n if (mimeType.startsWith(\"image/\")) return ImageIcon;\n if (mimeType.startsWith(\"video/\")) return VideoIcon;\n return FileIcon;\n};\n\nconst isImageFile = (file: FilePickerResult): boolean =>\n Boolean(file.metadata.mime_type?.startsWith(\"image/\"));\n\nexport const SelectionPanel: React.FC<SelectionPanelProps> = ({\n selectedFiles,\n onRemoveFile,\n onClear,\n onConfirm,\n shareableEnabled = false,\n isVisible,\n hasVariants,\n getAssetWithVariants,\n onVariantChange,\n onOpenVariantModal,\n onCropImage,\n}) => {\n const [cropTarget, setCropTarget] = useState<{\n assetCode: string;\n file: FilePickerResult;\n } | null>(null);\n const [imageFileForCrop, setImageFileForCrop] = useState<File | null>(null);\n const [cropModalOpen, setCropModalOpen] = useState(false);\n const [variantModalAsset, setVariantModalAsset] = useState<{\n asset: DamAsset;\n assetCode: string;\n } | null>(null);\n\n const openVariantModal = useCallback(\n (assetCode: string) => {\n if (getAssetWithVariants && onVariantChange) {\n const asset = getAssetWithVariants(assetCode);\n if (asset && (asset.variants?.length ?? 0) > 1) {\n setVariantModalAsset({ asset, assetCode });\n return;\n }\n }\n onOpenVariantModal?.(assetCode);\n },\n [getAssetWithVariants, onVariantChange, onOpenVariantModal],\n );\n\n const handleVariantSelect = useCallback(\n (variant: DamVariant) => {\n if (variantModalAsset) {\n onVariantChange?.(variantModalAsset.assetCode, variant.id);\n setVariantModalAsset(null);\n }\n },\n [variantModalAsset, onVariantChange],\n );\n\n // When user clicks \"Crop image\", fetch the image and open crop modal\n useEffect(() => {\n if (!cropTarget?.file.file_url || !isImageFile(cropTarget.file)) {\n return;\n }\n let cancelled = false;\n const { file_url } = cropTarget.file;\n const fileName = cropTarget.file.metadata.file_name ?? \"image.jpg\";\n const mimeType = cropTarget.file.metadata.mime_type ?? \"image/jpeg\";\n fetch(file_url)\n .then((r) => r.blob())\n .then((blob) => {\n if (cancelled) return;\n const file = new File([blob], fileName, { type: mimeType });\n setImageFileForCrop(file);\n setCropModalOpen(true);\n })\n .catch(() => {\n if (!cancelled) setCropTarget(null);\n });\n return () => {\n cancelled = true;\n };\n }, [cropTarget]);\n\n const handleCropComplete = useCallback(\n async (croppedFile: File) => {\n if (cropTarget) {\n const result = onCropImage?.(cropTarget.assetCode, croppedFile);\n if (\n result != null &&\n typeof (result as Promise<unknown>)?.then === \"function\"\n ) {\n await (result as Promise<unknown>);\n }\n }\n setCropTarget(null);\n setImageFileForCrop(null);\n setCropModalOpen(false);\n },\n [cropTarget, onCropImage],\n );\n\n const handleCropCancel = useCallback(() => {\n setCropTarget(null);\n setImageFileForCrop(null);\n setCropModalOpen(false);\n }, []);\n\n if (!isVisible || selectedFiles.length === 0) {\n return null;\n }\n\n return (\n <div className=\"flex h-full w-72 shrink-0 flex-col border-l border-gray-200 bg-white\">\n <div className=\"border-b border-gray-200 px-4 py-3\">\n <h3 className=\"text-sm font-semibold text-gray-900\">Selection</h3>\n </div>\n\n <div className=\"flex-1 overflow-y-auto p-3\">\n <div className=\"space-y-2\">\n {selectedFiles.map((file) => {\n const canCrop = isImageFile(file) && onCropImage;\n const hasVariantOption =\n hasVariants?.(file.asset_code) &&\n (onOpenVariantModal || (getAssetWithVariants && onVariantChange));\n return (\n <div\n key={file.asset_code}\n className=\"group flex items-center gap-3 rounded-lg border border-gray-100 bg-gray-50 p-2 transition-colors hover:border-gray-200 hover:bg-gray-100\"\n >\n <div className=\"relative h-10 w-10 shrink-0 overflow-hidden rounded bg-gray-200\">\n {file.file_url &&\n file.metadata.mime_type?.startsWith(\"image/\") ? (\n <img\n src={file.file_url}\n alt={file.metadata.file_name}\n width={40}\n height={40}\n className=\"h-full w-full object-cover\"\n />\n ) : file.file_url &&\n file.metadata.mime_type?.startsWith(\"video/\") ? (\n <img\n src={`${file.file_url.split(\"?\")[0]}/ik-thumbnail.jpg`}\n alt={file.metadata.file_name}\n width={40}\n height={40}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center\">\n {React.createElement(\n getFileIcon(file.metadata.mime_type),\n {\n className: \"h-4 w-4 text-gray-400\",\n },\n )}\n </div>\n )}\n </div>\n\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-xs font-medium text-gray-900\">\n {file.metadata.file_name}\n </p>\n <p className=\"truncate text-xs text-gray-500\">\n {file.metadata.mime_type?.split(\"/\")[1]?.toUpperCase() ||\n \"FILE\"}\n </p>\n </div>\n\n <div className=\"flex shrink-0 items-center gap-0.5\">\n <DropdownMenu>\n <DropdownMenuTrigger\n className=\"flex h-6 w-6 items-center justify-center rounded-full text-gray-400 opacity-0 transition-all group-hover:opacity-100 hover:bg-gray-200 hover:text-gray-600\"\n onClick={(e) => e.stopPropagation()}\n >\n <EllipsisVerticalIcon className=\"h-3 w-3\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"z-350\">\n <DropdownMenuItem\n disabled={!canCrop}\n onClick={(e) => {\n e.stopPropagation();\n if (canCrop && isImageFile(file))\n setCropTarget({ assetCode: file.asset_code, file });\n }}\n title={\n canCrop\n ? undefined\n : \"Crop is only available for image files\"\n }\n >\n <CropIcon className=\"mr-2 h-3.5 w-3.5 text-gray-600\" />\n Crop image\n </DropdownMenuItem>\n <DropdownMenuItem\n disabled={!hasVariantOption}\n onClick={(e) => {\n e.stopPropagation();\n if (hasVariantOption)\n openVariantModal(file.asset_code);\n }}\n >\n <LayersIcon\n className={`mr-2 h-3.5 w-3.5 ${hasVariantOption ? \"text-gray-600\" : \"text-gray-400\"}`}\n />\n Change variant\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n <button\n onClick={(e) => {\n e.stopPropagation();\n onRemoveFile(file.asset_code);\n }}\n className=\"flex h-6 w-6 items-center justify-center rounded-full text-gray-400 opacity-0 transition-all group-hover:opacity-100 hover:bg-gray-200 hover:text-gray-600\"\n >\n <XIcon className=\"h-3 w-3\" />\n </button>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n\n <div className=\"flex items-center justify-between gap-2 border-t border-gray-200 p-4\">\n <Button variant=\"secondary\" size=\"sm\" onClick={onClear}>\n Clear\n </Button>\n <Button variant=\"default\" size=\"sm\" onClick={onConfirm}>\n {shareableEnabled\n ? \"Next\"\n : `Use ${selectedFiles.length} File${selectedFiles.length !== 1 ? \"s\" : \"\"}`}\n </Button>\n </div>\n\n {imageFileForCrop && (\n <ImageCropModal\n isOpen={cropModalOpen}\n onClose={handleCropCancel}\n onCrop={handleCropComplete}\n imageFile={imageFileForCrop}\n />\n )}\n\n {variantModalAsset && (\n <Dialog\n open={true}\n onOpenChange={(open) => !open && setVariantModalAsset(null)}\n >\n <DialogPortal>\n <DialogOverlay className=\"z-9999 bg-black/70 backdrop-blur-none\" />\n <DialogPrimitive.Content\n className=\"fixed top-1/2 left-1/2 z-10000 max-h-[80vh] w-full max-w-3xl -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-lg border border-gray-200 bg-white shadow-xl\"\n style={{ zIndex: 10000 }}\n >\n <div className=\"border-b bg-white p-6\">\n <h3 className=\"text-lg font-semibold text-gray-900\">\n Select variant\n </h3>\n <p className=\"mt-1 text-sm text-gray-500\">\n Choose a variant for "\n {variantModalAsset.asset.name || \"Unknown Asset\"}"\n </p>\n </div>\n <div className=\"max-h-[60vh] overflow-y-auto p-6\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n {variantModalAsset.asset.variants?.map((variant) => (\n <button\n key={variant?.id}\n type=\"button\"\n onClick={() => handleVariantSelect(variant)}\n className=\"group rounded-lg border border-gray-200 bg-white p-4 text-left transition-colors hover:bg-gray-50\"\n >\n <div className=\"flex items-start gap-4\">\n <div className=\"shrink-0\">\n {variant.url &&\n variantModalAsset.asset.category === \"images\" ? (\n <img\n src={variant.url}\n alt={variant.file_name}\n width={64}\n height={64}\n className=\"h-16 w-16 rounded-md object-cover\"\n />\n ) : variantModalAsset.asset.category === \"videos\" &&\n variant?.url ? (\n <div className=\"flex h-16 w-16 items-center justify-center rounded-md bg-gray-50\">\n <img\n src={`${variant?.url?.split(\"?\")[0]}/ik-thumbnail.jpg`}\n alt={variant.file_name}\n width={64}\n height={64}\n className=\"h-16 w-16 rounded-md object-cover\"\n />\n </div>\n ) : (\n <div className=\"flex h-16 w-16 items-center justify-center rounded-md bg-gray-50\">\n {React.createElement(\n getCategoryIcon(\n variantModalAsset.asset?.category ||\n \"unknown\",\n ),\n { className: \"h-6 w-6 text-gray-400\" },\n )}\n </div>\n )}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate font-medium text-gray-900 group-hover:text-gray-700\">\n {variant?.file_name || \"Unknown File\"}\n </p>\n <p className=\"mt-1 text-xs text-gray-500\">\n {variant?.mime_type || \"Unknown Type\"}\n </p>\n {typeof variant.metadata?.width === \"number\" &&\n typeof variant.metadata?.height === \"number\" && (\n <p className=\"mt-1 text-xs font-medium text-gray-600\">\n {variant.metadata.width}px ×{\" \"}\n {variant.metadata.height}px\n </p>\n )}\n </div>\n </div>\n </button>\n ))}\n </div>\n </div>\n <div className=\"border-t bg-gray-50 p-6\">\n <div className=\"flex justify-end\">\n <Button\n variant=\"secondary\"\n onClick={() => setVariantModalAsset(null)}\n >\n Cancel\n </Button>\n </div>\n </div>\n </DialogPrimitive.Content>\n </DialogPortal>\n </Dialog>\n )}\n </div>\n );\n};\n\nexport default SelectionPanel;\n","import React, {\n useState,\n useCallback,\n useEffect,\n useRef,\n useMemo,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { ImagesIcon, LaptopIcon, CloudIcon, LinkIcon } from \"lucide-react\";\nimport {\n GoogleIcon,\n InstagramIcon,\n FacebookIcon,\n TiktokIcon,\n DropboxIcon,\n UnsplashIcon,\n} from \"../utils/brand-icons\";\nimport { Dialog, DialogContent, DialogTitle } from \"@fluid-app/ui-primitives\";\nimport {\n filePickerConfigSchema,\n type FilePickerResult,\n type FilePickerConfigInput,\n type UploadMethod,\n type DamAsset,\n type SortOptionId,\n} from \"@fluid-app/file-picker-core\";\nimport { type DamLibraryRef } from \"./DamLibrary\";\nimport { FilePickerSidebar } from \"./FilePickerSidebar\";\nimport { FilePickerHeader, type ViewMode } from \"./FilePickerHeader\";\nimport { FilePickerContent } from \"./FilePickerContent\";\nimport {\n defaultFilters as defaultDamFilters,\n type FilePickerFiltersState,\n} from \"./FilePickerFiltersPanel\";\nimport { SelectionPanel } from \"./SelectionPanel\";\n\nexport interface FilePickerProps {\n config?: FilePickerConfigInput;\n onFilesSelected: (results: FilePickerResult[]) => void;\n onClose?: () => void;\n open: boolean;\n showMediaTab?: boolean;\n className?: string;\n skipCropper?: boolean;\n defaultMethod?: UploadMethod;\n}\n\nexport const FilePicker: React.FC<FilePickerProps> = ({\n config: configInput = {},\n onFilesSelected,\n onClose,\n open,\n showMediaTab = false,\n className,\n skipCropper = false,\n defaultMethod,\n}) => {\n const config = useMemo(() => {\n const result = filePickerConfigSchema.safeParse(configInput);\n return result.success ? result.data : filePickerConfigSchema.parse({});\n }, [configInput]);\n\n const defaultTab = defaultMethod ?? \"dam\";\n const [activeMethod, setActiveMethod] = useState<UploadMethod>(() => {\n if (config.allowedMethods && config.allowedMethods.length > 0) {\n const allowed = config.allowedMethods as UploadMethod[];\n if (allowed.includes(defaultTab)) return defaultTab;\n return allowed[0] ?? \"dam\";\n }\n return defaultTab;\n });\n // Re-sync activeMethod if allowedMethods changes and current tab is no longer allowed\n useEffect(() => {\n if (\n config.allowedMethods &&\n config.allowedMethods.length > 0 &&\n !config.allowedMethods.includes(activeMethod)\n ) {\n setActiveMethod((config.allowedMethods[0] as UploadMethod) ?? \"dam\");\n }\n }, [config.allowedMethods, activeMethod]);\n\n const [selectedResults, setSelectedResults] = useState<FilePickerResult[]>(\n [],\n );\n const [damSelectionCount, setDamSelectionCount] = useState(0);\n const [damSelectedFiles, setDamSelectedFiles] = useState<FilePickerResult[]>(\n [],\n );\n const [damVariantCounts, setDamVariantCounts] = useState<\n Record<string, number>\n >({});\n const [damSearchQuery, setDamSearchQuery] = useState(\"\");\n const [damSearching, setDamSearching] = useState(false);\n const [mediaSearchQuery, setMediaSearchQuery] = useState(\"\");\n const [unsplashSearchQuery, setUnsplashSearchQuery] = useState(\"\");\n const [unsplashSearching, setUnsplashSearching] = useState(false);\n const [isUnsplashUploading, setIsUnsplashUploading] = useState(false);\n const [mediaSelectionCount, setMediaSelectionCount] = useState(0);\n const [damFilters, setDamFilters] =\n useState<FilePickerFiltersState>(defaultDamFilters);\n const [sortOption, setSortOption] = useState<SortOptionId | undefined>(\n undefined,\n );\n const [damFolders, setDamFolders] = useState<string[]>([]);\n const [selectedMediaIds, setSelectedMediaIds] = useState<number[]>([]);\n const [selectedMediaData, setSelectedMediaData] = useState<\n FilePickerResult[]\n >([]);\n const damLibraryRef = useRef<DamLibraryRef | null>(null);\n const [previewContainer, setPreviewContainer] =\n useState<HTMLDivElement | null>(null);\n const [previewAsset, setPreviewAsset] = useState<DamAsset | null>(null);\n\n // DAM toolbar state\n const [thumbnailSize, setThumbnailSize] = useState(100);\n const [viewMode, setViewMode] = useState<ViewMode>(\"grid\");\n const [showNamesOnMedia, setShowNamesOnMedia] = useState(true);\n\n const resetPicker = useCallback(() => {\n setActiveMethod(defaultTab);\n setSelectedResults([]);\n setDamSelectionCount(0);\n setDamSelectedFiles([]);\n setDamVariantCounts({});\n setDamSearchQuery(\"\");\n setMediaSearchQuery(\"\");\n setUnsplashSearchQuery(\"\");\n setUnsplashSearching(false);\n setIsUnsplashUploading(false);\n setMediaSelectionCount(0);\n setSelectedMediaIds([]);\n setSelectedMediaData([]);\n setDamFilters(defaultDamFilters);\n setSortOption(undefined);\n damLibraryRef.current?.clearSelection?.();\n setPreviewAsset(null);\n }, [defaultTab]);\n\n const handleMethodChange = useCallback((newMethod: UploadMethod) => {\n setSelectedResults([]);\n setDamSelectionCount(0);\n setDamSelectedFiles([]);\n setDamVariantCounts({});\n setDamSearchQuery(\"\");\n setUnsplashSearchQuery(\"\");\n setUnsplashSearching(false);\n setIsUnsplashUploading(false);\n setMediaSelectionCount(0);\n setSelectedMediaIds([]);\n setSelectedMediaData([]);\n damLibraryRef.current?.clearSelection?.();\n setActiveMethod(newMethod);\n }, []);\n\n const handleFilesSelected = useCallback(\n (results: FilePickerResult[]) => {\n const validResults = results.filter(\n (result) => result.upload_status !== \"error\",\n );\n\n if (config.maxFiles === 1) {\n setSelectedResults(validResults.slice(0, 1));\n } else {\n setSelectedResults((prev) => {\n const totalFiles = prev.length + validResults.length;\n if (config.maxFiles && totalFiles > config.maxFiles) {\n const remainingSlots = config.maxFiles - prev.length;\n return [...prev, ...validResults.slice(0, remainingSlots)];\n }\n return [...prev, ...validResults];\n });\n }\n },\n [config.maxFiles],\n );\n\n const handleUploadConfirm = useCallback(\n (results: FilePickerResult[]) => {\n const validResults = results.filter(\n (result) => result.upload_status !== \"error\",\n );\n if (validResults.length === 0) return;\n onFilesSelected(validResults);\n onClose?.();\n },\n [onFilesSelected, onClose],\n );\n\n const handleSingleFileSelected = useCallback(\n (result: FilePickerResult) => {\n handleFilesSelected([result]);\n },\n [handleFilesSelected],\n );\n\n const handleDamSelectionChange = useCallback(\n (\n count: number,\n files?: FilePickerResult[],\n variantCounts?: Record<string, number>,\n ) => {\n setDamSelectionCount(count);\n setDamSelectedFiles(files ?? []);\n setDamVariantCounts(variantCounts ?? {});\n },\n [],\n );\n\n const handleMediaSelectionChange = useCallback((count: number) => {\n setMediaSelectionCount(count);\n }, []);\n\n const handleMediaDataChange = useCallback((data: FilePickerResult[]) => {\n setSelectedMediaData(data);\n }, []);\n\n const handleThumbnailSizeChange = useCallback((size: number) => {\n setThumbnailSize(size);\n }, []);\n\n const handleViewModeChange = useCallback((mode: ViewMode) => {\n setViewMode(mode);\n }, []);\n\n const handleDamConfirm = useCallback(\n (results: FilePickerResult[]) => {\n onFilesSelected(results);\n onClose?.();\n },\n [onFilesSelected, onClose],\n );\n\n const methods: Array<{\n id: UploadMethod;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n description: string;\n }> = useMemo(() => {\n const allMethods = [\n {\n id: \"dam\" as UploadMethod,\n label: \"Library\",\n icon: CloudIcon,\n description: \"Choose from DAM library\",\n },\n ...(showMediaTab ||\n (config.allowedMethods && config.allowedMethods.includes(\"media\"))\n ? [\n {\n id: \"media\" as UploadMethod,\n label: \"Media\",\n icon: ImagesIcon,\n description: \"Choose from existing media\",\n },\n ]\n : []),\n {\n id: \"upload\" as UploadMethod,\n label: \"Local Files\",\n icon: LaptopIcon,\n description: \"Upload files from computer or URL\",\n },\n {\n id: \"url\" as UploadMethod,\n label: \"URL\",\n icon: LinkIcon,\n description: \"Upload files from web URLs\",\n },\n {\n id: \"unsplash\" as UploadMethod,\n label: \"Unsplash\",\n icon: UnsplashIcon,\n description: \"Search Unsplash images\",\n },\n {\n id: \"google-drive\" as UploadMethod,\n label: \"Google Drive\",\n icon: GoogleIcon,\n description: \"Import from Google Drive\",\n },\n {\n id: \"instagram\" as UploadMethod,\n label: \"Instagram\",\n icon: InstagramIcon,\n description: \"Import from Instagram\",\n },\n {\n id: \"facebook\" as UploadMethod,\n label: \"Facebook\",\n icon: FacebookIcon,\n description: \"Import from Facebook\",\n },\n {\n id: \"tiktok\" as UploadMethod,\n label: \"TikTok\",\n icon: TiktokIcon,\n description: \"Import from TikTok\",\n },\n {\n id: \"dropbox\" as UploadMethod,\n label: \"Dropbox\",\n icon: DropboxIcon,\n description: \"Import from Dropbox\",\n },\n ];\n\n if (config.allowedMethods && config.allowedMethods.length > 0) {\n return allMethods.filter((method) =>\n config.allowedMethods!.includes(method.id),\n );\n }\n\n return allMethods;\n }, [showMediaTab, config.allowedMethods]);\n\n const showPreview = previewAsset !== null;\n\n return (\n <>\n {typeof document !== \"undefined\" &&\n createPortal(\n <div\n ref={setPreviewContainer}\n className=\"fixed inset-0\"\n style={{\n zIndex: 9999,\n pointerEvents: showPreview ? \"auto\" : \"none\",\n }}\n aria-hidden={!showPreview}\n />,\n document.body,\n )}\n <Dialog\n open={open}\n onOpenChange={(isOpen) => {\n if (!isOpen) {\n resetPicker();\n onClose?.();\n }\n }}\n >\n <DialogContent\n className={`data-[state=closed]:animate-out! data-[state=closed]:fade-out-0! data-[state=closed]:zoom-out-95! data-[state=open]:animate-in! data-[state=open]:fade-in-0! data-[state=open]:zoom-in-95! z-300! flex! h-[90vh]! max-h-[1000px]! w-[95vw]! max-w-[1600px]! flex-col! gap-0! overflow-y-auto! rounded-lg! border-0! bg-white p-0! data-[state=closed]:duration-150! data-[state=open]:duration-200! ${className || \"\"}`}\n >\n <DialogTitle className=\"sr-only\">File Picker</DialogTitle>\n <div className=\"flex flex-1 overflow-y-auto\">\n <FilePickerSidebar\n currentStep=\"select\"\n activeMethod={activeMethod}\n onMethodChange={handleMethodChange}\n methods={methods}\n />\n\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\n <FilePickerHeader\n currentStep=\"select\"\n activeMethod={activeMethod}\n methods={methods}\n onClose={() => {\n resetPicker();\n onClose?.();\n }}\n damSearchQuery={\n activeMethod === \"dam\" ? damSearchQuery : mediaSearchQuery\n }\n onDamSearchChange={\n activeMethod === \"dam\"\n ? setDamSearchQuery\n : setMediaSearchQuery\n }\n damSearching={damSearching}\n thumbnailSize={thumbnailSize}\n onThumbnailSizeChange={handleThumbnailSizeChange}\n viewMode={viewMode}\n onViewModeChange={handleViewModeChange}\n showNamesOnMedia={showNamesOnMedia}\n onShowNamesOnMediaChange={setShowNamesOnMedia}\n hasSelectedFiles={damSelectionCount > 0}\n damFolders={damFolders}\n onAddToNewFolder={() =>\n damLibraryRef.current?.openAddFolderModal()\n }\n onAddToExistingFolder={(folderPath) =>\n damLibraryRef.current?.addToExistingFolder?.(folderPath)\n }\n filters={damFilters}\n onFiltersChange={setDamFilters}\n onFiltersClear={() => setDamFilters(defaultDamFilters)}\n sortOption={sortOption}\n onSortChange={setSortOption}\n unsplashSearchQuery={unsplashSearchQuery}\n onUnsplashSearchChange={setUnsplashSearchQuery}\n unsplashSearching={unsplashSearching}\n isUnsplashUploading={isUnsplashUploading}\n />\n <div className=\"flex min-h-0 min-w-0 flex-1 overflow-hidden\">\n <div className=\"min-h-0 min-w-0 flex-1 overflow-y-auto\">\n <FilePickerContent\n activeMethod={activeMethod}\n config={config}\n damTypeFilter={\n damFilters.type ? damFilters.type : undefined\n }\n sortOption={sortOption}\n onFilesSelected={handleFilesSelected}\n onUploadConfirmAndMaybeCreate={handleUploadConfirm}\n onSingleFileSelected={handleSingleFileSelected}\n damLibraryRef={damLibraryRef}\n onDamConfirmAndMaybeCreate={handleDamConfirm}\n selectedMediaIds={selectedMediaIds}\n onMediaIdsChange={setSelectedMediaIds}\n onMediaSelectionChange={handleMediaSelectionChange}\n onMediaDataChange={handleMediaDataChange}\n onDamSelectionChange={handleDamSelectionChange}\n onDamSearchingChange={setDamSearching}\n skipCropper={skipCropper}\n damSearchQuery={damSearchQuery}\n onDamSearchChange={setDamSearchQuery}\n mediaSearchQuery={mediaSearchQuery}\n onMediaSearchChange={setMediaSearchQuery}\n unsplashSearchQuery={unsplashSearchQuery}\n onUnsplashSearchChange={setUnsplashSearchQuery}\n onUnsplashSearchingChange={setUnsplashSearching}\n onUnsplashUploadingChange={setIsUnsplashUploading}\n thumbnailSize={thumbnailSize}\n viewMode={viewMode}\n showNamesOnMedia={showNamesOnMedia}\n onDamFoldersChange={setDamFolders}\n previewContainer={previewContainer}\n previewAsset={previewAsset}\n onPreviewAssetChange={setPreviewAsset}\n uploadSelectionCount={\n activeMethod === \"upload\" ||\n activeMethod === \"url\" ||\n activeMethod === \"unsplash\"\n ? selectedResults.length\n : undefined\n }\n />\n </div>\n {activeMethod === \"dam\" && (\n <SelectionPanel\n selectedFiles={damSelectedFiles}\n onRemoveFile={(assetCode) => {\n damLibraryRef.current?.deselectAssetByCode?.(assetCode);\n }}\n onClear={() => {\n damLibraryRef.current?.clearSelection();\n setDamSelectionCount(0);\n setDamSelectedFiles([]);\n setDamVariantCounts({});\n }}\n onConfirm={() => {\n damLibraryRef.current?.confirmAndClose();\n }}\n isVisible={damSelectionCount > 0}\n hasVariants={(assetCode) =>\n (damVariantCounts[assetCode] ?? 0) > 1\n }\n getAssetWithVariants={(assetCode) =>\n damLibraryRef.current?.getAssetWithVariants?.(\n assetCode,\n ) ?? null\n }\n onVariantChange={(assetCode, variantId) => {\n damLibraryRef.current?.setVariantForAsset?.(\n assetCode,\n variantId,\n );\n }}\n />\n )}\n {activeMethod === \"media\" && (\n <SelectionPanel\n selectedFiles={selectedMediaData}\n onRemoveFile={(assetCode) => {\n const id = parseInt(assetCode.replace(/^media_/, \"\"), 10);\n if (!Number.isNaN(id)) {\n setSelectedMediaIds((prev) =>\n prev.filter((mediaId) => mediaId !== id),\n );\n setSelectedMediaData((prev) =>\n prev.filter((r) => r.asset_code !== assetCode),\n );\n }\n }}\n onClear={() => {\n setSelectedMediaIds([]);\n setSelectedMediaData([]);\n setMediaSelectionCount(0);\n }}\n onConfirm={() => {\n onFilesSelected(selectedMediaData);\n onClose?.();\n }}\n isVisible={mediaSelectionCount > 0}\n hasVariants={() => false}\n />\n )}\n {(activeMethod === \"upload\" ||\n activeMethod === \"url\" ||\n activeMethod === \"unsplash\") && (\n <SelectionPanel\n selectedFiles={selectedResults}\n onRemoveFile={(assetCode) => {\n setSelectedResults((prev) =>\n prev.filter((r) => r.asset_code !== assetCode),\n );\n }}\n onClear={() => {\n setSelectedResults([]);\n }}\n onConfirm={() => {\n handleUploadConfirm(selectedResults);\n }}\n isVisible={selectedResults.length > 0}\n hasVariants={() => false}\n />\n )}\n </div>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </>\n );\n};\n","\"use client\";\n\nimport { useState, useCallback, useMemo } from \"react\";\nimport {\n useShareablesUser,\n useCreateMediaMutation,\n} from \"@fluid-app/shareables-core\";\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n Button,\n Input,\n Spinner,\n} from \"@fluid-app/ui-primitives\";\nimport { Upload, Video, FileText } from \"lucide-react\";\nimport {\n FilePicker,\n FilePickerProvider,\n type FilePickerContextValue,\n} from \"@fluid-app/file-picker-ui\";\nimport type { FilePickerResult } from \"@fluid-app/file-picker-core\";\nimport { useScreenHeaderBreadcrumbs } from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { useShareablesUI } from \"../../context\";\n\nexport interface MediaCreateScreenProps {\n onNavigate?: (screen: string, detailId?: string) => void;\n onBack?: () => void;\n}\n\nfunction deriveMediaType(mimeType: string): \"image\" | \"video\" | \"pdf\" {\n if (mimeType.startsWith(\"video/\")) return \"video\";\n if (mimeType === \"application/pdf\") return \"pdf\";\n return \"image\";\n}\n\nfunction FilePreview({\n result,\n mediaType,\n}: {\n result: FilePickerResult;\n mediaType: \"image\" | \"video\" | \"pdf\";\n}) {\n if (mediaType === \"image\") {\n return (\n <div className=\"relative aspect-video w-full overflow-hidden rounded-lg border\">\n <img\n src={result.file_url}\n alt={result.metadata.file_name}\n className=\"absolute inset-0 h-full w-full object-cover\"\n />\n </div>\n );\n }\n\n const Icon = mediaType === \"video\" ? Video : FileText;\n return (\n <div className=\"flex items-center gap-3 rounded-lg border p-4\">\n <Icon className=\"text-muted-foreground h-8 w-8 shrink-0\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-foreground truncate text-sm font-medium\">\n {result.metadata.file_name}\n </p>\n <p className=\"text-muted-foreground text-xs\">\n {result.metadata.mime_type}\n </p>\n </div>\n </div>\n );\n}\n\nexport function MediaCreateScreen({\n onNavigate,\n onBack,\n}: MediaCreateScreenProps) {\n const { navigate, showToast, filePickerClient } = useShareablesUI();\n const user = useShareablesUser();\n\n const headerBreadcrumbs = useMemo(\n () => (\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbLink\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n (onBack ?? (() => navigate(\"media\")))();\n }}\n >\n Media\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">New Media</BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n ),\n [onBack, navigate],\n );\n useScreenHeaderBreadcrumbs(headerBreadcrumbs);\n\n const [title, setTitle] = useState(\"\");\n const [description, setDescription] = useState(\"\");\n const [active, setActive] = useState(true);\n const [selectedResult, setSelectedResult] = useState<FilePickerResult | null>(\n null,\n );\n const [isPickerOpen, setIsPickerOpen] = useState(false);\n\n const mediaType = selectedResult\n ? deriveMediaType(selectedResult.metadata.mime_type)\n : null;\n\n const { mutate: createMedia, isPending: isCreating } = useCreateMediaMutation(\n {\n onSuccess: (newMedia: { id: number }) => {\n showToast({ title: \"Media created successfully\", type: \"success\" });\n onNavigate?.(\"media\", String(newMedia.id));\n },\n onError: (error: Error) => {\n showToast({\n title: `Failed to create media: ${error.message}`,\n type: \"error\",\n });\n },\n },\n );\n\n const handleSave = useCallback(() => {\n if (!title.trim()) {\n showToast({ title: \"Title is required\", type: \"warning\" });\n return;\n }\n if (!description.trim()) {\n showToast({ title: \"Description is required\", type: \"warning\" });\n return;\n }\n if (!selectedResult) {\n showToast({ title: \"Please select a file\", type: \"warning\" });\n return;\n }\n\n const mt = deriveMediaType(selectedResult.metadata.mime_type);\n\n createMedia({\n title: title.trim(),\n description: description.trim(),\n active,\n media_type: \"share\",\n kind: mt,\n image_url: mt === \"image\" ? selectedResult.file_url : undefined,\n video_url: mt === \"video\" ? selectedResult.file_url : undefined,\n pdf_url: mt === \"pdf\" ? selectedResult.file_url : undefined,\n user_id: user?.id,\n });\n }, [\n title,\n description,\n active,\n selectedResult,\n user,\n createMedia,\n showToast,\n ]);\n\n const handleFilesSelected = useCallback(\n (results: FilePickerResult[]) => {\n const result = results[0];\n if (!result) return;\n setSelectedResult(result);\n setIsPickerOpen(false);\n if (!title.trim()) {\n setTitle(result.metadata.file_name.replace(/\\.[^/.]+$/, \"\"));\n }\n },\n [title],\n );\n\n const filePickerContextValue = useMemo<FilePickerContextValue | null>(() => {\n if (!filePickerClient) return null;\n return {\n apiClient: filePickerClient,\n toast: {\n success: (msg: string) => showToast({ title: msg, type: \"success\" }),\n error: (msg: string, error?: unknown) =>\n showToast({\n title: error instanceof Error ? `${msg}: ${error.message}` : msg,\n type: \"error\",\n }),\n loading: () => \"\",\n dismiss: () => {},\n },\n };\n }, [filePickerClient, showToast]);\n\n return (\n <div className=\"flex flex-col gap-4 px-4 py-4 md:px-10 md:py-6\">\n <div className=\"mx-auto flex w-full max-w-lg flex-col gap-6\">\n <h1 className=\"text-foreground text-[26px] leading-[1.2] font-semibold\">\n Add Media\n </h1>\n\n {/* File picker */}\n <div className=\"flex flex-col gap-2\">\n <label className=\"text-foreground text-sm font-medium\">File</label>\n {selectedResult && mediaType ? (\n <div className=\"flex flex-col gap-2\">\n <FilePreview result={selectedResult} mediaType={mediaType} />\n <Button\n onClick={() => setIsPickerOpen(true)}\n variant=\"outline\"\n size=\"sm\"\n className=\"w-fit\"\n >\n Change File\n </Button>\n </div>\n ) : (\n <Button\n onClick={() => setIsPickerOpen(true)}\n variant=\"outline\"\n className=\"flex h-32 w-full flex-col items-center justify-center gap-2 border-dashed\"\n disabled={!filePickerClient}\n >\n <Upload className=\"text-muted-foreground h-6 w-6\" />\n <span className=\"text-muted-foreground text-sm\">Select File</span>\n </Button>\n )}\n </div>\n\n {/* Title */}\n <div className=\"flex flex-col gap-2\">\n <label className=\"text-foreground text-sm font-medium\">\n Title <span className=\"text-red-500\">*</span>\n </label>\n <Input\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"Enter media title\"\n required\n />\n </div>\n\n {/* Description */}\n <div className=\"flex flex-col gap-2\">\n <label className=\"text-foreground text-sm font-medium\">\n Description <span className=\"text-red-500\">*</span>\n </label>\n <textarea\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Enter description\"\n required\n rows={3}\n className=\"border-input bg-background text-foreground placeholder:text-muted-foreground rounded-md border px-3 py-2 text-sm focus:ring-2 focus:ring-offset-2 focus:outline-none\"\n />\n </div>\n\n {/* Active toggle */}\n <label className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={active}\n onChange={(e) => setActive(e.target.checked)}\n className=\"h-4 w-4 rounded\"\n />\n <span className=\"text-foreground text-sm font-medium\">Active</span>\n </label>\n\n {/* Save button */}\n <Button\n onClick={handleSave}\n disabled={isCreating}\n className=\"bg-foreground text-background hover:bg-foreground/70 flex h-10 w-full items-center justify-center rounded-lg px-4 transition-all\"\n >\n {isCreating ? <Spinner className=\"size-4\" /> : \"Save Media\"}\n </Button>\n </div>\n\n {/* File Picker Dialog */}\n {filePickerContextValue && (\n <FilePickerProvider value={filePickerContextValue}>\n <FilePicker\n open={isPickerOpen}\n onFilesSelected={handleFilesSelected}\n onClose={() => setIsPickerOpen(false)}\n config={{ maxFiles: 1 }}\n />\n </FilePickerProvider>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { useState } from \"react\";\nimport {\n Badge,\n Checkbox,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n cn,\n} from \"@fluid-app/ui-primitives\";\nimport { Heart, MoreVertical, Pencil, Trash2 } from \"lucide-react\";\nimport { useShareablesUI, useRenderImage } from \"../../context\";\n\nexport interface PlaylistCardProps {\n title: string;\n imageUrl?: string | null;\n href: string;\n itemCount: number;\n isFavorited?: boolean;\n isSelectable?: boolean;\n isSelected?: boolean;\n canEdit?: boolean;\n onSelectionChange?: (selected: boolean) => void;\n onToggleFavorite?: () => void;\n onEdit?: () => void;\n onDelete?: () => void;\n}\n\nconst DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\nexport function PlaylistCard({\n title,\n imageUrl,\n href,\n itemCount,\n isFavorited = false,\n isSelectable = false,\n isSelected = false,\n canEdit = false,\n onSelectionChange,\n onToggleFavorite,\n onEdit,\n onDelete,\n}: PlaylistCardProps) {\n const { navigate } = useShareablesUI();\n const renderImage = useRenderImage();\n const [imgError, setImgError] = useState(false);\n\n const hasStack = itemCount > 1;\n const badgeText = `${itemCount} ${itemCount === 1 ? \"item\" : \"items\"}`;\n\n return (\n <button\n type=\"button\"\n onClick={() => navigate(href)}\n className=\"group block w-full cursor-pointer text-left\"\n >\n {/* Stack effect container */}\n <div className=\"relative\">\n {/* Stack layers */}\n {hasStack && (\n <>\n <div\n className=\"bg-muted absolute -top-2 right-2 left-2 z-0 h-full rounded-lg shadow-sm transition-transform duration-300 ease-out\"\n style={{\n transform: isSelected ? \"rotate(0deg)\" : \"rotate(-4deg)\",\n }}\n />\n <div\n className=\"bg-muted absolute -top-1 right-1 left-1 z-0 h-full rounded-lg shadow-sm transition-transform duration-300 ease-out\"\n style={{\n transform: isSelected ? \"rotate(0deg)\" : \"rotate(7deg)\",\n }}\n />\n </>\n )}\n\n {/* Main card */}\n <div className=\"bg-background relative z-10 rounded-lg p-1 shadow-md\">\n <div className=\"bg-muted relative aspect-square overflow-hidden rounded-lg\">\n {renderImage({\n src: imgError ? DEFAULT_IMAGE : imageUrl || DEFAULT_IMAGE,\n alt: title,\n fill: true,\n className:\n \"object-cover transition-transform group-hover:scale-105\",\n onError: () => setImgError(true),\n })}\n\n {/* Selection checkbox */}\n {isSelectable && (\n <div\n className={cn(\n \"absolute top-2 left-2 z-10 transition-opacity\",\n isSelected\n ? \"opacity-100\"\n : \"opacity-0 group-hover:opacity-100\",\n )}\n onClick={(e) => {\n e.stopPropagation();\n }}\n >\n <Checkbox\n checked={isSelected}\n onCheckedChange={() => onSelectionChange?.(!isSelected)}\n />\n </div>\n )}\n\n {/* Context menu */}\n {(onToggleFavorite || canEdit) && (\n <div\n className={cn(\n \"absolute top-2 right-2 z-20 transition-opacity\",\n \"opacity-0 group-hover:opacity-100\",\n )}\n >\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n onClick={(e) => e.stopPropagation()}\n className=\"bg-background/90 hover:bg-background flex h-8 w-8 items-center justify-center rounded-lg shadow-md backdrop-blur-sm transition-all\"\n aria-label=\"More options\"\n >\n <MoreVertical className=\"text-foreground h-4 w-4\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n align=\"end\"\n onClick={(e) => e.stopPropagation()}\n >\n {onToggleFavorite && (\n <DropdownMenuItem onClick={onToggleFavorite}>\n <Heart\n className={cn(\n \"mr-2 h-4 w-4\",\n isFavorited && \"text-destructive fill-current\",\n )}\n />\n {isFavorited ? \"Unfavorite\" : \"Favorite\"}\n </DropdownMenuItem>\n )}\n {canEdit && onEdit && (\n <DropdownMenuItem onClick={onEdit}>\n <Pencil className=\"mr-2 h-4 w-4\" />\n Edit\n </DropdownMenuItem>\n )}\n {canEdit && onDelete && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n variant=\"destructive\"\n onClick={onDelete}\n >\n <Trash2 className=\"mr-2 h-4 w-4\" />\n Delete\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n )}\n\n {/* Radial gradient behind heart for visibility */}\n {onToggleFavorite && (\n <div className=\"pointer-events-none absolute -bottom-8 -left-8 z-9 h-24 w-24 rounded-full bg-[#00000020] blur-xl\" />\n )}\n\n {/* Favorite heart */}\n {onToggleFavorite && (\n <button\n type=\"button\"\n className=\"absolute bottom-3 left-3 z-10 cursor-pointer transition-transform hover:scale-110\"\n onClick={(e) => {\n e.stopPropagation();\n onToggleFavorite();\n }}\n aria-label={isFavorited ? \"Unfavorite\" : \"Favorite\"}\n >\n <Heart\n className={cn(\n \"h-6 w-6 drop-shadow-lg transition-all\",\n isFavorited\n ? \"fill-destructive text-destructive\"\n : \"text-[#ffffff]\",\n )}\n />\n </button>\n )}\n\n {/* Item count badge */}\n <div className=\"absolute right-2 bottom-2\">\n <Badge\n className=\"bg-foreground text-background shadow-lg backdrop-blur-sm\"\n variant=\"secondary\"\n >\n {badgeText}\n </Badge>\n </div>\n </div>\n\n {/* Title */}\n <div className=\"px-2 pt-2 pb-4\">\n <h3 className=\"text-foreground line-clamp-2 text-sm leading-tight font-bold\">\n {title || \"Untitled\"}\n </h3>\n </div>\n </div>\n </div>\n </button>\n );\n}\n","\"use client\";\n\nimport { useState, useEffect } from \"react\";\nimport {\n Button,\n Input,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"@fluid-app/ui-primitives\";\nimport { Search, ArrowUpDown } from \"lucide-react\";\n\nexport interface SortOption {\n id: string;\n label: string;\n sortBy: string;\n sortDirection: \"asc\" | \"desc\";\n}\n\nexport const PLAYLIST_SORT_OPTIONS: SortOption[] = [\n {\n id: \"title_asc\",\n label: \"Name (A-Z)\",\n sortBy: \"title\",\n sortDirection: \"asc\",\n },\n {\n id: \"title_desc\",\n label: \"Name (Z-A)\",\n sortBy: \"title\",\n sortDirection: \"desc\",\n },\n {\n id: \"created_at_desc\",\n label: \"Date Created (Newest)\",\n sortBy: \"created_at\",\n sortDirection: \"desc\",\n },\n {\n id: \"created_at_asc\",\n label: \"Date Created (Oldest)\",\n sortBy: \"created_at\",\n sortDirection: \"asc\",\n },\n];\n\nexport interface SearchAndSortBarProps {\n searchTerm: string;\n onSearchTermChange: (value: string) => void;\n currentSort: SortOption;\n onSortChange: (sort: SortOption) => void;\n placeholder?: string;\n sortOptions?: SortOption[];\n}\n\nexport function SearchAndSortBar({\n searchTerm,\n onSearchTermChange,\n currentSort,\n onSortChange,\n placeholder = \"Search playlists...\",\n sortOptions = PLAYLIST_SORT_OPTIONS,\n}: SearchAndSortBarProps) {\n return (\n <div className=\"flex items-center gap-3\">\n <div className=\"relative h-full flex-1\">\n <Search className=\"text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2\" />\n <Input\n placeholder={placeholder}\n value={searchTerm}\n onChange={(e) => onSearchTermChange(e.target.value)}\n className=\"pl-9\"\n />\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" className=\"gap-1.5\">\n <ArrowUpDown className=\"h-4 w-4\" />\n <span className=\"hidden sm:inline\">{currentSort.label}</span>\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {sortOptions.map((option) => (\n <DropdownMenuItem\n key={option.id}\n onClick={() => onSortChange(option)}\n className={option.id === currentSort.id ? \"font-semibold\" : \"\"}\n >\n {option.label}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n );\n}\n\nexport function useDebounce<T>(value: T, delay: number): T {\n const [debouncedValue, setDebouncedValue] = useState(value);\n\n useEffect(() => {\n const timer = setTimeout(() => {\n setDebouncedValue(value);\n }, delay);\n return () => clearTimeout(timer);\n }, [value, delay]);\n\n return debouncedValue;\n}\n","\"use client\";\n\nimport { Button } from \"@fluid-app/ui-primitives\";\nimport { X } from \"lucide-react\";\n\nexport interface BulkSelectionBarProps {\n selectedCount: number;\n totalCount: number;\n onSelectAll: () => void;\n onClearSelection: () => void;\n onBulkFavorite: () => void;\n}\n\nexport function BulkSelectionBar({\n selectedCount,\n totalCount,\n onSelectAll,\n onClearSelection,\n onBulkFavorite,\n}: BulkSelectionBarProps) {\n return (\n <div className=\"bg-foreground flex items-center justify-between rounded-lg px-4 py-3\">\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={onSelectAll}\n className=\"text-background hover:text-background/80 text-sm font-medium\"\n >\n {selectedCount === totalCount ? \"Deselect All\" : \"Select All\"}\n </button>\n <span className=\"text-background/70 text-sm\">\n {selectedCount} selected\n </span>\n </div>\n <div className=\"flex items-center gap-3\">\n <Button\n onClick={onBulkFavorite}\n disabled={selectedCount === 0}\n variant=\"secondary\"\n size=\"sm\"\n className=\"bg-background text-foreground hover:bg-background/90 rounded-md px-3 py-1.5 text-xs font-medium disabled:cursor-not-allowed disabled:opacity-50\"\n >\n Favorite\n </Button>\n <button\n type=\"button\"\n onClick={onClearSelection}\n className=\"text-background hover:text-background/80\"\n aria-label=\"Cancel selection\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport { useState, useEffect, useRef, useCallback, useMemo } from \"react\";\nimport {\n useInfiniteQuery,\n useMutation,\n useQueryClient,\n} from \"@tanstack/react-query\";\nimport { playlists } from \"@fluid-app/shareables-api-client\";\nimport { useShareablesClient } from \"@fluid-app/shareables-core\";\nimport {\n Breadcrumb,\n BreadcrumbList,\n BreadcrumbItem,\n BreadcrumbPage,\n Button,\n Skeleton,\n} from \"@fluid-app/ui-primitives\";\nimport { Plus } from \"lucide-react\";\nimport {\n useScreenHeaderActions,\n useScreenHeaderBreadcrumbs,\n} from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { useShareablesUI } from \"../../context\";\nimport { PlaylistCard } from \"../playlists/PlaylistCard\";\nimport {\n SearchAndSortBar,\n PLAYLIST_SORT_OPTIONS,\n useDebounce,\n type SortOption,\n} from \"../playlists/SearchAndSortBar\";\nimport { BulkSelectionBar } from \"../playlists/BulkSelectionBar\";\n\nconst PAGE_SIZE = 12;\n\nconst GRID_CLASS =\n \"grid grid-cols-1 gap-8 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4\";\n\nexport interface PlaylistsListingScreenProps {}\n\nexport function PlaylistsListingScreen(_props: PlaylistsListingScreenProps) {\n const client = useShareablesClient();\n const { navigate, showToast, user, onToggleFavorite, onDeletePlaylist } =\n useShareablesUI();\n const queryClient = useQueryClient();\n\n const headerActions = useMemo(\n () => (\n <Button onClick={() => navigate(\"playlists/new\")} size=\"sm\">\n <Plus className=\"mr-1 h-4 w-4\" />\n Create Playlist\n </Button>\n ),\n [navigate],\n );\n useScreenHeaderActions(headerActions);\n\n const headerBreadcrumbs = useMemo(\n () => (\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">Playlists</BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n ),\n [],\n );\n useScreenHeaderBreadcrumbs(headerBreadcrumbs);\n\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [currentSort, setCurrentSort] = useState<SortOption>(\n PLAYLIST_SORT_OPTIONS[0]!, // Name A-Z\n );\n const [selectedIds, setSelectedIds] = useState<Set<number>>(new Set());\n const debouncedSearch = useDebounce(searchTerm, 300);\n const observerTarget = useRef<HTMLDivElement>(null);\n\n const sortParam =\n currentSort.sortDirection === \"desc\"\n ? `-${currentSort.sortBy}`\n : currentSort.sortBy;\n\n const queryKey = [\"shareables-playlists\", debouncedSearch, sortParam];\n\n const {\n data,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n fetchNextPage,\n isFetched,\n error,\n } = useInfiniteQuery({\n queryKey,\n queryFn: async ({ pageParam }) => {\n const response = await playlists.getPlaylists(client, {\n \"filter[title]\": debouncedSearch || undefined,\n \"page[cursor]\": pageParam,\n \"page[limit]\": PAGE_SIZE,\n sort: sortParam,\n });\n return {\n playlists: response.playlists,\n nextCursor: response.meta.pagination.next_cursor,\n };\n },\n getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,\n initialPageParam: undefined as string | undefined,\n });\n\n const allPlaylists = useMemo(\n () => data?.pages.flatMap((page) => page.playlists) ?? [],\n [data?.pages],\n );\n\n // Infinite scroll\n useEffect(() => {\n const target = observerTarget.current;\n if (!target) return;\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries[0]?.isIntersecting && hasNextPage && !isFetchingNextPage) {\n fetchNextPage();\n }\n },\n { threshold: 0, rootMargin: \"200px\" },\n );\n observer.observe(target);\n return () => observer.disconnect();\n }, [fetchNextPage, hasNextPage, isFetchingNextPage]);\n\n // --- Selection ---\n const hasSelection = selectedIds.size > 0;\n\n const handleToggleSelection = useCallback((id: number, selected: boolean) => {\n setSelectedIds((prev) => {\n const next = new Set(prev);\n if (selected) next.add(id);\n else next.delete(id);\n return next;\n });\n }, []);\n\n const handleSelectAll = useCallback(() => {\n if (selectedIds.size === allPlaylists.length) {\n setSelectedIds(new Set());\n } else {\n setSelectedIds(new Set(allPlaylists.map((p) => p.id)));\n }\n }, [selectedIds.size, allPlaylists]);\n\n const handleClearSelection = useCallback(() => {\n setSelectedIds(new Set());\n }, []);\n\n // --- Favorites ---\n const favoriteMutation = useMutation({\n mutationFn: (playlistId: number) => {\n if (!onToggleFavorite) {\n return Promise.reject(new Error(\"Favorite not available\"));\n }\n return onToggleFavorite({\n favoriteableId: playlistId,\n favoriteableType: \"Library\",\n });\n },\n onMutate: async (playlistId) => {\n await queryClient.cancelQueries({ queryKey });\n const previousData = queryClient.getQueryData(queryKey);\n\n queryClient.setQueryData<typeof data>(queryKey, (oldData) => {\n if (!oldData) return oldData;\n return {\n ...oldData,\n pages: oldData.pages.map((page) => ({\n ...page,\n playlists: page.playlists.map((p) =>\n p.id === playlistId ? { ...p, is_favorited: !p.is_favorited } : p,\n ),\n })),\n };\n });\n\n return { previousData, capturedKey: queryKey };\n },\n onError: (_err, _playlistId, context) => {\n if (context?.previousData) {\n queryClient.setQueryData(context.capturedKey, context.previousData);\n }\n showToast({ title: \"Failed to update favorite\", type: \"error\" });\n },\n onSuccess: (result) => {\n showToast({\n title: result.is_favorited\n ? \"Added to favorites\"\n : \"Removed from favorites\",\n type: \"success\",\n });\n },\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey: [\"shareables-playlists\"] });\n },\n });\n\n const handleFavorite = useCallback(\n (playlistId: number) => {\n if (!onToggleFavorite) return;\n favoriteMutation.mutate(playlistId);\n },\n [onToggleFavorite, favoriteMutation],\n );\n\n const handleBulkFavorite = useCallback(() => {\n for (const id of selectedIds) {\n handleFavorite(id);\n }\n setSelectedIds(new Set());\n }, [selectedIds, handleFavorite]);\n\n // --- Card actions ---\n const handleEdit = useCallback(\n (playlistId: number) => {\n navigate(`playlists/${playlistId}/edit`);\n },\n [navigate],\n );\n\n const handleDelete = useCallback(\n async (playlistId: number) => {\n if (!onDeletePlaylist) return;\n if (!window.confirm(\"Are you sure you want to delete this playlist?\"))\n return;\n try {\n await onDeletePlaylist(playlistId);\n queryClient.invalidateQueries({ queryKey: [\"shareables-playlists\"] });\n showToast({ title: \"Playlist deleted\", type: \"success\" });\n } catch {\n showToast({\n title: \"Failed to delete playlist\",\n type: \"error\",\n });\n }\n },\n [onDeletePlaylist, queryClient, showToast],\n );\n\n // Loading skeleton\n if (isLoading) {\n return (\n <div className=\"mx-auto space-y-6 px-2 md:px-10 md:py-6\">\n <div className=\"flex items-center gap-3\">\n <Skeleton className=\"h-10 flex-1\" />\n <Skeleton className=\"h-10 w-24\" />\n </div>\n <div className={GRID_CLASS}>\n {Array.from({ length: 8 }).map((_, i) => (\n <div key={i} className=\"space-y-2\">\n <Skeleton className=\"aspect-square w-full rounded-lg\" />\n <Skeleton className=\"h-4 w-3/4\" />\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n return (\n <>\n <div className=\"mx-auto px-2 md:px-10\">\n {hasSelection ? (\n <div className=\"my-4\">\n <BulkSelectionBar\n selectedCount={selectedIds.size}\n totalCount={allPlaylists.length}\n onSelectAll={handleSelectAll}\n onClearSelection={handleClearSelection}\n onBulkFavorite={handleBulkFavorite}\n />\n </div>\n ) : (\n <div className=\"flex items-center justify-end py-4\">\n <div className=\"w-full max-w-sm\">\n <SearchAndSortBar\n searchTerm={searchTerm}\n onSearchTermChange={setSearchTerm}\n currentSort={currentSort}\n onSortChange={setCurrentSort}\n placeholder=\"Search playlists...\"\n />\n </div>\n </div>\n )}\n </div>\n\n <div className=\"mx-auto space-y-8 px-2 md:px-10 md:py-8\">\n {allPlaylists.length > 0 && (\n <div className={GRID_CLASS}>\n {allPlaylists.map((playlist) => {\n const firstItem = playlist.items?.[0];\n const imageUrl =\n playlist.image_url ||\n firstItem?.relateable?.image_url ||\n firstItem?.relateable?.compressed_image_url;\n const itemCount = playlist.items?.length ?? 0;\n const canEdit = playlist.user_id === user?.id;\n\n return (\n <PlaylistCard\n key={playlist.id}\n title={playlist.title || \"Untitled Playlist\"}\n imageUrl={imageUrl}\n href={`playlists/${playlist.id}`}\n itemCount={itemCount}\n isFavorited={playlist.is_favorited}\n isSelectable\n isSelected={selectedIds.has(playlist.id)}\n canEdit={canEdit}\n onSelectionChange={(selected) =>\n handleToggleSelection(playlist.id, selected)\n }\n onToggleFavorite={\n onToggleFavorite\n ? () => handleFavorite(playlist.id)\n : undefined\n }\n onEdit={() => handleEdit(playlist.id)}\n onDelete={\n onDeletePlaylist\n ? () => void handleDelete(playlist.id)\n : undefined\n }\n />\n );\n })}\n </div>\n )}\n\n <div ref={observerTarget} className=\"h-1\" />\n\n {isFetchingNextPage && (\n <div className=\"flex items-center justify-center\">\n <div className=\"border-primary h-6 w-6 animate-spin rounded-full border-2 border-t-transparent\" />\n </div>\n )}\n\n {isFetched && allPlaylists.length === 0 && (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <p className=\"text-muted-foreground text-sm\">\n {searchTerm\n ? `No playlists match \"${searchTerm}\". Try a different search term.`\n : \"There are no playlists available at the moment.\"}\n </p>\n </div>\n )}\n\n {error && (\n <p className=\"bg-destructive/10 text-destructive mx-auto my-6 rounded-lg px-3 py-2\">\n Error: {error.message}\n </p>\n )}\n </div>\n </>\n );\n}\n","export const NAVIGABLE_RELATEABLE_TYPES = new Set<string>([\n \"Product\",\n \"Page\",\n \"Medium\",\n]);\n","\"use client\";\n\nimport { useRenderImage } from \"../../context\";\nimport { ShoppingBag, ArrowUpRight } from \"lucide-react\";\nimport type { shareables } from \"@fluid-app/shareables-api-client\";\n\nconst DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\ntype TaggedProduct = shareables.Relateable;\n\ninterface TaggedProductsListProps {\n products: TaggedProduct[];\n /** Called when user clicks a product card */\n onProductClick?: (productId: number) => void;\n}\n\nfunction resolvePrice(product: TaggedProduct): string | null {\n let price = product.display_price || product.price;\n\n if (product.variants && product.variants.length > 0) {\n const firstVariant = product.variants[0];\n if (firstVariant?.display_price) {\n price = firstVariant.display_price;\n } else if (firstVariant?.price) {\n price = firstVariant.price;\n }\n }\n\n if (!price) return null;\n\n const numPrice = parseFloat(price.replace(/[^0-9.-]/g, \"\"));\n if (Number.isNaN(numPrice) || numPrice <= 0) return null;\n\n if (price.includes(\"$\")) return price;\n\n return `$${numPrice.toFixed(2)}`;\n}\n\nexport default function TaggedProductsList({\n products,\n onProductClick,\n}: TaggedProductsListProps) {\n const renderImage = useRenderImage();\n\n const handleProductClick = (\n productId: number | undefined,\n e: React.MouseEvent,\n ) => {\n e.stopPropagation();\n if (productId == null) return;\n onProductClick?.(productId);\n };\n\n if (!products || products.length === 0) {\n return (\n <div className=\"mb-6\">\n <div className=\"mb-4 px-4\">\n <h2 className=\"text-foreground text-[15px] leading-[1.4] font-semibold\">\n Tagged Products (0)\n </h2>\n </div>\n <div className=\"flex flex-1 items-center justify-center px-6 py-12\">\n <div className=\"text-center\">\n <div className=\"text-muted-foreground mb-2\">\n <ShoppingBag className=\"mx-auto h-12 w-12\" />\n </div>\n <div className=\"text-foreground mb-1 text-sm\">\n No Tagged Products\n </div>\n <div className=\"text-muted-foreground text-xs\">\n No products are tagged in this playlist\n </div>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"mb-6\">\n {/* Header */}\n <div className=\"mb-4 px-4\">\n <h2 className=\"text-foreground text-[15px] leading-[1.4] font-semibold\">\n Tagged Products ({products.length})\n </h2>\n </div>\n\n {/* Content */}\n <div className=\"px-4\">\n <div className=\"scrollbar-none flex gap-4 overflow-x-auto pb-2\">\n {products.map((product, index) => {\n const imageUrl =\n product.images?.[0]?.image_url ||\n product.image_url ||\n product.compressed_image_url ||\n DEFAULT_IMAGE;\n const title = product.title || \"Untitled\";\n const displayPrice = resolvePrice(product);\n return (\n <button\n key={product.id ?? index}\n onClick={(e) => handleProductClick(product.id, e)}\n className=\"group bg-muted hover:bg-muted-600 flex w-[168px] shrink-0 flex-col gap-2 rounded-lg p-3 text-left transition-all\"\n >\n {/* Product Image */}\n <div className=\"bg-background relative h-[168px] w-full overflow-hidden rounded-lg\">\n {renderImage({\n src: imageUrl,\n alt: title,\n fill: true,\n className: \"object-cover\",\n })}\n </div>\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-col gap-1\">\n {/* Title with hover arrow */}\n <h3 className=\"text-foreground flex items-start gap-1.5 text-[15px] leading-[1.4] font-semibold\">\n <span className=\"line-clamp-2\">{title}</span>\n <ArrowUpRight className=\"text-foreground mt-0.5 h-3 w-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-100\" />\n </h3>\n\n {/* Price */}\n {displayPrice && (\n <div className=\"text-foreground text-[15px] leading-[1.4] font-normal\">\n {displayPrice}\n </div>\n )}\n </div>\n </button>\n );\n })}\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport { useRenderImage } from \"../../context\";\nimport { ListMusic, Play, ArrowUpRight } from \"lucide-react\";\nimport type { shareables } from \"@fluid-app/shareables-api-client\";\nimport { NAVIGABLE_RELATEABLE_TYPES } from \"../../constants\";\n\nconst DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\ninterface PlaylistItemsListProps {\n items: shareables.PlaylistItem[];\n onSelectItem?: (index: number) => void;\n selectedItemIndex?: number;\n /** Called when user clicks to navigate to an item's detail page */\n onNavigateToItem?: (itemId: number, relateableType?: string | null) => void;\n}\n\nfunction getItemType(type?: string | null, kind?: string | null) {\n if (type === \"Product\") return \"Product\";\n if (type === \"EnrollmentPack\") return \"Enrollment\";\n if (type === \"Page\") return \"Page\";\n if (kind === \"video\") return \"Video\";\n if (kind === \"image\") return \"Image\";\n return \"Media\";\n}\n\nfunction formatVideoLength(seconds?: number) {\n if (seconds == null) return null;\n const mins = Math.floor(seconds / 60);\n const secs = seconds % 60;\n return `${String(mins).padStart(2, \"0\")}:${String(secs).padStart(2, \"0\")}`;\n}\n\nfunction getFileType(mediaFormat?: string | null, imageUrl?: string | null) {\n if (mediaFormat) {\n return mediaFormat.toLowerCase();\n }\n if (imageUrl) {\n const urlParts = imageUrl.split(\".\");\n const extension = urlParts[urlParts.length - 1]?.split(\"?\")[0];\n if (extension) {\n return extension.toLowerCase();\n }\n }\n return null;\n}\n\nexport default function PlaylistItemsList({\n items,\n onSelectItem,\n selectedItemIndex = 0,\n onNavigateToItem,\n}: PlaylistItemsListProps) {\n const handleNavigateToItem = (\n itemId: number,\n type?: string | null,\n e?: React.MouseEvent,\n ) => {\n e?.stopPropagation();\n onNavigateToItem?.(itemId, type);\n };\n\n const handleCardClick = (index: number) => {\n onSelectItem?.(index);\n };\n\n const renderImage = useRenderImage();\n\n const renderContent = () => {\n if (!items || items.length === 0) {\n return (\n <div className=\"flex flex-1 items-center justify-center px-6 py-12\">\n <div className=\"text-center\">\n <div className=\"mb-2 text-gray-400\">\n <ListMusic className=\"mx-auto h-12 w-12\" />\n </div>\n <div className=\"mb-1 text-sm text-gray-500\">No Items</div>\n <div className=\"text-xs text-gray-400\">\n This playlist doesn't have any items yet\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-3\">\n {items.map((item, index) => {\n const relateable = item.relateable;\n const imageUrl =\n relateable?.image_url ||\n relateable?.compressed_image_url ||\n DEFAULT_IMAGE;\n const title = relateable?.title || \"Untitled\";\n const rawPrice = relateable?.display_price || relateable?.price;\n const price =\n rawPrice && parseFloat(rawPrice.replace(/[^0-9.-]/g, \"\")) > 0\n ? rawPrice\n : null;\n const itemType = getItemType(item.relateable_type, relateable?.kind);\n const isVideo = relateable?.kind === \"video\";\n const isImage = relateable?.kind === \"image\";\n const isProduct = item.relateable_type === \"Product\";\n const isNavigable = NAVIGABLE_RELATEABLE_TYPES.has(\n item.relateable_type ?? \"\",\n );\n const videoLength = formatVideoLength(relateable?.duration);\n const fileType = getFileType(\n relateable?.media_format,\n relateable?.image_url,\n );\n const isSelected = index === selectedItemIndex;\n\n return (\n <div\n key={item.id}\n onClick={() => handleCardClick(index)}\n className={`hover:bg-muted-600 flex w-full cursor-pointer items-start gap-3 rounded-lg p-3 text-left transition-all ${\n isSelected ? \"bg-muted-600\" : \"bg-muted\"\n }`}\n >\n {/* Thumbnail */}\n <div className=\"bg-background relative h-[104px] w-[104px] shrink-0 overflow-hidden rounded-lg\">\n {renderImage({\n src: imageUrl,\n alt: title,\n fill: true,\n className: \"object-cover\",\n })}\n {/* Play button overlay for videos */}\n {isVideo && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <div className=\"bg-background/90 flex h-10 w-10 items-center justify-center rounded-full shadow-md\">\n <Play className=\"text-foreground ml-0.5 h-4 w-4\" />\n </div>\n </div>\n )}\n </div>\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-1 flex-col gap-1 py-1\">\n {/* Title - clickable to navigate for supported types */}\n {isNavigable ? (\n <button\n onClick={(e) =>\n handleNavigateToItem(\n relateable?.id ?? item.id,\n item.relateable_type,\n e,\n )\n }\n className=\"group text-foreground flex items-start gap-1.5 text-left text-[15px] leading-[1.4] font-semibold hover:underline\"\n >\n <span className=\"line-clamp-2\">{title}</span>\n <ArrowUpRight className=\"text-foreground mt-0.5 h-3 w-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-100\" />\n </button>\n ) : (\n <div className=\"text-foreground text-[15px] leading-[1.4] font-semibold\">\n <span className=\"line-clamp-2\">{title}</span>\n </div>\n )}\n\n {/* Product: Price */}\n {isProduct && price && (\n <div className=\"text-foreground text-[15px] leading-[1.4] font-normal\">\n {price}\n </div>\n )}\n\n {/* Video: Duration */}\n {isVideo && (\n <div className=\"text-muted-foreground text-[13px] leading-[1.4] font-normal\">\n {videoLength || \"Video\"}\n </div>\n )}\n\n {/* Image: File type */}\n {isImage && (\n <div className=\"text-muted-foreground text-[13px] leading-[1.4] font-normal\">\n {fileType || \"image\"}\n </div>\n )}\n\n {/* Badge */}\n <div className=\"mt-1\">\n <span className=\"bg-background text-foreground inline-flex items-center rounded-md px-2.5 py-1 text-[13px] leading-[1.4] font-medium\">\n {itemType}\n </span>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n );\n };\n\n return (\n <div className=\"hide-scrollbar flex h-full flex-col\">\n {/* Header */}\n <div className=\"mb-4 px-4 pt-4\">\n <h2 className=\"text-foreground text-[15px] leading-[1.4] font-semibold\">\n Items in playlist ({items.length})\n </h2>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto px-4 pb-4\">{renderContent()}</div>\n </div>\n );\n}\n","\"use client\";\n\nimport { useState, useCallback, useMemo } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { playlists } from \"@fluid-app/shareables-api-client\";\nimport { NAVIGABLE_RELATEABLE_TYPES } from \"../../constants\";\nimport {\n useShareablesClient,\n useShareLink,\n shareablesKeys,\n} from \"@fluid-app/shareables-core\";\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n Button,\n Separator,\n Spinner,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"@fluid-app/ui-primitives\";\nimport { Pencil, Trash2 } from \"lucide-react\";\nimport SharePageImageDisplay from \"../SharePage/SharePageImageDisplay\";\nimport ShareLinkSection from \"../SharePage/ShareLinkSection\";\nimport TaggedProductsList from \"../SharePage/TaggedProductsList\";\nimport PlaylistItemsList from \"../SharePage/PlaylistItemsList\";\nimport { stripTags } from \"../../utils/strip-tags\";\nimport {\n useScreenHeaderActions,\n useScreenHeaderBreadcrumbs,\n} from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { useShareablesUI } from \"../../context\";\n\nconst DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\nexport interface PlaylistDetailScreenProps {\n playlistId: string;\n onNavigate?: (screen: string, detailId?: string) => void;\n onBack?: () => void;\n}\n\nexport function PlaylistDetailScreen({\n playlistId,\n onNavigate,\n}: PlaylistDetailScreenProps) {\n const client = useShareablesClient();\n const { navigate, showToast, onDeletePlaylist, user } = useShareablesUI();\n const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false);\n const [selectedPlaylistItemIndex, setSelectedPlaylistItemIndex] = useState(0);\n const [isDeleteOpen, setIsDeleteOpen] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n\n // Fetch playlist\n const { data: playlistResponse, isLoading } = useQuery({\n queryKey: shareablesKeys.playlists.detail(Number(playlistId)),\n queryFn: () => playlists.getPlaylistById(client, Number(playlistId)),\n });\n\n // Share link — relateableType for playlist is \"Library\"\n const {\n shareLink,\n loading: shareLinkLoading,\n error: shareLinkError,\n } = useShareLink({ id: Number(playlistId) }, \"Library\");\n\n const playlist = playlistResponse?.playlist;\n\n const canEdit = playlist?.user_id === user?.id;\n const displayTitle = playlist?.title || \"\";\n\n const headerActions = useMemo(() => {\n if (!canEdit) return null;\n return (\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => navigate(`playlists/${playlistId}/edit`)}\n >\n <Pencil className=\"mr-1 h-3.5 w-3.5\" />\n Edit\n </Button>\n {onDeletePlaylist && (\n <Button\n variant=\"destructive\"\n size=\"sm\"\n onClick={() => setIsDeleteOpen(true)}\n >\n <Trash2 className=\"mr-1 h-3.5 w-3.5\" />\n Delete\n </Button>\n )}\n </div>\n );\n }, [canEdit, navigate, playlistId, onDeletePlaylist]);\n useScreenHeaderActions(headerActions);\n\n const headerBreadcrumbs = useMemo(\n () => (\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbLink\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n navigate(\"playlists\");\n }}\n >\n Playlists\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {displayTitle || \"Playlist\"}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n ),\n [displayTitle, navigate],\n );\n useScreenHeaderBreadcrumbs(headerBreadcrumbs);\n\n const handleDelete = useCallback(async () => {\n if (!onDeletePlaylist) return;\n setIsDeleting(true);\n try {\n await onDeletePlaylist(Number(playlistId));\n showToast({ title: \"Playlist deleted successfully\", type: \"success\" });\n navigate(\"playlists\");\n } catch {\n showToast({ title: \"Failed to delete playlist\", type: \"error\" });\n } finally {\n setIsDeleting(false);\n setIsDeleteOpen(false);\n }\n }, [onDeletePlaylist, playlistId, showToast, navigate]);\n\n // Get the selected playlist item\n const selectedPlaylistItem = playlist?.items?.[selectedPlaylistItemIndex];\n\n // Derive display values\n const displayImage =\n selectedPlaylistItem?.relateable?.image_url ||\n selectedPlaylistItem?.relateable?.compressed_image_url ||\n DEFAULT_IMAGE;\n const displayDescription =\n playlist?.description ||\n playlist?.search_engine_optimizer?.description ||\n \"\";\n const strippedDescription = stripTags(displayDescription);\n const shouldShowReadMore = strippedDescription.length > 150;\n\n // Handle video display for playlist items\n const displayVideo =\n selectedPlaylistItem?.relateable?.kind === \"video\"\n ? selectedPlaylistItem?.relateable?.video_url || undefined\n : undefined;\n const isVideo =\n selectedPlaylistItem?.relateable?.kind === \"video\" && !!displayVideo;\n\n // Tagged products — filter playlist items where relateable_type is \"Product\"\n const taggedProducts =\n playlist?.items\n ?.filter((item) => item.relateable_type === \"Product\")\n .map((item) => item.relateable)\n .filter((p): p is NonNullable<typeof p> => !!p) || [];\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center py-16\">\n <Spinner className=\"size-8\" />\n </div>\n );\n }\n\n // No data state\n if (!playlist) {\n return (\n <div className=\"flex flex-col items-center justify-center py-16\">\n <p className=\"text-destructive text-sm\">\n Playlist not found or failed to load.\n </p>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col gap-4 px-4 py-4 md:px-10 md:py-6\">\n <div className=\"mx-auto flex w-full max-w-480 flex-col gap-6 md:h-[calc(100vh-140px)] md:flex-row\">\n {/* Left Column - Image/Video with badge */}\n <div className=\"aspect-square w-full md:aspect-auto md:h-full\">\n <div className=\"relative h-full overflow-hidden rounded-2xl\">\n <SharePageImageDisplay\n displayImage={displayImage}\n displayTitle={displayTitle}\n displayVideo={displayVideo}\n isVideo={isVideo}\n badgeLabel=\"Playlist\"\n />\n </div>\n </div>\n\n {/* Right Column - Details and Related Sharables */}\n <div className=\"flex w-full flex-col md:overflow-y-auto\">\n {/* Details Panel */}\n <div className=\"space-y-4\">\n {/* Title */}\n <h1 className=\"text-foreground text-[26px] leading-[1.2] font-semibold\">\n {displayTitle}\n </h1>\n\n {/* Description */}\n {strippedDescription && (\n <div className=\"text-foreground/70 text-sm leading-relaxed\">\n <div\n className={\n !isDescriptionExpanded && shouldShowReadMore\n ? \"line-clamp-3\"\n : \"\"\n }\n >\n {strippedDescription}\n </div>\n {shouldShowReadMore && (\n <Button\n onClick={() =>\n setIsDescriptionExpanded(!isDescriptionExpanded)\n }\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-foreground hover:text-foreground/80 mt-1 h-auto p-0 text-xs font-normal underline\"\n >\n {isDescriptionExpanded ? \"Read less\" : \"Read more\"}\n </Button>\n )}\n </div>\n )}\n\n {/* Share Link Section */}\n <ShareLinkSection\n shareLink={shareLinkError ? null : shareLink || null}\n loading={shareLinkLoading}\n displayTitle={displayTitle}\n isVideo={isVideo}\n relateableId={Number(playlistId)}\n relateableType=\"Library\"\n />\n </div>\n\n <Separator className=\"border-foreground my-4\" />\n\n <div className=\"hide-scrollbar bg-background h-full overflow-y-auto rounded-lg\">\n {/* Tagged Products Section */}\n <TaggedProductsList\n products={taggedProducts}\n onProductClick={(productId) =>\n onNavigate?.(\"product\", String(productId))\n }\n />\n\n {/* Playlist Items Section */}\n <PlaylistItemsList\n items={playlist?.items || []}\n onSelectItem={setSelectedPlaylistItemIndex}\n selectedItemIndex={selectedPlaylistItemIndex}\n onNavigateToItem={(itemId, relateableType) => {\n if (!NAVIGABLE_RELATEABLE_TYPES.has(relateableType ?? \"\"))\n return;\n if (relateableType === \"Product\") {\n onNavigate?.(\"product\", String(itemId));\n } else if (relateableType === \"Page\") {\n onNavigate?.(\"page\", String(itemId));\n } else if (relateableType === \"Medium\") {\n onNavigate?.(\"media\", String(itemId));\n }\n }}\n />\n </div>\n </div>\n </div>\n {/* Delete confirmation dialog */}\n <Dialog open={isDeleteOpen} onOpenChange={setIsDeleteOpen}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Delete Playlist</DialogTitle>\n <DialogDescription>\n Are you sure you want to delete this playlist? This action cannot\n be undone.\n </DialogDescription>\n </DialogHeader>\n <DialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setIsDeleteOpen(false)}\n disabled={isDeleting}\n >\n Cancel\n </Button>\n <Button\n variant=\"destructive\"\n onClick={() => void handleDelete()}\n disabled={isDeleting}\n >\n {isDeleting ? \"Deleting...\" : \"Delete\"}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n );\n}\n","\"use client\";\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nexport type PlaylistSearchEngineOptimizer = {\n id?: number;\n title: string;\n description: string;\n image_url: string;\n image_path: string;\n block_crawler: boolean;\n};\n\nexport type PlaylistFormState = {\n id?: number;\n title: string;\n description: string | null;\n image_url: string | null;\n slug: string | null;\n custom_slug: boolean | null;\n category_id: number | null;\n application_theme_template_id: number | null;\n active: boolean;\n company_library: boolean | null;\n indexed_for_seo: boolean;\n countries: string[];\n search_engine_optimizer: PlaylistSearchEngineOptimizer | null;\n};\n\nexport type PlaylistLike = {\n id: number;\n title?: string | null;\n description?: string | null;\n image_url?: string | null;\n slug?: string | null;\n custom_slug?: boolean | null;\n category_id?: number | null;\n application_theme_template_id?: number | null;\n active?: boolean;\n company_library?: boolean | null;\n indexed_for_seo?: boolean;\n countries?: Array<{ iso: string }> | null;\n search_engine_optimizer?: PlaylistSearchEngineOptimizer | null;\n canonical_url?: string | null;\n preview_link?: string | null;\n items?: Array<unknown> | null;\n};\n\ntype ValidationErrors = {\n [K in keyof PlaylistFormState]?: string;\n};\n\ninterface PlaylistFormContextValue {\n form: PlaylistFormState;\n validationErrors: ValidationErrors;\n isDirty: boolean;\n updateField: <K extends keyof PlaylistFormState>(\n field: K,\n value: PlaylistFormState[K],\n ) => void;\n updateSeo: (\n updates: Partial<PlaylistSearchEngineOptimizer> & {\n slug?: string | null;\n },\n ) => void;\n validateForm: () => boolean;\n clearFieldError: (field: keyof PlaylistFormState) => void;\n resetForm: (nextPlaylist?: PlaylistLike) => void;\n}\n\nconst PlaylistFormContext = createContext<PlaylistFormContextValue | undefined>(\n undefined,\n);\n\nconst createDefaultSeo = (title: string): PlaylistSearchEngineOptimizer => ({\n title: title || \"\",\n description: \"\",\n image_url: \"\",\n image_path: \"\",\n block_crawler: false,\n});\n\nconst mapPlaylistToForm = (playlist?: PlaylistLike): PlaylistFormState => ({\n id: playlist?.id,\n title: playlist?.title ?? \"\",\n description: playlist?.description ?? null,\n image_url: playlist?.image_url ?? null,\n slug: playlist?.slug ?? \"\",\n custom_slug: playlist?.custom_slug ?? false,\n category_id: playlist?.category_id ?? null,\n application_theme_template_id:\n playlist?.application_theme_template_id ?? null,\n active: playlist?.active ?? false,\n company_library: playlist?.company_library ?? true,\n indexed_for_seo: playlist?.indexed_for_seo ?? false,\n countries: playlist?.countries?.map((country) => country.iso) ?? [],\n search_engine_optimizer: playlist?.search_engine_optimizer ?? null,\n});\n\nexport function PlaylistFormProvider({\n children,\n playlist,\n}: {\n children: React.ReactNode;\n playlist?: PlaylistLike;\n}) {\n const initialForm = useMemo(() => mapPlaylistToForm(playlist), [playlist]);\n const [form, setForm] = useState<PlaylistFormState>(initialForm);\n const [validationErrors, setValidationErrors] = useState<ValidationErrors>(\n {},\n );\n\n const isDirty = useMemo(() => {\n return JSON.stringify(form) !== JSON.stringify(initialForm);\n }, [form, initialForm]);\n\n const prevPlaylistRef = useRef<string>(JSON.stringify(playlist));\n\n useEffect(() => {\n const serialized = JSON.stringify(playlist);\n if (serialized === prevPlaylistRef.current) return;\n prevPlaylistRef.current = serialized;\n setForm(mapPlaylistToForm(playlist));\n }, [playlist]);\n\n const updateField = useCallback(\n <K extends keyof PlaylistFormState>(\n field: K,\n value: PlaylistFormState[K],\n ) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n if (validationErrors[field]) {\n setValidationErrors((prev) => {\n const next = { ...prev };\n delete next[field];\n return next;\n });\n }\n },\n [validationErrors],\n );\n\n const updateSeo = useCallback(\n (\n updates: Partial<PlaylistSearchEngineOptimizer> & {\n slug?: string | null;\n },\n ) => {\n setForm((prev) => {\n const currentSeo =\n prev.search_engine_optimizer ?? createDefaultSeo(prev.title);\n const nextSeo = { ...currentSeo };\n\n if (updates.title !== undefined) nextSeo.title = updates.title ?? \"\";\n if (updates.description !== undefined)\n nextSeo.description = updates.description ?? \"\";\n if (updates.image_url !== undefined)\n nextSeo.image_url = updates.image_url ?? \"\";\n if (updates.image_path !== undefined)\n nextSeo.image_path = updates.image_path ?? \"\";\n if (updates.block_crawler !== undefined)\n nextSeo.block_crawler = updates.block_crawler ?? false;\n\n const nextSlug =\n updates.slug !== undefined ? (updates.slug ?? null) : prev.slug;\n\n return {\n ...prev,\n slug: nextSlug,\n search_engine_optimizer: nextSeo,\n };\n });\n },\n [],\n );\n\n const validateForm = useCallback(() => {\n const errors: ValidationErrors = {};\n if (!form.title || !form.title.trim()) {\n errors.title = \"Title is required\";\n }\n setValidationErrors(errors);\n return Object.keys(errors).length === 0;\n }, [form.title]);\n\n const clearFieldError = useCallback((field: keyof PlaylistFormState) => {\n setValidationErrors((prev) => {\n const next = { ...prev };\n delete next[field];\n return next;\n });\n }, []);\n\n const resetForm = useCallback(\n (nextPlaylist?: PlaylistLike) => {\n setForm(mapPlaylistToForm(nextPlaylist ?? playlist));\n setValidationErrors({});\n },\n [playlist],\n );\n\n const value = useMemo(\n () => ({\n form,\n validationErrors,\n isDirty,\n updateField,\n updateSeo,\n validateForm,\n clearFieldError,\n resetForm,\n }),\n [\n form,\n validationErrors,\n isDirty,\n updateField,\n updateSeo,\n validateForm,\n clearFieldError,\n resetForm,\n ],\n );\n\n return (\n <PlaylistFormContext.Provider value={value}>\n {children}\n </PlaylistFormContext.Provider>\n );\n}\n\nexport function usePlaylistForm() {\n const context = useContext(PlaylistFormContext);\n if (!context) {\n throw new Error(\"usePlaylistForm must be used within PlaylistFormProvider\");\n }\n return context;\n}\n","\"use client\";\n\nimport {\n createContext,\n useContext,\n useState,\n useCallback,\n useMemo,\n useEffect,\n useRef,\n} from \"react\";\n\nexport interface PlaylistItemLike {\n id: number;\n order?: number;\n relateable_type?: string | null;\n relateable?: {\n id?: number;\n title?: string | null;\n image_url?: string | null;\n compressed_image_url?: string | null;\n kind?: string | null;\n video_url?: string | null;\n price?: string | null;\n sku?: string | null;\n description?: { body?: string | null } | null;\n [key: string]: unknown;\n };\n}\n\nexport interface PlaylistItemsContextValue {\n items: PlaylistItemLike[];\n setItems: (items: PlaylistItemLike[]) => void;\n addItem: (item: PlaylistItemLike) => void;\n removeItem: (itemId: number) => void;\n updateItems: (\n items:\n | PlaylistItemLike[]\n | ((prev: PlaylistItemLike[]) => PlaylistItemLike[]),\n ) => void;\n isDirty: boolean;\n}\n\nconst PlaylistItemsContext = createContext<\n PlaylistItemsContextValue | undefined\n>(undefined);\n\nexport function usePlaylistItems() {\n const context = useContext(PlaylistItemsContext);\n if (!context) {\n throw new Error(\n \"usePlaylistItems must be used within PlaylistItemsProvider\",\n );\n }\n return context;\n}\n\nexport function PlaylistItemsProvider({\n children,\n initialItems = [],\n}: {\n children: React.ReactNode;\n initialItems?: PlaylistItemLike[];\n}) {\n const [items, setItems] = useState<PlaylistItemLike[]>(initialItems);\n const [initialSnapshot, setInitialSnapshot] =\n useState<PlaylistItemLike[]>(initialItems);\n\n const prevInitialRef = useRef<string>(JSON.stringify(initialItems));\n\n useEffect(() => {\n const serialized = JSON.stringify(initialItems);\n if (serialized === prevInitialRef.current) return;\n prevInitialRef.current = serialized;\n setItems(initialItems);\n setInitialSnapshot(initialItems);\n }, [initialItems]);\n\n const isDirty = useMemo(\n () => JSON.stringify(items) !== JSON.stringify(initialSnapshot),\n [items, initialSnapshot],\n );\n\n const addItem = useCallback((item: PlaylistItemLike) => {\n setItems((prev) => [...prev, item]);\n }, []);\n\n const removeItem = useCallback((itemId: number) => {\n setItems((prev) => prev.filter((item) => item.id !== itemId));\n }, []);\n\n const updateItems = useCallback(\n (\n newItems:\n | PlaylistItemLike[]\n | ((prev: PlaylistItemLike[]) => PlaylistItemLike[]),\n ) => {\n setItems((prev) =>\n typeof newItems === \"function\" ? newItems(prev) : newItems,\n );\n },\n [],\n );\n\n const value: PlaylistItemsContextValue = useMemo(\n () => ({\n items,\n setItems,\n addItem,\n removeItem,\n updateItems,\n isDirty,\n }),\n [items, addItem, removeItem, updateItems, isDirty],\n );\n\n return (\n <PlaylistItemsContext.Provider value={value}>\n {children}\n </PlaylistItemsContext.Provider>\n );\n}\n","\"use client\";\n\nimport { useMemo, useState } from \"react\";\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"@fluid-app/ui-primitives\";\nimport { Trash2 } from \"lucide-react\";\nimport {\n useScreenHeaderActions,\n useScreenHeaderBreadcrumbs,\n} from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { usePlaylistForm } from \"./PlaylistFormProvider\";\nimport { usePlaylistItems } from \"./PlaylistItemsContext\";\nimport { useShareablesUI } from \"../../../context\";\n\ninterface PlaylistFormHeaderProps {\n playlistId?: string;\n isEditMode: boolean;\n onSubmit: () => Promise<void>;\n isSaving: boolean;\n onBack?: () => void;\n}\n\nexport function PlaylistFormHeader({\n playlistId,\n isEditMode,\n onSubmit,\n isSaving,\n}: PlaylistFormHeaderProps) {\n const { form, isDirty: formIsDirty } = usePlaylistForm();\n const { isDirty: itemsIsDirty } = usePlaylistItems();\n const { onDeletePlaylist, showToast, navigate } = useShareablesUI();\n const [isDeleteOpen, setIsDeleteOpen] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n\n const isFormValid = useMemo(() => {\n return !!(form.title && form.title.trim());\n }, [form.title]);\n\n const isDirty = formIsDirty || itemsIsDirty;\n const isMutating = isDeleting || isSaving;\n\n const handleDelete = async () => {\n if (!playlistId || !onDeletePlaylist) return;\n setIsDeleting(true);\n try {\n await onDeletePlaylist(Number(playlistId));\n showToast({ title: \"Playlist deleted successfully\", type: \"success\" });\n navigate(\"playlists\");\n } catch (error) {\n showToast({\n title: \"Failed to delete playlist\",\n type: \"error\",\n error,\n });\n } finally {\n setIsDeleting(false);\n setIsDeleteOpen(false);\n }\n };\n\n // Push actions/breadcrumbs into the portal shell header.\n // Returns true if inside a ScreenHeaderProvider (portal shell).\n const headerActions = useMemo(\n () => (\n <div className=\"flex items-center gap-2\">\n {playlistId && onDeletePlaylist && (\n <Button\n variant=\"destructive\"\n size=\"sm\"\n onClick={() => setIsDeleteOpen(true)}\n disabled={isMutating}\n >\n <Trash2 className=\"mr-1 h-4 w-4\" />\n Delete\n </Button>\n )}\n <Button\n onClick={() => {\n onSubmit().catch(() => {});\n }}\n disabled={isMutating || !isDirty || !isFormValid}\n size=\"sm\"\n >\n {isSaving ? \"Saving...\" : \"Save\"}\n </Button>\n </div>\n ),\n [\n playlistId,\n onDeletePlaylist,\n isMutating,\n isDirty,\n isFormValid,\n isSaving,\n onSubmit,\n ],\n );\n useScreenHeaderActions(headerActions);\n\n const headerBreadcrumbs = useMemo(\n () => (\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbLink\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n navigate(\"playlists\");\n }}\n >\n Playlists\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {isEditMode ? \"Edit Playlist\" : \"New Playlist\"}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n ),\n [isEditMode, navigate],\n );\n useScreenHeaderBreadcrumbs(headerBreadcrumbs);\n\n return (\n <Dialog open={isDeleteOpen} onOpenChange={setIsDeleteOpen}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Delete Playlist</DialogTitle>\n <DialogDescription>\n Are you sure you want to delete this playlist? This action cannot be\n undone.\n </DialogDescription>\n </DialogHeader>\n <DialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setIsDeleteOpen(false)}\n disabled={isDeleting}\n >\n Cancel\n </Button>\n <Button\n variant=\"destructive\"\n onClick={() => void handleDelete()}\n disabled={isDeleting}\n >\n {isDeleting ? \"Deleting...\" : \"Delete\"}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","\"use client\";\n\nimport { useEffect } from \"react\";\nimport { useEditor, EditorContent } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport TextAlign from \"@tiptap/extension-text-align\";\nimport Underline from \"@tiptap/extension-underline\";\nimport {\n AlignLeft,\n AlignCenter,\n AlignRight,\n AlignJustify,\n List,\n ListOrdered,\n} from \"lucide-react\";\nimport { cn } from \"@fluid-app/ui-primitives\";\n\nexport interface RichTextEditorProps {\n value: string;\n onChange: (html: string) => void;\n placeholder?: string;\n className?: string;\n editorClassName?: string;\n}\n\nexport function RichTextEditor({\n value,\n onChange,\n placeholder = \"Start writing...\",\n className,\n editorClassName,\n}: RichTextEditorProps) {\n const editor = useEditor({\n extensions: [\n StarterKit.configure({\n bulletList: { keepMarks: true, keepAttributes: false },\n orderedList: { keepMarks: true, keepAttributes: false },\n }),\n Placeholder.configure({ placeholder }),\n TextAlign.configure({\n types: [\"paragraph\"],\n alignments: [\"left\", \"center\", \"right\", \"justify\"],\n }),\n Underline,\n ],\n content: value || \"\",\n onUpdate: ({ editor }) => {\n onChange(editor.getHTML());\n },\n });\n\n useEffect(() => {\n if (!editor) return;\n if (editor.getHTML() !== value) {\n editor.commands.setContent(value || \"\", false);\n }\n }, [editor, value]);\n\n const buttonBase =\n \"flex h-7 w-7 items-center justify-center rounded text-xs transition-colors\";\n const buttonActive = \"bg-gray-100 text-primary\";\n const buttonInactive = \"text-gray-400 hover:bg-gray-50\";\n const separator = <div className=\"mx-1 h-5 w-px bg-gray-200\" />;\n\n return (\n <div\n className={cn(\n \"border-border flex flex-col overflow-hidden rounded-lg border\",\n className,\n )}\n >\n {/* Toolbar */}\n <div className=\"border-border flex items-center gap-0.5 border-b bg-gray-50 px-2 py-1.5\">\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().toggleBold().run()}\n disabled={!editor?.can().chain().focus().toggleBold().run()}\n className={cn(\n buttonBase,\n \"font-bold\",\n editor?.isActive(\"bold\") ? buttonActive : buttonInactive,\n )}\n title=\"Bold\"\n >\n B\n </button>\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().toggleItalic().run()}\n disabled={!editor?.can().chain().focus().toggleItalic().run()}\n className={cn(\n buttonBase,\n \"italic\",\n editor?.isActive(\"italic\") ? buttonActive : buttonInactive,\n )}\n title=\"Italic\"\n >\n I\n </button>\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().toggleUnderline().run()}\n disabled={!editor?.can().chain().focus().toggleUnderline().run()}\n className={cn(\n buttonBase,\n \"underline\",\n editor?.isActive(\"underline\") ? buttonActive : buttonInactive,\n )}\n title=\"Underline\"\n >\n U\n </button>\n {separator}\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().setTextAlign(\"left\").run()}\n className={cn(\n buttonBase,\n editor?.isActive({ textAlign: \"left\" })\n ? buttonActive\n : buttonInactive,\n )}\n title=\"Align left\"\n >\n <AlignLeft className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().setTextAlign(\"center\").run()}\n className={cn(\n buttonBase,\n editor?.isActive({ textAlign: \"center\" })\n ? buttonActive\n : buttonInactive,\n )}\n title=\"Align center\"\n >\n <AlignCenter className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().setTextAlign(\"right\").run()}\n className={cn(\n buttonBase,\n editor?.isActive({ textAlign: \"right\" })\n ? buttonActive\n : buttonInactive,\n )}\n title=\"Align right\"\n >\n <AlignRight className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().setTextAlign(\"justify\").run()}\n className={cn(\n buttonBase,\n editor?.isActive({ textAlign: \"justify\" })\n ? buttonActive\n : buttonInactive,\n )}\n title=\"Justify\"\n >\n <AlignJustify className=\"h-3.5 w-3.5\" />\n </button>\n {separator}\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().toggleBulletList().run()}\n className={cn(\n buttonBase,\n editor?.isActive(\"bulletList\") ? buttonActive : buttonInactive,\n )}\n title=\"Bullet list\"\n >\n <List className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => editor?.chain().focus().toggleOrderedList().run()}\n className={cn(\n buttonBase,\n editor?.isActive(\"orderedList\") ? buttonActive : buttonInactive,\n )}\n title=\"Ordered list\"\n >\n <ListOrdered className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n\n {/* Editor area */}\n <EditorContent\n editor={editor}\n className={cn(\"flex-1 overflow-y-auto p-4\", editorClassName)}\n />\n\n <style>{`\n .ProseMirror {\n outline: none;\n }\n .ProseMirror p.is-empty::before {\n color: #9ca3af;\n content: attr(data-placeholder);\n float: left;\n height: 0;\n pointer-events: none;\n }\n .ProseMirror ul {\n list-style-type: disc;\n padding-left: 1.5rem;\n margin: 0.5rem 0;\n }\n .ProseMirror ol {\n list-style-type: decimal;\n padding-left: 1.5rem;\n margin: 0.5rem 0;\n }\n .ProseMirror li {\n margin: 0.25rem 0;\n display: list-item;\n }\n .ProseMirror li > p {\n margin: 0;\n }\n `}</style>\n </div>\n );\n}\n","\"use client\";\n\nimport { Input, Label, cn } from \"@fluid-app/ui-primitives\";\nimport { usePlaylistForm } from \"./PlaylistFormProvider\";\nimport { RichTextEditor } from \"../RichTextEditor\";\n\nexport function PlaylistFormDetails() {\n const { form, updateField, validationErrors } = usePlaylistForm();\n\n return (\n <div className=\"border-border bg-card w-full rounded-lg border p-4 shadow-xs\">\n <div className=\"space-y-6\">\n <div>\n <h2 className=\"text-md text-foreground font-semibold\">Playlist</h2>\n <p className=\"text-muted-foreground text-sm\">\n Name your playlist and add a short description.\n </p>\n </div>\n\n <div className=\"space-y-4\">\n <div className=\"space-y-1\">\n <Label className=\"text-foreground font-medium\">Name *</Label>\n <Input\n name=\"title\"\n placeholder=\"Playlist name\"\n value={form.title}\n onChange={(e) => updateField(\"title\", e.target.value)}\n required\n className={cn(\n validationErrors.title && \"ring-destructive ring-1\",\n )}\n />\n {validationErrors.title && (\n <p className=\"text-destructive text-xs\">\n {validationErrors.title}\n </p>\n )}\n </div>\n\n <div className=\"flex flex-col gap-2\">\n <Label className=\"text-foreground font-medium\">Description</Label>\n <div className=\"border-border rounded-[6px] border shadow-xs\">\n <RichTextEditor\n value={form.description || \"\"}\n onChange={(value) => updateField(\"description\", value)}\n className=\"h-48 border-0\"\n editorClassName=\"h-36 overflow-auto\"\n placeholder=\"Describe this playlist...\"\n />\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { useSortable } from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\n\ninterface SortableRowProps {\n children: React.ReactNode;\n id: string;\n enableReordering?: boolean;\n}\n\nexport function SortableRow({\n children,\n id,\n enableReordering = true,\n}: SortableRowProps) {\n const {\n attributes,\n listeners,\n setNodeRef,\n transform,\n transition,\n isDragging,\n } = useSortable({ id, disabled: !enableReordering });\n\n const style = {\n transform: CSS.Transform.toString(transform),\n transition,\n opacity: isDragging ? 0.5 : 1,\n };\n\n return (\n <tr ref={setNodeRef} style={style} {...attributes}>\n {React.Children.map(children, (child, index) => {\n if (index === 0 && enableReordering) {\n return React.cloneElement(child as React.ReactElement, {\n ...listeners,\n });\n }\n return child;\n })}\n </tr>\n );\n}\n","\"use client\";\n\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"@fluid-app/ui-primitives\";\nimport type { LucideIcon } from \"lucide-react\";\nimport {\n MoreHorizontal,\n Trash2,\n GripVertical,\n Image,\n ShoppingBag,\n FileText,\n Package,\n Video,\n} from \"lucide-react\";\nimport {\n DndContext,\n closestCenter,\n KeyboardSensor,\n PointerSensor,\n useSensor,\n useSensors,\n type DragEndEvent,\n} from \"@dnd-kit/core\";\nimport {\n arrayMove,\n SortableContext,\n sortableKeyboardCoordinates,\n verticalListSortingStrategy,\n} from \"@dnd-kit/sortable\";\nimport type { PlaylistItemLike } from \"./PlaylistItemsContext\";\nimport { SortableRow } from \"./SortableRow\";\n\nfunction typeBadgeIcon(relateableType: string, kind?: string): LucideIcon {\n const k = kind?.toLowerCase() ?? \"\";\n if (relateableType === \"Medium\") {\n if (k === \"video\" || k.includes(\"video\")) return Video;\n return Image;\n }\n if (relateableType === \"Page\") return FileText;\n if (relateableType === \"Product\") return ShoppingBag;\n if (relateableType === \"EnrollmentPack\") return Package;\n return ShoppingBag;\n}\n\nfunction getTypeDisplay(relateableType: string, relateableKind?: string) {\n switch (relateableType) {\n case \"Product\":\n return \"Product\";\n case \"EnrollmentPack\":\n return \"Enrollment Pack\";\n case \"Medium\": {\n const kind = relateableKind?.toLowerCase() ?? \"\";\n if (kind === \"video\" || kind.includes(\"video\")) return \"Video\";\n if (kind === \"image\" || kind.includes(\"image\")) return \"Image\";\n return relateableKind || \"Media\";\n }\n case \"Page\":\n return \"Page\";\n default:\n return relateableType?.replace(/_/g, \" \") || \"Unknown\";\n }\n}\n\nfunction getItemIcon(relateableType: string) {\n switch (relateableType) {\n case \"Medium\":\n return <Image className=\"text-muted-foreground h-4 w-4\" />;\n case \"Page\":\n return <FileText className=\"text-muted-foreground h-4 w-4\" />;\n case \"Product\":\n return <ShoppingBag className=\"text-muted-foreground h-4 w-4\" />;\n case \"EnrollmentPack\":\n return <Package className=\"text-muted-foreground h-4 w-4\" />;\n default:\n return <Image className=\"text-muted-foreground h-4 w-4\" />;\n }\n}\n\ntype TableRow = {\n id: number | string;\n relateable_type?: string | null;\n relateable_id?: number;\n relateable?: PlaylistItemLike[\"relateable\"];\n order: number;\n title: string;\n};\n\ninterface SortableTableProps {\n items: PlaylistItemLike[];\n setItems?: (items: PlaylistItemLike[]) => void;\n onDeleteItem: (itemId: number) => void;\n isDeletePending: boolean;\n enableReordering?: boolean;\n}\n\nexport function SortableTable({\n items,\n setItems,\n onDeleteItem,\n isDeletePending,\n enableReordering = true,\n}: SortableTableProps) {\n const sensors = useSensors(\n useSensor(PointerSensor),\n useSensor(KeyboardSensor, {\n coordinateGetter: sortableKeyboardCoordinates,\n }),\n );\n\n const handleDragEnd = (event: DragEndEvent) => {\n const { active, over } = event;\n if (active.id !== over?.id && setItems) {\n const oldIndex = items.findIndex(\n (item) => item.id?.toString() === active.id,\n );\n const newIndex = items.findIndex(\n (item) => item.id?.toString() === over?.id,\n );\n setItems(arrayMove(items, oldIndex, newIndex));\n }\n };\n\n const tableData: TableRow[] = items.map((item, index) => ({\n id: item.id || `item-${index}`,\n relateable_type: item.relateable_type,\n relateable_id: item.relateable?.id,\n relateable: item.relateable,\n order: index + 1,\n title:\n item.relateable?.title ||\n `${item.relateable_type} #${item.relateable?.id}`,\n }));\n\n const tableContent = (\n <div className=\"border-border w-full overflow-x-auto rounded-lg border\">\n <table className=\"w-full min-w-max\">\n <thead className=\"bg-muted\">\n <tr>\n <th className=\"text-muted-foreground px-6 py-3 text-left text-xs font-medium tracking-wider uppercase\">\n Order\n </th>\n <th className=\"text-muted-foreground px-6 py-3 text-left text-xs font-medium tracking-wider uppercase\">\n Item\n </th>\n <th className=\"text-muted-foreground px-6 py-3 text-left text-xs font-medium tracking-wider uppercase\">\n Type\n </th>\n <th className=\"text-muted-foreground px-6 py-3 text-left text-xs font-medium tracking-wider uppercase\">\n Actions\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-border bg-card divide-y\">\n {tableData.map((row) => (\n <SortableRow\n key={row.id}\n id={row.id.toString()}\n enableReordering={enableReordering}\n >\n <td className=\"px-6 py-4 whitespace-nowrap\">\n <div className=\"flex items-center gap-2\">\n <div className={enableReordering ? \"cursor-move\" : \"\"}>\n <GripVertical\n className={`h-4 w-4 ${enableReordering ? \"text-muted-foreground\" : \"text-muted-foreground/50\"}`}\n />\n </div>\n <div className=\"text-foreground text-sm font-medium\">\n {row.order}\n </div>\n </div>\n </td>\n <td className=\"max-w-xs px-6 py-4 lg:max-w-md\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <div className=\"bg-muted flex h-8 w-8 shrink-0 items-center justify-center overflow-hidden rounded\">\n {row.relateable?.image_url ? (\n <img\n src={String(row.relateable.image_url)}\n alt={String(row.title)}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n getItemIcon(row.relateable_type || \"\")\n )}\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-foreground truncate text-sm font-medium\">\n {row.title || \"Untitled\"}\n </div>\n </div>\n </div>\n </td>\n <td className=\"px-6 py-4 whitespace-nowrap\">\n <div className=\"bg-muted text-foreground inline-flex items-center gap-1 rounded-full px-2 py-1 text-xs\">\n {(() => {\n const Icon = typeBadgeIcon(\n row.relateable_type || \"\",\n (row.relateable?.kind as string) || \"\",\n );\n return (\n <>\n <Icon className=\"h-3 w-3 shrink-0\" />\n {getTypeDisplay(\n row.relateable_type || \"\",\n (row.relateable?.kind as string) || \"\",\n )}\n </>\n );\n })()}\n </div>\n </td>\n <td className=\"px-6 py-4 text-right text-sm font-medium whitespace-nowrap\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"flex h-8 w-8 items-center justify-center p-0\"\n disabled={isDeletePending}\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n onClick={() => {\n const itemId =\n typeof row.id === \"number\"\n ? row.id\n : parseInt(String(row.id).replace(\"item-\", \"\"));\n onDeleteItem(itemId);\n }}\n variant=\"destructive\"\n >\n <Trash2 className=\"mr-2 h-4 w-4\" />\n Delete Item\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </td>\n </SortableRow>\n ))}\n </tbody>\n </table>\n </div>\n );\n\n if (enableReordering) {\n return (\n <DndContext\n sensors={sensors}\n collisionDetection={closestCenter}\n onDragEnd={handleDragEnd}\n >\n <SortableContext\n items={tableData.map((item) => item.id.toString())}\n strategy={verticalListSortingStrategy}\n >\n {tableContent}\n </SortableContext>\n </DndContext>\n );\n }\n\n return tableContent;\n}\n","\"use client\";\n\nimport { useCallback, useMemo, useState } from \"react\";\nimport { Button } from \"@fluid-app/ui-primitives\";\nimport { Plus } from \"lucide-react\";\nimport {\n FilePicker,\n FilePickerProvider,\n type FilePickerContextValue,\n} from \"@fluid-app/file-picker-ui\";\nimport type { FilePickerResult } from \"@fluid-app/file-picker-core\";\nimport {\n useAddItemToPlaylistMutation,\n useRemoveItemsFromPlaylistMutation,\n useShareablesClient,\n useRepContext,\n} from \"@fluid-app/shareables-core\";\nimport {\n media as mediaApi,\n type shareables,\n} from \"@fluid-app/shareables-api-client\";\nimport { useShareablesUI } from \"../../../context\";\nimport {\n usePlaylistItems,\n type PlaylistItemLike,\n} from \"./PlaylistItemsContext\";\nimport { SortableTable } from \"./SortableTable\";\n\nconst PLAYLIST_CONTENT_TYPES = new Set<string>([\n \"Medium\",\n \"Page\",\n \"Product\",\n \"EnrollmentPack\",\n]);\n\nfunction computeOrderedItems(items: PlaylistItemLike[]): PlaylistItemLike[] {\n const required: PlaylistItemLike[] = [];\n const optional: PlaylistItemLike[] = [];\n\n items.forEach((item) => {\n const type = item.relateable_type;\n if (type === \"Medium\" || type === \"Page\") {\n required.push(item);\n } else if (type === \"Product\" || type === \"EnrollmentPack\") {\n optional.push(item);\n }\n });\n\n const result: PlaylistItemLike[] = [];\n required.forEach((item, index) => {\n if (item.id) {\n result.push({ ...item, order: index + 1 });\n }\n });\n optional.forEach((item, index) => {\n if (item.id) {\n result.push({ ...item, order: required.length + index + 1 });\n }\n });\n return result;\n}\n\ninterface PlaylistItemsSectionProps {\n playlistId?: number;\n}\n\nexport function PlaylistItemsSection({\n playlistId,\n}: PlaylistItemsSectionProps) {\n const { showToast, filePickerClient } = useShareablesUI();\n const shareablesApiClient = useShareablesClient();\n const repContext = useRepContext();\n const [filePickerOpen, setFilePickerOpen] = useState(false);\n\n const {\n items: contextItems,\n updateItems,\n addItem,\n removeItem,\n } = usePlaylistItems();\n\n const tableItems = contextItems;\n\n const { contentItems, tailItems } = useMemo(() => {\n const content: PlaylistItemLike[] = [];\n const tail: PlaylistItemLike[] = [];\n tableItems.forEach((item) => {\n if (\n item.relateable_type &&\n PLAYLIST_CONTENT_TYPES.has(item.relateable_type)\n ) {\n content.push(item);\n } else {\n tail.push(item);\n }\n });\n return { contentItems: content, tailItems: tail };\n }, [tableItems]);\n\n const addItemMutation = useAddItemToPlaylistMutation({\n onError: (error: Error) => {\n showToast({\n title: \"Failed to add item to playlist\",\n type: \"error\",\n error,\n });\n },\n });\n\n const removeItemMutation = useRemoveItemsFromPlaylistMutation({\n onSuccess: () => {\n showToast({\n title: \"Item removed from playlist successfully\",\n type: \"success\",\n });\n },\n onError: (error: Error) => {\n showToast({\n title: \"Failed to remove item from playlist\",\n type: \"error\",\n error,\n });\n },\n });\n\n const mergeAndOrder = useCallback(\n (reorderedContent: PlaylistItemLike[]) => {\n const merged = [...reorderedContent, ...tailItems];\n updateItems(computeOrderedItems(merged));\n },\n [tailItems, updateItems],\n );\n\n const handleDeleteItem = (itemId: number) => {\n removeItem(itemId);\n if (playlistId) {\n removeItemMutation.mutate({\n playlistId,\n itemIds: [itemId],\n });\n }\n };\n\n const handleFilePickerResults = async (results: FilePickerResult[]) => {\n setFilePickerOpen(false);\n const mediaResults = results.filter((r) =>\n r.asset_code.startsWith(\"media_\"),\n );\n if (mediaResults.length === 0) {\n showToast({\n title: \"No media items selected\",\n type: \"warning\",\n });\n return;\n }\n\n for (const result of mediaResults) {\n const label =\n result.metadata?.file_name?.replace(/\\.[^/.]+$/, \"\") ||\n `Medium #${result.asset_id}`;\n\n const relateableId = result.asset_id;\n const relateableType = \"Medium\";\n\n const itemExists = tableItems.some(\n (existingItem) =>\n existingItem.relateable_type === relateableType &&\n existingItem.relateable?.id === relateableId,\n );\n\n if (itemExists) {\n showToast({\n title: \"This item is already in the playlist\",\n type: \"error\",\n });\n continue;\n }\n\n const newItem: PlaylistItemLike = {\n id: Date.now() + Math.random(),\n order: 0,\n relateable_type: relateableType,\n relateable: {\n id: relateableId,\n title: label,\n image_url: result.thumbnail_url ?? result.file_url ?? null,\n },\n };\n\n if (playlistId) {\n try {\n await addItemMutation.mutateAsync({\n playlistId,\n newItem,\n data: {\n relateable_type: relateableType,\n relateable_id: relateableId,\n order: 0,\n },\n });\n showToast({\n title: \"Item added to playlist successfully\",\n type: \"success\",\n });\n } catch {\n // Error already handled by mutation onError\n }\n } else {\n addItem(newItem);\n showToast({\n title: \"Item added. Save the playlist to persist these items.\",\n type: \"warning\",\n });\n }\n }\n };\n\n const filePickerContextValue = useMemo<FilePickerContextValue | null>(() => {\n if (!filePickerClient) return null;\n return {\n apiClient: filePickerClient,\n toast: {\n success: (msg: string) => showToast({ title: msg, type: \"success\" }),\n error: (msg: string, error?: unknown) =>\n showToast({\n title: error instanceof Error ? `${msg}: ${error.message}` : msg,\n type: \"error\",\n }),\n loading: () => \"\",\n dismiss: () => {},\n },\n shareablesClient: {\n media: {\n list: (filters: unknown) =>\n mediaApi.getMedia(\n shareablesApiClient,\n (filters ?? {}) as shareables.MediaFilters,\n repContext,\n ),\n },\n },\n };\n }, [filePickerClient, showToast, shareablesApiClient, repContext]);\n\n return (\n <div className=\"border-border bg-card rounded-lg border p-4\">\n <div className=\"space-y-4\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"min-w-0\">\n <h3 className=\"text-md text-foreground font-semibold\">Content</h3>\n <p className=\"text-muted-foreground text-sm\">\n Use <strong>Add content</strong> to open the file picker and\n choose media from your library. Drag rows to reorder.\n </p>\n </div>\n <div className=\"flex shrink-0 flex-wrap items-center justify-end gap-2\">\n <Button\n type=\"button\"\n disabled={addItemMutation.isPending || !filePickerClient}\n variant=\"default\"\n size=\"sm\"\n className=\"flex min-w-25 items-center gap-2\"\n onClick={() => setFilePickerOpen(true)}\n >\n <Plus className=\"h-4 w-4\" />\n Add content\n </Button>\n </div>\n </div>\n\n <SortableTable\n items={contentItems}\n setItems={(reordered) => mergeAndOrder(reordered)}\n onDeleteItem={handleDeleteItem}\n isDeletePending={removeItemMutation.isPending}\n enableReordering\n />\n </div>\n\n {filePickerContextValue && (\n <FilePickerProvider value={filePickerContextValue}>\n <FilePicker\n open={filePickerOpen}\n onFilesSelected={(selected) =>\n void handleFilePickerResults(selected)\n }\n onClose={() => setFilePickerOpen(false)}\n config={{\n maxFiles: 50,\n allowedMethods: [\"media\"],\n }}\n />\n </FilePickerProvider>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { usePlaylistForm, type PlaylistLike } from \"./PlaylistFormProvider\";\nimport {\n usePlaylistItems,\n type PlaylistItemLike,\n} from \"./PlaylistItemsContext\";\n\nconst OG_PLACEHOLDER_IMAGE =\n \"https://ik.imagekit.io/fluid/s3/placeholders/image.jpg?w=1200&q=75\";\n\nconst PLAYLIST_PREVIEW_CONTENT_TYPES = new Set<string>([\n \"Medium\",\n \"Page\",\n \"Product\",\n \"EnrollmentPack\",\n]);\n\nfunction getImageFromRelateable(\n item: PlaylistItemLike | undefined,\n): string | null {\n const r = item?.relateable;\n if (!r) return null;\n const url = r.image_url || r.compressed_image_url;\n const t = url?.trim();\n return t || null;\n}\n\nfunction getFirstOrderedContentItem(\n items: PlaylistItemLike[],\n): PlaylistItemLike | undefined {\n const content = items.filter(\n (item) =>\n item.relateable_type &&\n PLAYLIST_PREVIEW_CONTENT_TYPES.has(item.relateable_type),\n );\n if (content.length === 0) return undefined;\n return [...content].sort(\n (a, b) =>\n (a.order ?? Number.POSITIVE_INFINITY) -\n (b.order ?? Number.POSITIVE_INFINITY),\n )[0];\n}\n\nfunction stripHtml(html: string): string {\n if (!html) return \"\";\n const doc = new DOMParser().parseFromString(html, \"text/html\");\n return doc.body.textContent || \"\";\n}\n\nfunction buildPlaylistUrl(options: {\n playlist?: PlaylistLike | null;\n formSlug?: string | null;\n baseUrl?: string;\n preview?: boolean;\n}): string {\n const { playlist, formSlug, baseUrl, preview = false } = options;\n\n if (playlist?.canonical_url) {\n return preview\n ? `${playlist.canonical_url}?preview=true`\n : playlist.canonical_url;\n }\n if (playlist?.preview_link) {\n return preview\n ? playlist.preview_link\n : playlist.preview_link.replace(\"?preview=true\", \"\");\n }\n const slug = formSlug || playlist?.slug;\n if (baseUrl && slug) {\n const cleanBaseUrl = baseUrl.replace(/\\/$/, \"\");\n const url = `${cleanBaseUrl}/playlists/${slug}`;\n return preview ? `${url}?preview=true` : url;\n }\n return \"\";\n}\n\nexport interface PlaylistOpenGraphPreviewProps {\n playlist?: PlaylistLike | null;\n playlistShareBaseUrl?: string;\n}\n\nexport function PlaylistOpenGraphPreview({\n playlist,\n playlistShareBaseUrl,\n}: PlaylistOpenGraphPreviewProps) {\n const { form } = usePlaylistForm();\n const { items } = usePlaylistItems();\n const seo = form.search_engine_optimizer;\n\n const firstContentItem = useMemo(\n () => getFirstOrderedContentItem(items),\n [items],\n );\n\n const firstItemImageUrl = useMemo(\n () => getImageFromRelateable(firstContentItem),\n [firstContentItem],\n );\n\n const title = (seo?.title || form.title || \"\").trim() || \"Playlist title\";\n\n const rawDescription =\n (seo?.description || \"\").trim() || stripHtml(form.description || \"\") || \"\";\n const description =\n rawDescription ||\n \"Description shown when this playlist is shared on social platforms.\";\n\n const imageUrl =\n firstItemImageUrl ||\n (seo?.image_url || \"\").trim() ||\n (form.image_url || \"\").trim();\n\n const shareUrl = useMemo(\n () =>\n buildPlaylistUrl({\n playlist,\n formSlug: form.slug,\n baseUrl: playlistShareBaseUrl,\n preview: false,\n }),\n [playlist, form.slug, playlistShareBaseUrl],\n );\n\n const hostname = useMemo(() => {\n if (!shareUrl) return \"\";\n try {\n return new URL(shareUrl).hostname;\n } catch {\n return shareUrl.replace(/^https?:\\/\\//, \"\").split(\"/\")[0] ?? \"\";\n }\n }, [shareUrl]);\n\n return (\n <div className=\"border-border bg-card rounded-lg border p-4 shadow-xs\">\n <h3 className=\"text-foreground mb-2 text-sm font-semibold\">Preview</h3>\n <div className=\"border-border bg-card overflow-hidden rounded-lg border\">\n <div className=\"bg-muted aspect-[1200/630] w-full\">\n <img\n key={imageUrl || \"placeholder\"}\n src={imageUrl || OG_PLACEHOLDER_IMAGE}\n alt=\"\"\n className=\"h-full w-full object-cover\"\n />\n </div>\n <div className=\"space-y-1 p-3\">\n <p className=\"text-muted-foreground text-[11px] tracking-wide uppercase\">\n {hostname || \"your-site.com\"}\n </p>\n <p className=\"text-foreground line-clamp-2 text-base font-semibold\">\n {title}\n </p>\n <p className=\"text-muted-foreground line-clamp-3 text-sm leading-snug\">\n {description}\n </p>\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport { useEffect, useCallback } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { playlists } from \"@fluid-app/shareables-api-client\";\nimport {\n useShareablesClient,\n useCreatePlaylistMutation,\n useUpdatePlaylistMutation,\n shareablesKeys,\n} from \"@fluid-app/shareables-core\";\nimport { Skeleton } from \"@fluid-app/ui-primitives\";\nimport { useShareablesUI } from \"../../context\";\nimport {\n PlaylistFormProvider,\n usePlaylistForm,\n type PlaylistLike,\n} from \"../playlists/form/PlaylistFormProvider\";\nimport {\n PlaylistItemsProvider,\n usePlaylistItems,\n type PlaylistItemLike,\n} from \"../playlists/form/PlaylistItemsContext\";\nimport { PlaylistFormHeader } from \"../playlists/form/PlaylistFormHeader\";\nimport { PlaylistFormDetails } from \"../playlists/form/PlaylistFormDetails\";\nimport { PlaylistItemsSection } from \"../playlists/form/PlaylistItemsSection\";\nimport { PlaylistOpenGraphPreview } from \"../playlists/form/PlaylistOpenGraphPreview\";\n\nexport interface PlaylistCreateScreenProps {\n playlistId?: string;\n onBack?: () => void;\n /** Hide the built-in header (use when the host app provides its own header chrome) */\n hideHeader?: boolean;\n /** Render custom children inside the form providers (e.g. portal shell header wiring) */\n renderHeaderSlot?: (props: {\n onSubmit: () => Promise<void>;\n isSaving: boolean;\n playlistId?: string;\n isEditMode: boolean;\n }) => React.ReactNode;\n}\n\nfunction PlaylistFormContent({\n playlistId,\n playlist,\n isEditMode,\n itemsToUse,\n onBack,\n hideHeader,\n renderHeaderSlot,\n}: {\n playlistId?: string;\n playlist?: PlaylistLike | null;\n isEditMode: boolean;\n itemsToUse: PlaylistItemLike[];\n onBack?: () => void;\n hideHeader?: boolean;\n renderHeaderSlot?: PlaylistCreateScreenProps[\"renderHeaderSlot\"];\n}) {\n const client = useShareablesClient();\n const { showToast, navigate, user } = useShareablesUI();\n const { items: contextItems } = usePlaylistItems();\n const { form, updateField, validateForm } = usePlaylistForm();\n\n const playlistTitle = form.title || \"\";\n const slugValue = form.slug ?? \"\";\n\n // Auto-generate slug from title\n useEffect(() => {\n const isCustomSlug = form.custom_slug || playlist?.custom_slug || false;\n if (playlistTitle && !isCustomSlug) {\n const generatedSlug = playlistTitle\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n\n if (generatedSlug !== slugValue) {\n updateField(\"slug\", generatedSlug);\n }\n }\n }, [\n playlistTitle,\n form.custom_slug,\n playlist?.custom_slug,\n slugValue,\n updateField,\n ]);\n\n const createMutation = useCreatePlaylistMutation({\n onSuccess: (createdPlaylist) => {\n showToast({ title: \"Playlist created successfully\", type: \"success\" });\n const newId = createdPlaylist.playlist.id;\n navigate(`playlists/${newId}/edit`);\n },\n onError: (error: Error) => {\n showToast({\n title: \"Failed to create playlist\",\n type: \"error\",\n error,\n });\n },\n });\n\n const updateMutation = useUpdatePlaylistMutation({\n onSuccess: () => {\n showToast({ title: \"Playlist updated successfully\", type: \"success\" });\n },\n onError: (error: Error) => {\n showToast({\n title: \"Failed to update playlist\",\n type: \"error\",\n error,\n });\n },\n });\n\n const itemsForSubmission = contextItems;\n\n const onSaveForm = useCallback(async (): Promise<number> => {\n if (!validateForm()) {\n showToast({\n title: \"Please fill out all required fields\",\n type: \"error\",\n });\n return Promise.reject(new Error(\"Validation failed\"));\n }\n\n const formData = {\n title: form.title,\n description: form.description ?? null,\n image_url: form.image_url ?? null,\n slug: form.slug || null,\n custom_slug: form.custom_slug ?? false,\n category_id: form.category_id ?? null,\n application_theme_template_id: form.application_theme_template_id\n ? Number(form.application_theme_template_id)\n : null,\n active: isEditMode ? (form.active ?? false) : true,\n company_library: true,\n indexed_for_seo: form.indexed_for_seo ?? false,\n available_countries: form.countries ?? [],\n search_engine_optimizer_attributes:\n form.search_engine_optimizer ?? undefined,\n };\n\n if (isEditMode && playlistId) {\n await updateMutation.mutateAsync({\n playlistId: parseInt(playlistId),\n data: {\n playlist: {\n ...formData,\n library_items_attributes: itemsForSubmission?.map((item) => ({\n id: item.id,\n order: item.order,\n })),\n },\n },\n });\n return parseInt(playlistId);\n }\n\n let searchEngineOptimizer = form.search_engine_optimizer;\n if (!searchEngineOptimizer) {\n searchEngineOptimizer = {\n title: form.title || \"\",\n description: \"\",\n image_url: \"\",\n image_path: \"\",\n block_crawler: false,\n };\n }\n\n const createdPlaylist = await createMutation.mutateAsync({\n playlist: {\n ...formData,\n search_engine_optimizer_attributes: searchEngineOptimizer,\n ...(user?.id ? { user_id: user.id } : {}),\n },\n });\n const newPlaylistId = createdPlaylist.playlist.id;\n\n if (itemsForSubmission.length > 0) {\n try {\n const itemsData = itemsForSubmission\n .filter(\n (\n item,\n ): item is PlaylistItemLike & {\n relateable_type: string;\n relateable: { id: number };\n } =>\n !!item.relateable_type && typeof item.relateable?.id === \"number\",\n )\n .map((item, index) => ({\n relateable_type: item.relateable_type,\n relateable_id: item.relateable.id,\n order: index + 1,\n }));\n\n await playlists.addItemToPlaylist(client, newPlaylistId, {\n items: itemsData,\n });\n } catch (itemError) {\n console.error(\"Error adding items to playlist:\", itemError);\n showToast({\n title: \"Playlist created but some items failed to add\",\n type: \"warning\",\n });\n }\n }\n\n return newPlaylistId;\n }, [\n validateForm,\n form,\n isEditMode,\n playlistId,\n itemsForSubmission,\n createMutation,\n updateMutation,\n showToast,\n client,\n user?.id,\n ]);\n\n const isSaving = createMutation.isPending || updateMutation.isPending;\n const handleSubmit = async () => {\n await onSaveForm();\n };\n\n return (\n <>\n {renderHeaderSlot?.({\n onSubmit: handleSubmit,\n isSaving,\n playlistId,\n isEditMode,\n })}\n {!hideHeader && !renderHeaderSlot && (\n <PlaylistFormHeader\n playlistId={playlistId}\n isEditMode={isEditMode}\n onSubmit={handleSubmit}\n isSaving={isSaving}\n onBack={onBack}\n />\n )}\n <div className=\"mx-auto w-full max-w-7xl space-y-6 px-6 pt-6 pb-8 md:px-10 md:py-8\">\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <div className=\"space-y-6 lg:col-span-2\">\n <PlaylistFormDetails />\n <PlaylistItemsSection\n playlistId={playlistId ? parseInt(playlistId, 10) : undefined}\n />\n </div>\n <div className=\"space-y-6 lg:col-span-1\">\n <PlaylistOpenGraphPreview playlist={playlist} />\n </div>\n </div>\n </div>\n </>\n );\n}\n\nexport function PlaylistCreateScreen({\n playlistId,\n onBack,\n hideHeader,\n renderHeaderSlot,\n}: PlaylistCreateScreenProps) {\n const client = useShareablesClient();\n const isEditMode = !!playlistId;\n\n const { data: playlistResponse, isLoading } = useQuery({\n queryKey: playlistId\n ? shareablesKeys.playlists.detail(parseInt(playlistId, 10))\n : [\"playlists\", \"detail\", null],\n queryFn: () => playlists.getPlaylistById(client, parseInt(playlistId!, 10)),\n enabled: isEditMode,\n });\n\n const playlist = playlistResponse?.playlist as PlaylistLike | undefined;\n const itemsToUse: PlaylistItemLike[] =\n (playlist?.items as PlaylistItemLike[] | undefined) ?? [];\n\n if (isEditMode && isLoading) {\n return (\n <div className=\"mx-auto max-w-7xl space-y-6 px-6 pt-6 pb-8 md:px-10 md:py-8\">\n <Skeleton className=\"h-8 w-48\" />\n <Skeleton className=\"h-64 w-full\" />\n </div>\n );\n }\n\n return (\n <PlaylistFormProvider playlist={playlist}>\n <PlaylistItemsProvider initialItems={itemsToUse}>\n <PlaylistFormContent\n playlistId={playlistId}\n playlist={playlist}\n isEditMode={isEditMode}\n itemsToUse={itemsToUse}\n onBack={onBack}\n hideHeader={hideHeader}\n renderHeaderSlot={renderHeaderSlot}\n />\n </PlaylistItemsProvider>\n </PlaylistFormProvider>\n );\n}\n","\"use client\";\n\nimport { useState, useEffect, useRef, useCallback, useMemo } from \"react\";\nimport { useInfiniteQuery } from \"@tanstack/react-query\";\nimport { fileResources } from \"@fluid-app/shareables-api-client\";\nimport { useShareablesClient } from \"@fluid-app/shareables-core\";\nimport {\n Breadcrumb,\n BreadcrumbList,\n BreadcrumbItem,\n BreadcrumbPage,\n Card,\n Badge,\n Input,\n Skeleton,\n} from \"@fluid-app/ui-primitives\";\nimport { Search, CirclePlay } from \"lucide-react\";\nimport { useScreenHeaderBreadcrumbs } from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { useRenderImage } from \"../../context\";\n\nconst PAGE_SIZE = 24;\n\nconst GRID_CLASS =\n \"grid grid-cols-1 gap-8 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-4\";\n\nfunction formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n if (bytes < 1024 * 1024 * 1024)\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;\n}\n\nexport interface FilesListingScreenProps {\n onNavigate?: (screen: string, detailId?: string) => void;\n}\n\nconst DEFAULT_IMAGE =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n\nexport function FilesListingScreen({ onNavigate }: FilesListingScreenProps) {\n const client = useShareablesClient();\n const renderImage = useRenderImage();\n\n const headerBreadcrumbs = useMemo(\n () => (\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">Files</BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n ),\n [],\n );\n useScreenHeaderBreadcrumbs(headerBreadcrumbs);\n\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [debouncedSearch, setDebouncedSearch] = useState(\"\");\n const observerTarget = useRef<HTMLDivElement>(null);\n\n // Debounce search input\n useEffect(() => {\n const timer = setTimeout(() => {\n setDebouncedSearch(searchTerm);\n }, 300);\n return () => clearTimeout(timer);\n }, [searchTerm]);\n\n const {\n data,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n fetchNextPage,\n error,\n } = useInfiniteQuery({\n queryKey: [\"shareables-files\", debouncedSearch],\n queryFn: async ({ pageParam = 1 }) => {\n const response = await fileResources.getFileResources(client, {\n search_query: debouncedSearch || undefined,\n pageParam: pageParam.toString(),\n pageSize: PAGE_SIZE.toString(),\n });\n return response;\n },\n getNextPageParam: (lastPage) => {\n const { current_page, total_pages } = lastPage.meta;\n return current_page < total_pages ? current_page + 1 : undefined;\n },\n initialPageParam: 1,\n });\n\n const files = useMemo(\n () => data?.pages.flatMap((page) => page.file_resources) ?? [],\n [data?.pages],\n );\n\n const handleIntersect = useCallback(\n (entries: IntersectionObserverEntry[]) => {\n if (entries[0]?.isIntersecting && hasNextPage && !isFetchingNextPage) {\n fetchNextPage();\n }\n },\n [hasNextPage, isFetchingNextPage, fetchNextPage],\n );\n\n useEffect(() => {\n const target = observerTarget.current;\n if (!target) return;\n\n const observer = new IntersectionObserver(handleIntersect, {\n threshold: 0.1,\n rootMargin: \"200px\",\n });\n observer.observe(target);\n return () => observer.disconnect();\n }, [handleIntersect]);\n\n // Loading skeleton\n if (isLoading) {\n return (\n <div className=\"space-y-6 px-4 py-4 md:px-10 md:py-6\">\n <div className=\"flex items-center gap-3\">\n <Skeleton className=\"h-10 flex-1\" />\n </div>\n <div className={GRID_CLASS}>\n {Array.from({ length: 8 }).map((_, i) => (\n <div key={i} className=\"space-y-2\">\n <Skeleton className=\"aspect-square w-full rounded-lg\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-3 w-1/2\" />\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex flex-col items-center justify-center py-16\">\n <p className=\"text-destructive text-sm\">\n Failed to load files. Please try again.\n </p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 px-4 py-4 md:px-10 md:py-6\">\n {/* Search control */}\n <div className=\"flex items-center gap-3\">\n <div className=\"relative flex-1\">\n <Search className=\"text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2\" />\n <Input\n placeholder=\"Search files...\"\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n className=\"pl-9\"\n />\n </div>\n </div>\n\n {/* Files grid */}\n {files.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-16\">\n <p className=\"text-muted-foreground text-sm\">\n {debouncedSearch\n ? `No files match \"${debouncedSearch}\". Try a different search term.`\n : \"No files available.\"}\n </p>\n </div>\n ) : (\n <div className={GRID_CLASS}>\n {files.map((file) => {\n const isVideo = file.content_type?.startsWith(\"video/\");\n const fileUrl = file.url || \"#\";\n\n return (\n <Card\n key={file.id}\n role=\"button\"\n tabIndex={0}\n onClick={() => window.open(fileUrl, \"_blank\")}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n window.open(fileUrl, \"_blank\");\n }\n }}\n className=\"group hover:bg-muted cursor-pointer gap-0 overflow-hidden rounded-lg border-0 p-0 shadow-none transition-colors\"\n >\n <div className=\"bg-muted relative aspect-square overflow-hidden rounded-lg\">\n {renderImage({\n src: file.preview_image_url || DEFAULT_IMAGE,\n alt: file.filename || \"Untitled File\",\n fill: true,\n className:\n \"object-cover transition-transform group-hover:scale-105\",\n })}\n {isVideo && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <div className=\"bg-foreground/50 flex h-16 w-16 items-center justify-center rounded-full backdrop-blur-sm\">\n <CirclePlay className=\"text-background h-12 w-12\" />\n </div>\n </div>\n )}\n {!isVideo && (\n <Badge\n className=\"absolute top-2 right-2 shadow-lg\"\n variant=\"default\"\n >\n {formatFileSize(file.content_size)}\n </Badge>\n )}\n </div>\n <div className=\"px-2 pt-2 pb-4\">\n <h3 className=\"text-foreground line-clamp-2 text-sm leading-tight font-bold\">\n {file.filename || \"Untitled File\"}\n </h3>\n <p className=\"text-muted-foreground mt-1 text-xs\">\n {file.content_type || \"File\"}\n </p>\n </div>\n </Card>\n );\n })}\n </div>\n )}\n\n {/* Loading more indicator */}\n {isFetchingNextPage && (\n <div className=\"flex justify-center py-4\">\n <div className=\"border-primary h-6 w-6 animate-spin rounded-full border-2 border-t-transparent\" />\n </div>\n )}\n\n {/* Intersection observer sentinel */}\n <div ref={observerTarget} className=\"h-1\" />\n </div>\n );\n}\n","import { ProductsScreen } from \"./screens/ProductsScreen\";\nimport { ProductDetailScreen } from \"./screens/ProductDetailScreen\";\nimport { MediaListingScreen } from \"./screens/MediaListingScreen\";\nimport { MediaDetailScreen } from \"./screens/MediaDetailScreen\";\nimport { MediaCreateScreen } from \"./screens/MediaCreateScreen\";\nimport { PlaylistsListingScreen } from \"./screens/PlaylistsListingScreen\";\nimport { PlaylistDetailScreen } from \"./screens/PlaylistDetailScreen\";\nimport { PlaylistCreateScreen } from \"./screens/PlaylistCreateScreen\";\nimport { FilesListingScreen } from \"./screens/FilesListingScreen\";\n\nexport interface ShareablesAppProps {\n /** Current sub-route: \"products\" | \"media\" | \"playlists\" | \"files\" | null */\n screen?: string | null;\n /** Detail ID when viewing a specific item */\n detailId?: string | null;\n /** Action for detail pages (e.g., \"edit\") */\n action?: string | null;\n /** Company logo URL for image fallbacks */\n companyLogoUrl?: string | null;\n /** Country code for product pricing */\n countryCode?: string;\n /** Called when navigating to a sub-screen */\n onNavigate?: (screen: string, detailId?: string) => void;\n /** Called when navigating back */\n onBack?: () => void;\n}\n\nexport function ShareablesApp({\n screen,\n detailId,\n action,\n onNavigate,\n onBack,\n countryCode,\n}: ShareablesAppProps) {\n let content: React.JSX.Element;\n\n switch (screen) {\n case \"media\":\n if (detailId === \"new\") {\n content = <MediaCreateScreen onBack={onBack} onNavigate={onNavigate} />;\n } else if (detailId) {\n content = (\n <MediaDetailScreen\n mediaId={detailId}\n onNavigate={onNavigate}\n onBack={onBack}\n />\n );\n } else {\n content = <MediaListingScreen onNavigate={onNavigate} />;\n }\n break;\n case \"playlists\":\n case \"playlist\":\n if (detailId === \"new\") {\n content = <PlaylistCreateScreen onBack={onBack} />;\n } else if (detailId && action === \"edit\") {\n content = (\n <PlaylistCreateScreen playlistId={detailId} onBack={onBack} />\n );\n } else if (detailId) {\n content = (\n <PlaylistDetailScreen\n playlistId={detailId}\n onNavigate={onNavigate}\n onBack={onBack}\n />\n );\n } else {\n content = <PlaylistsListingScreen />;\n }\n break;\n case \"files\":\n content = <FilesListingScreen onNavigate={onNavigate} />;\n break;\n case \"products\":\n default:\n content = detailId ? (\n <ProductDetailScreen\n productId={detailId}\n countryCode={countryCode}\n onNavigate={onNavigate}\n onBack={onBack}\n />\n ) : (\n <ProductsScreen countryCode={countryCode} onNavigate={onNavigate} />\n );\n break;\n }\n\n return content;\n}\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { ProductsScreen } from \"./screens/ProductsScreen\";\nimport { ProductDetailScreen } from \"./screens/ProductDetailScreen\";\n\nexport interface ProductsAppProps {\n countryCode?: string;\n companyLogoUrl?: string | null;\n /** When provided, controls which product detail to show (URL-driven routing) */\n productId?: string | null;\n /** Called when a product is selected from the listing */\n onSelectProduct?: (productId: string) => void;\n /** Called when user navigates back from product detail */\n onBack?: () => void;\n}\n\nexport default function ProductsApp({\n countryCode,\n companyLogoUrl,\n productId: controlledProductId,\n onSelectProduct: onSelectProductProp,\n onBack: onBackProp,\n}: ProductsAppProps) {\n const [internalProductId, setInternalProductId] = useState<string | null>(\n null,\n );\n\n const isControlled = controlledProductId !== undefined;\n const activeProductId = isControlled\n ? controlledProductId\n : internalProductId;\n\n const handleSelectProduct = onSelectProductProp ?? setInternalProductId;\n const handleBack = onBackProp ?? (() => setInternalProductId(null));\n\n const handleNavigate = (screen: string, detailId?: string) => {\n if (screen === \"products\" && detailId) {\n handleSelectProduct(detailId);\n }\n // Ignore media navigation — ProductsApp only handles products\n };\n\n if (activeProductId) {\n return (\n <ProductDetailScreen\n productId={activeProductId}\n countryCode={countryCode}\n onNavigate={handleNavigate}\n onBack={handleBack}\n />\n );\n }\n\n return (\n <ProductsScreen\n countryCode={countryCode}\n onNavigate={(screen, detailId) => {\n if (detailId) {\n handleSelectProduct(detailId);\n }\n }}\n />\n );\n}\n"],"x_google_ignoreList":[28],"mappings":"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"}
|