@burdenoff/microfe-files 2026.530.3 → 2026.601.2
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilesRoot.js","names":[],"sources":["../../src/files/FilesRoot.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect } from \"react\";\nimport { ErrorBoundary } from \"@burdenoff/fe-libs/shared/components\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport { FolderOpen } from \"lucide-react\";\nimport { FeatureFlagProvider } from \"@burdenoff/fe-libs/shared/feature-flags\";\nimport { FilesProvider } from \"./providers/FilesProvider\";\nimport type { NavItem, FilesRootProps } from \"./types\";\nimport { FilesRoutes } from \"./FilesRoutes\";\nimport { setFilesConfig, buildGraphQLEndpoint } from \"../config\";\n\n// Create a query client instance\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 1000 * 60 * 5, // 5 minutes\n refetchOnWindowFocus: false,\n },\n },\n});\n\n/**\n * Root component for the Files microfrontend.\n *\n * This is the main entry point that host applications import and render.\n * It sets up the necessary providers and renders the internal routing.\n * Automatically registers navigation items with the shell if registration functions are provided.\n *\n * @example\n * ```tsx\n * import { FilesRoot } from '@burdenoff/micro-fe/files';\n *\n * function App() {\n * return (\n * <Route path=\"/files/*\" element={\n * <FilesRoot\n * basePath=\"/files\"\n * navigate={navigate}\n * currentUser={user}\n * workspaceId=\"ws-123\"\n * apiGatewayUrl=\"https://api.example.com\"\n * authToken=\"token\"\n * />\n * } />\n * );\n * }\n * ```\n */\nexport const FilesRoot: FC<FilesRootProps> = (props) => {\n const {\n registerNavItems,\n registerFooterItems,\n basePath = \"/\",\n apiGatewayUrl,\n authToken,\n workspaceId,\n } = props;\n\n // Initialize runtime configuration from props\n useEffect(() => {\n if (apiGatewayUrl) {\n const fullEndpoint = apiGatewayUrl.includes(\"/graphql\")\n ? apiGatewayUrl\n : buildGraphQLEndpoint(apiGatewayUrl, \"workspaces\");\n\n setFilesConfig({\n apiGatewayUrl: fullEndpoint,\n apiGatewayBaseUrl: apiGatewayUrl,\n authToken,\n });\n }\n }, [apiGatewayUrl, authToken]);\n\n // Helper to join paths correctly, avoiding double slashes\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n // Register navigation items with the shell\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: \"files-browse\",\n label: \"All Files\",\n path: joinPath(basePath, \"browse\"),\n icon: <FolderOpen size={20} />,\n order: 10,\n group: \"files\",\n },\n ];\n\n // Register and get cleanup function\n const cleanup = registerNavItems(navItems);\n\n // Return cleanup function to unregister on unmount\n return cleanup;\n }, [registerNavItems, basePath]);\n\n // Register footer items (example: storage status in left section)\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: \"files-status\",\n label: \"Files Ready\",\n type: \"status\" as const,\n order: 10,\n section: \"left\" as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems]);\n\n return (\n <ErrorBoundary\n fallback={\n <div className=\"p-6 text-center text-text-secondary\">\n Something went wrong loading this module. Please refresh the page.\n </div>\n }\n >\n <QueryClientProvider client={queryClient}>\n <FeatureFlagProvider\n workspaceId={workspaceId ?? null}\n gateway=\"global\"\n defaultEnabled={false}\n >\n <FilesProvider {...props}>\n <FilesRoutes />\n </FilesProvider>\n </FeatureFlagProvider>\n </QueryClientProvider>\n </ErrorBoundary>\n );\n};\n"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"FilesRoot.js","names":[],"sources":["../../src/files/FilesRoot.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect } from \"react\";\nimport { ErrorBoundary } from \"@burdenoff/fe-libs/shared/components\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport { FolderOpen } from \"lucide-react\";\nimport { FeatureFlagProvider } from \"@burdenoff/fe-libs/shared/feature-flags\";\nimport { FilesProvider } from \"./providers/FilesProvider\";\nimport type { NavItem, FilesRootProps } from \"./types\";\nimport { FilesRoutes } from \"./FilesRoutes\";\nimport { setFilesConfig, buildGraphQLEndpoint } from \"../config\";\n\n// Create a query client instance.\n//\n// NOTE: every data hook in this MFE is Apollo (it shares the shell's Apollo\n// client) — there are no React Query queries/mutations here. Refetching after a\n// mutation is owned by the shell's Apollo layer (mutationAutoRefreshLink +\n// refetchActiveGatewayQueries in @burdenoff/fe-libs). A previous\n// `mutationCache.onSuccess -> invalidateQueries()` was added here to fix stale\n// lists, but it was inert (no React Query mutations exist to trigger it); the\n// real cause was a stale 15s GraphQL read cache in fe-libs, now fixed there.\n// This provider remains only because shared fe-libs components rendered inside\n// the MFE expect a React Query context.\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 1000 * 60 * 5, // 5 minutes\n refetchOnWindowFocus: false,\n },\n },\n});\n\n/**\n * Root component for the Files microfrontend.\n *\n * This is the main entry point that host applications import and render.\n * It sets up the necessary providers and renders the internal routing.\n * Automatically registers navigation items with the shell if registration functions are provided.\n *\n * @example\n * ```tsx\n * import { FilesRoot } from '@burdenoff/micro-fe/files';\n *\n * function App() {\n * return (\n * <Route path=\"/files/*\" element={\n * <FilesRoot\n * basePath=\"/files\"\n * navigate={navigate}\n * currentUser={user}\n * workspaceId=\"ws-123\"\n * apiGatewayUrl=\"https://api.example.com\"\n * authToken=\"token\"\n * />\n * } />\n * );\n * }\n * ```\n */\nexport const FilesRoot: FC<FilesRootProps> = (props) => {\n const {\n registerNavItems,\n registerFooterItems,\n basePath = \"/\",\n apiGatewayUrl,\n authToken,\n workspaceId,\n } = props;\n\n // Initialize runtime configuration from props\n useEffect(() => {\n if (apiGatewayUrl) {\n const fullEndpoint = apiGatewayUrl.includes(\"/graphql\")\n ? apiGatewayUrl\n : buildGraphQLEndpoint(apiGatewayUrl, \"workspaces\");\n\n setFilesConfig({\n apiGatewayUrl: fullEndpoint,\n apiGatewayBaseUrl: apiGatewayUrl,\n authToken,\n });\n }\n }, [apiGatewayUrl, authToken]);\n\n // Helper to join paths correctly, avoiding double slashes\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n // Register navigation items with the shell\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: \"files-browse\",\n label: \"All Files\",\n path: joinPath(basePath, \"browse\"),\n icon: <FolderOpen size={20} />,\n order: 10,\n group: \"files\",\n },\n ];\n\n // Register and get cleanup function\n const cleanup = registerNavItems(navItems);\n\n // Return cleanup function to unregister on unmount\n return cleanup;\n }, [registerNavItems, basePath]);\n\n // Register footer items (example: storage status in left section)\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: \"files-status\",\n label: \"Files Ready\",\n type: \"status\" as const,\n order: 10,\n section: \"left\" as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems]);\n\n return (\n <ErrorBoundary\n fallback={\n <div className=\"p-6 text-center text-text-secondary\">\n Something went wrong loading this module. Please refresh the page.\n </div>\n }\n >\n <QueryClientProvider client={queryClient}>\n <FeatureFlagProvider\n workspaceId={workspaceId ?? null}\n gateway=\"global\"\n defaultEnabled={false}\n >\n <FilesProvider {...props}>\n <FilesRoutes />\n </FilesProvider>\n </FeatureFlagProvider>\n </QueryClientProvider>\n </ErrorBoundary>\n );\n};\n"],"mappings":";;;;;;;;;;AAsBA,IAAM,IAAc,IAAI,EAAY,EAClC,gBAAgB,EACd,SAAS;CACP,WAAW,MAAO,KAAK;CACvB,sBAAsB;CACvB,EACF,EACF,CAAC,EA6BW,KAAiC,MAAU;CACtD,IAAM,EACJ,qBACA,wBACA,cAAW,KACX,kBACA,cACA,mBACE;AAGJ,SAAgB;AACd,EAAI,KAKF,EAAe;GACb,eALmB,EAAc,SAAS,WAAW,GACnD,IACA,EAAqB,GAAe,aAAa;GAInD,mBAAmB;GACnB;GACD,CAAC;IAEH,CAAC,GAAe,EAAU,CAAC;CAG9B,IAAM,KAAY,GAAc,OACZ,EAAK,SAAS,IAAI,GAAG,EAAK,MAAM,GAAG,GAAG,GAAG,MACzC,EAAK,WAAW,IAAI,GAAG,IAAO,IAAI;AA4CtD,QAvCA,QAAgB;AACT,QAiBL,QAHgB,EAZY,CAC1B;GACE,IAAI;GACJ,OAAO;GACP,MAAM,EAAS,GAAU,SAAS;GAClC,MAAM,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA;GAC9B,OAAO;GACP,OAAO;GACR,CACF,CAGyC;IAIzC,CAAC,GAAkB,EAAS,CAAC,EAGhC,QAAgB;AACT,QAaL,QADgB,EAVI,CAClB;GACE,IAAI;GACJ,OAAO;GACP,MAAM;GACN,OAAO;GACP,SAAS;GACV,CACF,CAE+C;IAE/C,CAAC,EAAoB,CAAC,EAGvB,kBAAC,GAAD;EACE,UACE,kBAAC,OAAD;GAAK,WAAU;aAAsC;GAE/C,CAAA;YAGR,kBAAC,GAAD;GAAqB,QAAQ;aAC3B,kBAAC,GAAD;IACE,aAAa,KAAe;IAC5B,SAAQ;IACR,gBAAgB;cAEhB,kBAAC,GAAD;KAAe,GAAI;eACjB,kBAAC,GAAD,EAAe,CAAA;KACD,CAAA;IACI,CAAA;GACF,CAAA;EACR,CAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCreateFolderShareMutation as e } from "../../generated/wspace-operations.js";
|
|
2
2
|
//#region src/files/hooks/useCreateFolderShare.ts
|
|
3
3
|
var t = () => {
|
|
4
|
-
let [t, { loading: n, error: r }] = e();
|
|
4
|
+
let [t, { loading: n, error: r }] = e({ refetchQueries: ["GetUserShares"] });
|
|
5
5
|
return {
|
|
6
6
|
mutateAsync: async (e) => (await t({ variables: { input: e } })).data?.createFolderShare,
|
|
7
7
|
isPending: n,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCreateFolderShare.js","names":[],"sources":["../../../src/files/hooks/useCreateFolderShare.ts"],"sourcesContent":["import { useCreateFolderShareMutation } from \"../../generated/wspace-operations\";\n\nexport interface CreateFolderShareInput {\n folderId: string;\n shareType: \"PUBLIC_LINK\" | \"EMAIL_INVITE\" | \"GROUP_SHARE\";\n accessLevel: \"VIEW\" | \"COMMENT\" | \"DOWNLOAD\" | \"EDIT\";\n expiresAt?: string;\n password?: string;\n maxDownloads?: number;\n invitedEmail?: string;\n invitedUserIds?: string[];\n}\n\nexport const useCreateFolderShare = () => {\n const [createFolderShareMutation, { loading, error }] =\n useCreateFolderShareMutation();\n\n return {\n mutateAsync: async (input: CreateFolderShareInput) => {\n const result = await createFolderShareMutation({\n variables: { input },\n });\n\n return result.data?.createFolderShare;\n },\n isPending: loading,\n error,\n };\n};\n"],"mappings":";;AAaA,IAAa,UAA6B;CACxC,IAAM,CAAC,GAA2B,EAAE,YAAS,cAC3C,
|
|
1
|
+
{"version":3,"file":"useCreateFolderShare.js","names":[],"sources":["../../../src/files/hooks/useCreateFolderShare.ts"],"sourcesContent":["import { useCreateFolderShareMutation } from \"../../generated/wspace-operations\";\n\nexport interface CreateFolderShareInput {\n folderId: string;\n shareType: \"PUBLIC_LINK\" | \"EMAIL_INVITE\" | \"GROUP_SHARE\";\n accessLevel: \"VIEW\" | \"COMMENT\" | \"DOWNLOAD\" | \"EDIT\";\n expiresAt?: string;\n password?: string;\n maxDownloads?: number;\n invitedEmail?: string;\n invitedUserIds?: string[];\n}\n\nexport const useCreateFolderShare = () => {\n const [createFolderShareMutation, { loading, error }] =\n useCreateFolderShareMutation({\n refetchQueries: [\"GetUserShares\"],\n });\n\n return {\n mutateAsync: async (input: CreateFolderShareInput) => {\n const result = await createFolderShareMutation({\n variables: { input },\n });\n\n return result.data?.createFolderShare;\n },\n isPending: loading,\n error,\n };\n};\n"],"mappings":";;AAaA,IAAa,UAA6B;CACxC,IAAM,CAAC,GAA2B,EAAE,YAAS,cAC3C,EAA6B,EAC3B,gBAAgB,CAAC,gBAAgB,EAClC,CAAC;AAEJ,QAAO;EACL,aAAa,OAAO,OACH,MAAM,EAA0B,EAC7C,WAAW,EAAE,UAAO,EACrB,CAAC,EAEY,MAAM;EAEtB,WAAW;EACX;EACD"}
|
|
@@ -54,7 +54,8 @@ function d() {
|
|
|
54
54
|
let { t: d } = tt(), { folderId: f } = et(), p = $e(), nt = e(), { workspaceId: m } = nt, rt = Fe(m ?? ""), { viewMode: it, setViewMode: at, breadcrumbs: ot, selectedFiles: h, selectedFolders: g, clearSelection: _ } = t(), [v, st] = o("all"), ct = He(() => [
|
|
55
55
|
"all",
|
|
56
56
|
"recent",
|
|
57
|
-
"starred"
|
|
57
|
+
"starred",
|
|
58
|
+
"bin"
|
|
58
59
|
], []);
|
|
59
60
|
Ve(() => {
|
|
60
61
|
ct.includes(v) || st("all");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BrowserPage.js","names":[],"sources":["../../../src/files/pages/BrowserPage.tsx"],"sourcesContent":["import { useState, useEffect, useMemo } from \"react\";\nimport { useParams, useNavigate } from \"react-router-dom\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { useKeyboardNavigation } from \"../hooks/useKeyboardNavigation\";\nimport {\n Upload,\n FolderPlus,\n FolderUp,\n Grid3x3,\n List,\n Search,\n X,\n Loader2,\n Trash2,\n Home,\n FileDown,\n FolderX,\n} from \"lucide-react\";\nimport { useFilesContext } from \"../providers/FilesProvider\";\nimport { useFilesStore } from \"../store/filesStore\";\nimport { useBrowsePage } from \"../hooks/useBrowsePage\";\nimport { useRecentFiles } from \"../hooks/useRecentFiles\";\nimport { useStarredItems } from \"../hooks/useStarredItems\";\nimport { useBinItems } from \"../hooks/useBinItems\";\nimport { useCreateFolder } from \"../hooks/useCreateFolder\";\nimport { useUploadFile } from \"../hooks/useUploadFile\";\nimport { useDeleteFile } from \"../hooks/useDeleteFile\";\nimport { useDeleteFolder } from \"../hooks/useDeleteFolder\";\nimport { useRestoreFile } from \"../hooks/useRestoreFile\";\nimport { useRestoreFolder } from \"../hooks/useRestoreFolder\";\nimport { usePermanentlyDeleteFile } from \"../hooks/usePermanentlyDeleteFile\";\nimport { usePermanentlyDeleteFolder } from \"../hooks/usePermanentlyDeleteFolder\";\nimport { useToggleStar } from \"../hooks/useToggleStar\";\nimport { useRenameFile } from \"../hooks/useRenameFile\";\nimport { useRenameFolder } from \"../hooks/useRenameFolder\";\nimport { type CreateShareInput, useCreateShare } from \"../hooks/useCreateShare\";\nimport {\n type CreateFolderShareInput,\n useCreateFolderShare,\n} from \"../hooks/useCreateFolderShare\";\nimport { useCopyFile } from \"../hooks/useCopyFile\";\nimport { useMoveFile } from \"../hooks/useMoveFile\";\nimport { useMoveFolder } from \"../hooks/useMoveFolder\";\nimport { useBulkDelete } from \"../hooks/useBatchOperations\";\nimport { type SearchFilters, useSearch } from \"../hooks/useSearch\";\nimport { useToast } from \"../hooks/useToast\";\nimport { FileBrowser } from \"../components/FileBrowser\";\nimport { TabBar, type TabView } from \"../components/TabBar\";\nimport { ShareDialog, type ShareDialogInput } from \"../components/ShareDialog\";\nimport { FilePreviewModal } from \"../components/FilePreviewModal\";\nimport { FileInfoPanel } from \"../components/FileInfoPanel\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { ToastContainer } from \"../components/Toast\";\nimport { SearchBar } from \"../components/SearchBar\";\nimport { MoveDialog } from \"../components/MoveDialog\";\nimport { CopyDialog } from \"../components/CopyDialog\";\nimport { ConfirmDialog } from \"../components/ConfirmDialog\";\nimport { UploadQueue } from \"../components/UploadQueue\";\nimport { StorageBanner } from \"../components/StorageBanner\";\nimport { useStorageQuota } from \"../hooks/useStorageQuota\";\nimport type { FileItem, FolderItem, UploadItem } from \"../types\";\nimport type { FilesEventName } from \"../utils/events\";\nimport { isQuotaExhaustedError } from \"../utils/quotaUtils\";\nimport {\n nativeImpact,\n nativeNotify,\n nativeOpenUrl,\n} from \"../../utils/nativeBridge\";\nimport { emitFilesEvent } from \"../utils/events\";\nimport { tWithFallback } from \"../utils/i18n\";\nimport { generateThumbnail } from \"../utils\";\n\ninterface ShareDialogResult {\n publicUrl: string;\n token: string;\n}\n\ninterface ConfirmDialogState {\n title: string;\n description: string;\n confirmLabel: string;\n variant?: \"danger\" | \"default\";\n onConfirm: () => Promise<void> | void;\n loading?: boolean;\n}\n\n/**\n * BrowserPage - Main file browser with toolbar and view modes\n */\nexport function BrowserPage() {\n const { t } = useI18n();\n const { folderId } = useParams<{ folderId: string }>();\n const navigate = useNavigate();\n const filesContext = useFilesContext();\n const { workspaceId } = filesContext;\n const storageQuota = useStorageQuota(workspaceId ?? \"\");\n const {\n viewMode,\n setViewMode,\n breadcrumbs,\n selectedFiles,\n selectedFolders,\n clearSelection,\n } = useFilesStore();\n\n // Tab state\n const [activeTab, setActiveTab] = useState<TabView>(\"all\");\n\n const enabledTabs = useMemo<TabView[]>(() => {\n return [\"all\", \"recent\", \"starred\"];\n }, []);\n\n // If the active tab gets disabled at runtime (flag flipped, user opt-out),\n // fall back to \"all\" instead of leaving the user on a blank screen.\n useEffect(() => {\n if (!enabledTabs.includes(activeTab)) {\n setActiveTab(\"all\");\n }\n }, [enabledTabs, activeTab]);\n\n const pageSize = 100;\n\n // Search state\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [searchFilters, setSearchFilters] = useState<SearchFilters>();\n const isSearching = searchQuery.trim().length >= 2;\n\n // Info panel state\n const [infoPanel, setInfoPanel] = useState<{\n item: FileItem | FolderItem;\n type: \"file\" | \"folder\";\n } | null>(null);\n\n // Preview state\n const [previewFile, setPreviewFile] = useState<FileItem | null>(null);\n\n // Toast notifications\n const toast = useToast();\n\n // Fetch data based on active tab. Each view owns one composed page query so\n // files, folders, and folder context travel in a single GraphQL operation.\n const browsePageQuery = useBrowsePage({\n folderId: folderId || undefined,\n pageSize,\n skip: activeTab !== \"all\" || isSearching,\n });\n const recentFilesQuery = useRecentFiles({\n skip: activeTab !== \"recent\" || isSearching,\n });\n const starredItemsQuery = useStarredItems({\n skip: activeTab !== \"starred\" || isSearching,\n });\n const binItemsQuery = useBinItems({\n skip: activeTab !== \"bin\" || isSearching,\n });\n\n const searchResults = useSearch(searchQuery, isSearching, searchFilters);\n\n // Update breadcrumbs when folder path data changes\n useEffect(() => {\n if (\n folderId &&\n browsePageQuery.data?.folderPath &&\n browsePageQuery.data?.currentFolder\n ) {\n // Combine parent folders with current folder\n const parentFolders = browsePageQuery.data.folderPath.folders || [];\n const currentFolder = browsePageQuery.data.currentFolder;\n const breadcrumbPath = [\n ...parentFolders.map((f: { id: string; name: string }) => ({\n id: f.id,\n name: f.name,\n })),\n { id: currentFolder.id, name: currentFolder.name },\n ];\n useFilesStore.getState().setBreadcrumbs(breadcrumbPath);\n } else {\n useFilesStore.getState().setBreadcrumbs([]);\n }\n }, [folderId, browsePageQuery.data]);\n\n // Determine which data to display based on search state\n let files: FileItem[] = [];\n let folders: FolderItem[] = [];\n let loading = false;\n let error: Error | null = null;\n let preserveSortOrder = false; // For Recent/Bin tabs - skip re-sorting\n\n // If searching, use search results\n if (isSearching && searchResults.data) {\n files = searchResults.data.files || [];\n folders = searchResults.data.folders || [];\n loading = searchResults.isLoading;\n error = searchResults.error as Error | null;\n } else {\n // Show data based on active tab\n switch (activeTab) {\n case \"all\":\n files = (browsePageQuery.data?.filesList?.files || []) as FileItem[];\n folders = (browsePageQuery.data?.foldersList?.folders ||\n []) as FolderItem[];\n loading = browsePageQuery.loading;\n error = (browsePageQuery.error as Error) || null;\n preserveSortOrder = false;\n break;\n case \"recent\":\n files = recentFilesQuery.files || [];\n folders = recentFilesQuery.folders || [];\n loading = recentFilesQuery.isLoading;\n error = (recentFilesQuery.error as Error) || null;\n preserveSortOrder = true; // Recent tab already sorted by backend\n break;\n case \"starred\":\n files = starredItemsQuery.files;\n folders = starredItemsQuery.folders;\n loading = starredItemsQuery.isLoading;\n error = (starredItemsQuery.error as Error) || null;\n preserveSortOrder = true;\n break;\n case \"bin\":\n files = binItemsQuery.files;\n folders = binItemsQuery.folders;\n loading = binItemsQuery.isLoading;\n error = (binItemsQuery.error as Error) || null;\n preserveSortOrder = true; // Bin tab already sorted by backend\n break;\n }\n }\n\n const totalItemsInCurrentView =\n activeTab === \"all\"\n ? (browsePageQuery.data?.filesList?.total ?? files.length) +\n (browsePageQuery.data?.foldersList?.total ?? folders.length)\n : null;\n\n const totalSelected = selectedFiles.size + selectedFolders.size;\n const [showNewFolderDialog, setShowNewFolderDialog] = useState(false);\n const [showFolderQuotaExhausted, setShowFolderQuotaExhausted] =\n useState(false);\n const [showRenameDialog, setShowRenameDialog] = useState(false);\n const [showShareDialog, setShowShareDialog] = useState(false);\n const [showMoveDialog, setShowMoveDialog] = useState(false);\n const [showCopyDialog, setShowCopyDialog] = useState(false);\n const [shareTarget, setShareTarget] = useState<FileItem | FolderItem | null>(\n null,\n );\n const [moveTarget, setMoveTarget] = useState<{\n item: FileItem | FolderItem;\n type: \"file\" | \"folder\";\n } | null>(null);\n const [copyTarget, setCopyTarget] = useState<{\n item: FileItem | FolderItem;\n type: \"file\" | \"folder\";\n } | null>(null);\n const [renameTarget, setRenameTarget] = useState<{\n id: string;\n name: string;\n type: \"file\" | \"folder\";\n } | null>(null);\n const [newName, setNewName] = useState(\"\");\n const [newFolderName, setNewFolderName] = useState(\"\");\n const [uploading, setUploading] = useState(false);\n const [uploadQueue, setUploadQueue] = useState<UploadItem[]>([]);\n const [isUploadPanelOpen, setIsUploadPanelOpen] = useState(false);\n const [confirmDialog, setConfirmDialog] = useState<ConfirmDialogState | null>(\n null,\n );\n\n const emitEvent = (\n name: FilesEventName,\n payload: Record<string, unknown>,\n ) => {\n emitFilesEvent(filesContext, name, payload);\n };\n\n const runConfirmedAction = async () => {\n if (!confirmDialog || confirmDialog.loading) return;\n\n const action = confirmDialog.onConfirm;\n setConfirmDialog({ ...confirmDialog, loading: true });\n try {\n await action();\n setConfirmDialog(null);\n } catch (error) {\n emitEvent(\"files.api.failed\", {\n action: \"confirmDialog\",\n message:\n error instanceof Error ? error.message : \"Confirmed action failed\",\n });\n setConfirmDialog((current) =>\n current ? { ...current, loading: false } : current,\n );\n }\n };\n\n // Mutations\n const createFolderMutation = useCreateFolder();\n const uploadFileMutation = useUploadFile();\n const deleteFileMutation = useDeleteFile();\n const deleteFolderMutation = useDeleteFolder();\n const restoreFileMutation = useRestoreFile();\n const restoreFolderMutation = useRestoreFolder();\n const permanentlyDeleteFileMutation = usePermanentlyDeleteFile();\n const permanentlyDeleteFolderMutation = usePermanentlyDeleteFolder();\n const toggleStarMutation = useToggleStar();\n const renameFileMutation = useRenameFile();\n const renameFolderMutation = useRenameFolder();\n const createShareMutation = useCreateShare();\n const createFolderShareMutation = useCreateFolderShare();\n const copyFileMutation = useCopyFile();\n const moveFileMutation = useMoveFile();\n const moveFolderMutation = useMoveFolder();\n const bulkDeleteMutation = useBulkDelete();\n\n // Action handlers\n const handleUploadClick = () => {\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.multiple = true;\n input.onchange = async (e) => {\n const files = (e.target as HTMLInputElement).files;\n if (files && files.length > 0) {\n const newItems: UploadItem[] = await Promise.all(\n Array.from(files).map(async (file, index) => {\n const thumbnailUrl = await generateThumbnail(file);\n return {\n id: `${Date.now()}-${index}-${file.name}`,\n file,\n status: \"pending\" as const,\n progress: 0,\n folderId: folderId || undefined,\n thumbnailUrl: thumbnailUrl ?? undefined,\n };\n }),\n );\n setUploadQueue((prev) => [...prev, ...newItems]);\n setIsUploadPanelOpen(true);\n setUploading(true);\n\n for (const item of newItems) {\n await processUpload(item);\n }\n setUploading(false);\n }\n };\n input.click();\n };\n\n const processUpload = async (item: UploadItem) => {\n const updateItem = (updates: Partial<UploadItem>) => {\n setUploadQueue((prev) =>\n prev.map((i) => (i.id === item.id ? { ...i, ...updates } : i)),\n );\n };\n\n updateItem({ status: \"uploading\" });\n\n try {\n const uploadedFile = await uploadFileMutation.mutateAsync({\n file: item.file,\n folderId: item.folderId,\n onProgress: (progress) => {\n updateItem({ progress });\n },\n });\n updateItem({ status: \"completed\", progress: 100 });\n emitEvent(\"files.file.uploaded\", {\n fileId: uploadedFile.id,\n fileName: uploadedFile.name,\n folderId: item.folderId,\n });\n } catch (error) {\n const errorMessage =\n error instanceof Error\n ? error.message\n : tWithFallback(t, \"files.upload.errors.failed\", \"Upload failed\");\n if (errorMessage === \"Upload paused\") {\n updateItem({ status: \"paused\" });\n } else if (errorMessage === \"QUOTA_EXHAUSTED\") {\n updateItem({\n status: \"failed\",\n error: tWithFallback(\n t,\n \"files.upload.errors.quotaExceeded\",\n \"Storage quota exceeded\",\n ),\n });\n toast.error(\n tWithFallback(\n t,\n \"files.upload.errors.quotaExceeded\",\n \"Storage quota exceeded. Please upgrade your plan or delete files.\",\n ),\n );\n } else {\n updateItem({ status: \"failed\", error: errorMessage });\n emitEvent(\"files.api.failed\", {\n action: \"uploadFile\",\n fileName: item.file.name,\n message: errorMessage,\n });\n }\n }\n };\n\n const handlePauseUpload = (id: string) => {\n uploadFileMutation.pause();\n setUploadQueue((prev) =>\n prev.map((i) => (i.id === id ? { ...i, status: \"paused\" as const } : i)),\n );\n };\n\n const handleResumeUpload = (id: string) => {\n const item = uploadQueue.find((i) => i.id === id);\n if (item) {\n processUpload(item);\n }\n };\n\n const handleRetryUpload = (id: string) => {\n const item = uploadQueue.find((i) => i.id === id);\n if (item) {\n setUploadQueue((prev) =>\n prev.map((i) =>\n i.id === id\n ? {\n ...i,\n status: \"pending\" as const,\n progress: 0,\n error: undefined,\n }\n : i,\n ),\n );\n processUpload(item);\n }\n };\n\n const handleCancelUpload = (id: string) => {\n setUploadQueue((prev) => {\n const remaining = prev.filter((i) => i.id !== id);\n if (remaining.length === 0) {\n setIsUploadPanelOpen(false);\n setUploading(false);\n }\n return remaining;\n });\n };\n\n // Folder upload handler - uploads entire folder with structure\n const handleFolderUploadClick = () => {\n if (!workspaceId) {\n toast.error(\n tWithFallback(\n t,\n \"files.workspace.errors.notSelected\",\n \"Workspace not selected\",\n ),\n );\n return;\n }\n\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.webkitdirectory = true;\n input.onchange = async (e) => {\n const files = (e.target as HTMLInputElement).files;\n if (files && files.length > 0) {\n const filesByFolder = new Map<string, File[]>();\n for (const file of Array.from(files)) {\n const relativePath = file.webkitRelativePath || file.name;\n const pathParts = relativePath.split(\"/\");\n const folderPath = pathParts.slice(0, -1).join(\"/\");\n\n if (!filesByFolder.has(folderPath)) {\n filesByFolder.set(folderPath, []);\n }\n filesByFolder.get(folderPath)!.push(file);\n }\n\n const folderIdMap = new Map<string, string>();\n const sortedPaths = Array.from(filesByFolder.keys()).sort();\n\n for (const folderPath of sortedPaths) {\n if (!folderPath) continue;\n\n const pathParts = folderPath.split(\"/\");\n let currentPath = \"\";\n let parentId = folderId || undefined;\n\n for (const part of pathParts) {\n currentPath = currentPath ? `${currentPath}/${part}` : part;\n\n if (!folderIdMap.has(currentPath)) {\n try {\n const { data: createdFolder } =\n await createFolderMutation.mutateAsync({\n name: part,\n parentFolderId: parentId,\n });\n if (createdFolder?.id) {\n folderIdMap.set(currentPath, createdFolder.id);\n parentId = createdFolder.id;\n }\n } catch {\n // Continue best-effort\n }\n } else {\n parentId = folderIdMap.get(currentPath);\n }\n }\n }\n\n const newItems: UploadItem[] = [];\n let index = 0;\n for (const [folderPath, folderFiles] of filesByFolder) {\n const targetFolderId = folderPath\n ? folderIdMap.get(folderPath)\n : folderId || undefined;\n\n for (const file of folderFiles) {\n const thumbnailUrl = await generateThumbnail(file);\n newItems.push({\n id: `${Date.now()}-${index++}-${file.name}`,\n file,\n status: \"pending\",\n progress: 0,\n folderId: targetFolderId,\n thumbnailUrl: thumbnailUrl ?? undefined,\n });\n }\n }\n\n setUploadQueue((prev) => [...prev, ...newItems]);\n setIsUploadPanelOpen(true);\n setUploading(true);\n\n for (const item of newItems) {\n await processUpload(item);\n }\n setUploading(false);\n\n toast.success(\n tWithFallback(\n t,\n \"files.upload.folder.success\",\n \"Uploaded {{count}} file{{plural}} from folder\",\n { count: newItems.length, plural: newItems.length > 1 ? \"s\" : \"\" },\n ),\n );\n }\n };\n input.click();\n };\n\n const handleNewFolderClick = () => {\n setShowNewFolderDialog(true);\n };\n\n const handleCreateFolder = async () => {\n if (newFolderName.trim() && workspaceId) {\n const { data: createdFolder, error } =\n await createFolderMutation.mutateAsync({\n name: newFolderName,\n parentFolderId: folderId,\n });\n\n if (error) {\n if (isQuotaExhaustedError(error)) {\n setShowNewFolderDialog(false);\n setNewFolderName(\"\");\n setShowFolderQuotaExhausted(true);\n } else {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.folder.errors.createFailed\",\n \"Folder creation failed\",\n ),\n );\n emitEvent(\"files.api.failed\", {\n action: \"createFolder\",\n message:\n error instanceof Error ? error.message : \"Folder creation failed\",\n });\n }\n return;\n }\n\n if (createdFolder) {\n setNewFolderName(\"\");\n setShowNewFolderDialog(false);\n emitEvent(\"files.folder.created\", {\n folderName: newFolderName,\n parentFolderId: folderId,\n });\n }\n }\n };\n\n // File action handlers\n const handleFilePreview = (file: FileItem) => {\n setPreviewFile(file);\n };\n\n const handleFileShare = (file: FileItem) => {\n setShareTarget(file);\n setShowShareDialog(true);\n };\n\n const handleFolderShare = (folder: FolderItem) => {\n setShareTarget(folder);\n setShowShareDialog(true);\n };\n\n const handleCreateShare = async (\n input: ShareDialogInput,\n ): Promise<ShareDialogResult> => {\n if (!input.fileId && !input.folderId) {\n throw new Error(\n tWithFallback(\n t,\n \"files.share.errors.targetRequired\",\n \"Share requires a file id or folder id\",\n ),\n );\n }\n\n const shareType = input.invitedEmail ? \"EMAIL_INVITE\" : \"PUBLIC_LINK\";\n let token = \"\";\n\n if (input.fileId) {\n // File share\n const shareInput: CreateShareInput = {\n fileId: input.fileId,\n expiresAt: input.expiresAt,\n maxDownloads: input.maxDownloads,\n password: input.password,\n invitedEmail: input.invitedEmail,\n shareType,\n accessLevel: \"DOWNLOAD\" as const,\n };\n const result = await createShareMutation.mutateAsync(shareInput);\n token = result?.token ?? \"\";\n emitEvent(\"files.share.created\", {\n entityType: \"file\",\n fileId: input.fileId,\n shareType,\n });\n } else if (input.folderId) {\n // Folder share\n const shareInput: CreateFolderShareInput = {\n folderId: input.folderId,\n expiresAt: input.expiresAt,\n maxDownloads: input.maxDownloads,\n password: input.password,\n invitedEmail: input.invitedEmail,\n shareType,\n accessLevel: \"DOWNLOAD\" as const,\n };\n const result = await createFolderShareMutation.mutateAsync(shareInput);\n token = result?.createFolderShare?.token ?? \"\";\n emitEvent(\"files.share.created\", {\n entityType: \"folder\",\n folderId: input.folderId,\n shareType,\n });\n }\n\n return {\n publicUrl: token ? `${window.location.origin}/files/share/${token}` : \"\",\n token,\n };\n };\n\n const handleFileDelete = async (file: FileItem) => {\n nativeImpact(\"medium\");\n setConfirmDialog({\n title: tWithFallback(t, \"files.confirm.deleteFile.title\", \"Delete file\"),\n description: tWithFallback(\n t,\n \"files.confirm.deleteFile.description\",\n 'Move \"{{name}}\" to the bin?',\n { name: file.name },\n ),\n confirmLabel: tWithFallback(t, \"files.common.actions.delete\", \"Delete\"),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n await deleteFileMutation.mutateAsync({ id: file.id });\n nativeNotify(\"success\");\n toast.success(\n tWithFallback(\n t,\n \"files.file.delete.success\",\n '\"{{name}}\" moved to bin',\n { name: file.name },\n ),\n );\n emitEvent(\"files.file.deleted\", {\n fileId: file.id,\n fileName: file.name,\n permanent: false,\n });\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.file.errors.deleteFailed\",\n 'Failed to delete \"{{name}}\"',\n { name: file.name },\n ),\n );\n nativeNotify(\"error\");\n emitEvent(\"files.api.failed\", {\n action: \"deleteFile\",\n fileId: file.id,\n message:\n error instanceof Error ? error.message : \"Failed to delete file\",\n });\n }\n },\n });\n };\n\n const handleFileRestore = async (file: FileItem) => {\n try {\n await restoreFileMutation.mutateAsync({ id: file.id });\n toast.success(\n tWithFallback(t, \"files.file.restore.success\", '\"{{name}}\" restored', {\n name: file.name,\n }),\n );\n emitEvent(\"files.file.restored\", {\n fileId: file.id,\n fileName: file.name,\n });\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.file.errors.restoreFailed\",\n \"Failed to restore file\",\n ),\n );\n }\n };\n\n const handleFilePermanentDelete = async (file: FileItem) => {\n nativeImpact(\"heavy\");\n setConfirmDialog({\n title: tWithFallback(\n t,\n \"files.confirm.permanentDeleteFile.title\",\n \"Permanently delete file\",\n ),\n description: tWithFallback(\n t,\n \"files.confirm.permanentDeleteFile.description\",\n 'Permanently delete \"{{name}}\"? This cannot be undone.',\n { name: file.name },\n ),\n confirmLabel: tWithFallback(\n t,\n \"files.common.actions.deletePermanently\",\n \"Delete permanently\",\n ),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n await permanentlyDeleteFileMutation.mutateAsync({ id: file.id });\n toast.success(\n tWithFallback(\n t,\n \"files.file.permanentDelete.success\",\n '\"{{name}}\" deleted permanently',\n { name: file.name },\n ),\n );\n emitEvent(\"files.file.deleted\", {\n fileId: file.id,\n fileName: file.name,\n permanent: true,\n });\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.file.errors.permanentDeleteFailed\",\n \"Failed to permanently delete file\",\n ),\n );\n }\n },\n });\n };\n\n const handleFileFavorite = async (file: FileItem) => {\n try {\n await toggleStarMutation.mutateAsync({ id: file.id, type: \"file\" });\n emitEvent(\"files.file.starred\", {\n fileId: file.id,\n fileName: file.name,\n starred: !file.starred,\n });\n } catch {\n toast.error(\n tWithFallback(\n t,\n \"files.file.errors.starFailed\",\n \"Failed to update starred state\",\n ),\n );\n emitEvent(\"files.api.failed\", {\n action: \"toggleFileStar\",\n fileId: file.id,\n });\n }\n };\n\n const handleFileRename = (file: FileItem) => {\n setRenameTarget({ id: file.id, name: file.name, type: \"file\" });\n setNewName(file.name);\n setShowRenameDialog(true);\n };\n\n const handleFileDownload = async (file: FileItem) => {\n try {\n const downloadUrl = file.downloadUrl || file.url;\n\n if (!downloadUrl) {\n throw new Error(\n tWithFallback(\n t,\n \"files.file.errors.downloadUrlUnavailable\",\n \"Download URL is unavailable\",\n ),\n );\n }\n\n const a = document.createElement(\"a\");\n a.style.display = \"none\";\n a.href = downloadUrl;\n a.download = file.name;\n a.rel = \"noopener noreferrer\";\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n\n toast.success(\n tWithFallback(t, \"files.file.download.success\", \"Downloaded {{name}}\", {\n name: file.name,\n }),\n );\n emitEvent(\"files.file.downloaded\", {\n fileId: file.id,\n fileName: file.name,\n });\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.file.errors.downloadFailed\",\n \"Failed to download file\",\n ),\n );\n }\n };\n\n const handleFileCopy = (file: FileItem) => {\n setCopyTarget({ item: file, type: \"file\" });\n setShowCopyDialog(true);\n };\n\n const handleFileMove = (file: FileItem) => {\n setMoveTarget({ item: file, type: \"file\" });\n setShowMoveDialog(true);\n };\n\n const handleFileInfoClick = (file: FileItem) => {\n setInfoPanel({ item: file, type: \"file\" });\n };\n\n // Folder action handlers\n const handleFolderOpen = (folder: FolderItem) => {\n // Navigate to folder by updating URL\n navigate(`/files/browse/${folder.id}`);\n };\n\n const handleFolderDelete = async (folder: FolderItem) => {\n nativeImpact(\"medium\");\n setConfirmDialog({\n title: tWithFallback(\n t,\n \"files.confirm.deleteFolder.title\",\n \"Delete folder\",\n ),\n description: tWithFallback(\n t,\n \"files.confirm.deleteFolder.description\",\n 'Move folder \"{{name}}\" and all its contents to the bin?',\n { name: folder.name },\n ),\n confirmLabel: tWithFallback(t, \"files.common.actions.delete\", \"Delete\"),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n await deleteFolderMutation.mutateAsync({\n id: folder.id,\n recursive: true,\n });\n toast.success(\n tWithFallback(\n t,\n \"files.folder.delete.success\",\n '\"{{name}}\" moved to bin',\n { name: folder.name },\n ),\n );\n emitEvent(\"files.folder.deleted\", {\n folderId: folder.id,\n folderName: folder.name,\n permanent: false,\n });\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.folder.errors.deleteFailed\",\n \"Failed to delete folder\",\n ),\n );\n }\n },\n });\n };\n\n const handleFolderRestore = async (folder: FolderItem) => {\n try {\n await restoreFolderMutation.mutateAsync({ id: folder.id });\n toast.success(\n tWithFallback(\n t,\n \"files.folder.restore.success\",\n '\"{{name}}\" restored',\n { name: folder.name },\n ),\n );\n emitEvent(\"files.folder.restored\", {\n folderId: folder.id,\n folderName: folder.name,\n });\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.folder.errors.restoreFailed\",\n \"Failed to restore folder\",\n ),\n );\n }\n };\n\n const handleFolderPermanentDelete = async (folder: FolderItem) => {\n setConfirmDialog({\n title: tWithFallback(\n t,\n \"files.confirm.permanentDeleteFolder.title\",\n \"Permanently delete folder\",\n ),\n description: tWithFallback(\n t,\n \"files.confirm.permanentDeleteFolder.description\",\n 'Permanently delete folder \"{{name}}\" and its contents? This cannot be undone.',\n { name: folder.name },\n ),\n confirmLabel: tWithFallback(\n t,\n \"files.common.actions.deletePermanently\",\n \"Delete permanently\",\n ),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n await permanentlyDeleteFolderMutation.mutateAsync({ id: folder.id });\n toast.success(\n tWithFallback(\n t,\n \"files.folder.permanentDelete.success\",\n '\"{{name}}\" deleted permanently',\n { name: folder.name },\n ),\n );\n emitEvent(\"files.folder.deleted\", {\n folderId: folder.id,\n folderName: folder.name,\n permanent: true,\n });\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.folder.errors.permanentDeleteFailed\",\n \"Failed to permanently delete folder\",\n ),\n );\n }\n },\n });\n };\n\n const handleFolderRename = (folder: FolderItem) => {\n setRenameTarget({ id: folder.id, name: folder.name, type: \"folder\" });\n setNewName(folder.name);\n setShowRenameDialog(true);\n };\n\n const handleFolderMove = (folder: FolderItem) => {\n setMoveTarget({ item: folder, type: \"folder\" });\n setShowMoveDialog(true);\n };\n\n const handleFolderFavorite = async (folder: FolderItem) => {\n try {\n await toggleStarMutation.mutateAsync({ id: folder.id, type: \"folder\" });\n emitEvent(\"files.folder.starred\", {\n folderId: folder.id,\n folderName: folder.name,\n starred: !folder.starred,\n });\n } catch {\n toast.error(\n tWithFallback(\n t,\n \"files.folder.errors.starFailed\",\n \"Failed to update starred state\",\n ),\n );\n emitEvent(\"files.api.failed\", {\n action: \"toggleFolderStar\",\n folderId: folder.id,\n });\n }\n };\n\n const handleFolderInfoClick = (folder: FolderItem) => {\n setInfoPanel({ item: folder, type: \"folder\" });\n };\n\n // Move and Copy handlers\n const handleMoveSubmit = async (destinationFolderId: string | null) => {\n if (!moveTarget) return;\n\n try {\n if (moveTarget.type === \"file\") {\n await moveFileMutation.mutateAsync({\n fileId: moveTarget.item.id,\n targetFolderId: destinationFolderId || null,\n });\n toast.success(\n tWithFallback(t, \"files.file.move.success\", '\"{{name}}\" moved', {\n name: moveTarget.item.name,\n }),\n );\n emitEvent(\"files.file.moved\", {\n fileId: moveTarget.item.id,\n fileName: moveTarget.item.name,\n destinationFolderId,\n });\n } else {\n await moveFolderMutation.mutateAsync({\n folderId: moveTarget.item.id,\n targetParentFolderId: destinationFolderId,\n });\n toast.success(\n tWithFallback(t, \"files.folder.move.success\", '\"{{name}}\" moved', {\n name: moveTarget.item.name,\n }),\n );\n emitEvent(\"files.folder.moved\", {\n folderId: moveTarget.item.id,\n folderName: moveTarget.item.name,\n destinationFolderId,\n });\n }\n setShowMoveDialog(false);\n setMoveTarget(null);\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.item.errors.moveFailed\",\n 'Failed to move \"{{name}}\"',\n {\n name: moveTarget.item.name,\n },\n ),\n );\n emitEvent(\"files.api.failed\", {\n action: moveTarget.type === \"file\" ? \"moveFile\" : \"moveFolder\",\n itemId: moveTarget.item.id,\n itemType: moveTarget.type,\n message: error instanceof Error ? error.message : undefined,\n });\n }\n };\n\n const handleCopySubmit = async (\n destinationFolderId: string | null,\n newName?: string,\n ) => {\n if (!copyTarget) return;\n\n try {\n if (copyTarget.type !== \"file\") {\n throw new Error(\n tWithFallback(\n t,\n \"files.folder.errors.copyUnsupported\",\n \"Folder copy is not supported\",\n ),\n );\n }\n\n await copyFileMutation.mutateAsync({\n id: copyTarget.item.id,\n newName: newName || `Copy of ${copyTarget.item.name}`,\n targetFolderId: destinationFolderId || undefined,\n });\n toast.success(\n tWithFallback(t, \"files.file.copy.success\", '\"{{name}}\" copied', {\n name: copyTarget.item.name,\n }),\n );\n emitEvent(\"files.file.copied\", {\n fileId: copyTarget.item.id,\n fileName: copyTarget.item.name,\n destinationFolderId,\n });\n\n setShowCopyDialog(false);\n setCopyTarget(null);\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.file.errors.copyFailed\",\n 'Failed to copy \"{{name}}\"',\n {\n name: copyTarget.item.name,\n },\n ),\n );\n emitEvent(\"files.api.failed\", {\n action: \"copyFile\",\n fileId: copyTarget.item.id,\n message: error instanceof Error ? error.message : undefined,\n });\n }\n };\n\n const handleRenameSubmit = async () => {\n if (!renameTarget || !newName.trim()) return;\n\n try {\n const previousName = renameTarget.name;\n const nextName = newName.trim();\n\n if (renameTarget.type === \"file\") {\n await renameFileMutation.mutateAsync({\n fileId: renameTarget.id,\n newName: nextName,\n });\n toast.success(\n tWithFallback(t, \"files.file.rename.success\", 'Renamed \"{{name}}\"', {\n name: nextName,\n }),\n );\n emitEvent(\"files.file.renamed\", {\n fileId: renameTarget.id,\n previousName,\n name: nextName,\n });\n } else {\n await renameFolderMutation.mutateAsync({\n folderId: renameTarget.id,\n newName: nextName,\n });\n toast.success(\n tWithFallback(\n t,\n \"files.folder.rename.success\",\n 'Renamed \"{{name}}\"',\n {\n name: nextName,\n },\n ),\n );\n emitEvent(\"files.folder.renamed\", {\n folderId: renameTarget.id,\n previousName,\n name: nextName,\n });\n }\n setShowRenameDialog(false);\n setRenameTarget(null);\n setNewName(\"\");\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.item.errors.renameFailed\",\n \"Failed to rename {{type}}\",\n {\n type: renameTarget.type,\n },\n ),\n );\n emitEvent(\"files.api.failed\", {\n action: renameTarget.type === \"file\" ? \"renameFile\" : \"renameFolder\",\n itemId: renameTarget.id,\n itemType: renameTarget.type,\n message: error instanceof Error ? error.message : undefined,\n });\n }\n };\n\n const handleSearch = (query: string, filters?: SearchFilters) => {\n setSearchQuery(query);\n setSearchFilters(filters);\n };\n\n useKeyboardNavigation({\n files,\n folders,\n onFilePreview: handleFilePreview,\n onFileDelete: handleFileDelete,\n onFolderOpen: handleFolderOpen,\n onFolderDelete: handleFolderDelete,\n activeTab,\n });\n\n // Guard: if the host shell hasn't pushed workspace context yet, render a\n // clear loading state rather than firing queries with an undefined workspace\n // (which used to manifest as a silent blank screen).\n // NOTE: We explicitly check for undefined (not yet provided) vs empty string\n // (explicitly no workspace selected) to show appropriate messaging.\n if (workspaceId === undefined) {\n return (\n <div className=\"flex flex-col items-center justify-center min-h-[60vh] gap-3 text-center px-6\">\n <Loader2 className=\"size-8 animate-spin text-text-tertiary\" />\n <p className=\"text-sm text-text-secondary max-w-md\">\n {tWithFallback(\n t,\n \"files.browser.workspace.waiting\",\n \"Waiting for workspace context. If this persists, the app shell may not have provided a workspaceId to the Files module. Try reloading the page or switching workspaces.\",\n )}\n </p>\n </div>\n );\n }\n\n // Empty string means host shell provided context but no workspace is selected\n if (workspaceId === \"\") {\n return (\n <div className=\"flex flex-col items-center justify-center min-h-[60vh] gap-3 text-center px-6\">\n <FolderX className=\"size-8 text-text-tertiary\" />\n <p className=\"text-sm text-text-secondary max-w-md\">\n {tWithFallback(\n t,\n \"files.browser.workspace.notSelected\",\n \"No workspace selected. Please select a workspace to view files.\",\n )}\n </p>\n </div>\n );\n }\n\n return (\n <div className=\"min-h-screen bg-bg-surface\">\n {/* Google Drive-style Header with Breadcrumb Navigation */}\n <div\n className=\"bg-bg-surface border-b border-border-default px-4 py-3 sm:px-6 sm:py-4\"\n data-tour=\"files-breadcrumb-nav\"\n >\n <div className=\"flex items-center gap-2 sm:gap-4\">\n <div className=\"flex items-center gap-1 sm:gap-2 text-sm overflow-x-auto\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/files/browse\")}\n className=\"flex items-center gap-1 text-text-primary hover:bg-action-ghost-bg-hover px-2 py-1 rounded transition-colors duration-200 whitespace-nowrap\"\n title={tWithFallback(\n t,\n \"files.browser.actions.goToRoot\",\n \"Go to root\",\n )}\n >\n <Home className=\"size-4\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(t, \"files.browser.rootLabel\", \"My Drive\")}\n </span>\n </button>\n {breadcrumbs.map((crumb, index) => (\n <div key={crumb.id} className=\"flex items-center gap-1 sm:gap-2\">\n <span className=\"text-text-tertiary\">/</span>\n <button\n type=\"button\"\n onClick={() => navigate(`/files/browse/${crumb.id}`)}\n className={`hover:bg-action-ghost-bg-hover px-2 py-1 rounded transition-colors duration-200 whitespace-nowrap ${\n index === breadcrumbs.length - 1\n ? \"text-text-primary font-medium\"\n : \"text-text-secondary\"\n }`}\n title={`Go to ${crumb.name}`}\n >\n {crumb.name}\n </button>\n </div>\n ))}\n </div>\n </div>\n </div>\n\n {/* Navigation Tabs */}\n <div data-tour=\"files-tab-bar\">\n <TabBar\n activeTab={activeTab}\n onTabChange={setActiveTab}\n tabs={enabledTabs}\n />\n </div>\n\n {/* Google Drive-style Toolbar */}\n <div className=\"bg-bg-surface border-b border-border-default px-4 py-2 sm:px-6 sm:py-3\">\n <div className=\"flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2 sm:gap-inline-gap\">\n {/* Left side - Search */}\n <div\n className=\"w-full sm:flex-1 min-w-0 order-2 sm:order-1\"\n data-tour=\"files-search-bar\"\n >\n <div className=\"relative w-full max-w-2xl\">\n <SearchBar\n onSearch={handleSearch}\n placeholder={tWithFallback(\n t,\n \"files.search.input.placeholder\",\n \"Search files and folders...\",\n )}\n initialQuery={searchQuery}\n showFilters={true}\n />\n </div>\n </div>\n\n {/* Right side - Actions */}\n <div className=\"flex items-center gap-2 order-1 sm:order-2 w-full sm:w-auto justify-between sm:justify-end\">\n {/* Select All button */}\n {!isSearching && (files.length > 0 || folders.length > 0) && (\n <button\n type=\"button\"\n onClick={() => {\n const allFileIds = files.map((f) => f.id);\n const allFolderIds = folders.map((f) => f.id);\n useFilesStore.getState().selectAllFiles(allFileIds);\n allFolderIds.forEach((id) =>\n useFilesStore.getState().selectFolder(id),\n );\n }}\n className=\"flex items-center gap-2 px-3 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-lg hover:bg-action-ghost-bg-hover hover:border-border-default transition-all duration-200\"\n >\n {tWithFallback(\n t,\n \"files.browser.actions.selectAll\",\n \"Select All\",\n )}\n </button>\n )}\n\n {/* Only show New Folder and Upload buttons in \"All Files\" tab */}\n {!isSearching && (\n <>\n <button\n type=\"button\"\n onClick={handleNewFolderClick}\n className=\"flex items-center gap-1 sm:gap-2 px-2 sm:px-4 py-2 text-xs sm:text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-lg hover:bg-action-ghost-bg-hover hover:border-border-default transition-all duration-200 shadow-sm\"\n data-tour=\"files-new-folder-button\"\n >\n <FolderPlus className=\"size-4\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(\n t,\n \"files.browser.actions.newFolder\",\n \"New Folder\",\n )}\n </span>\n </button>\n\n <button\n type=\"button\"\n onClick={handleUploadClick}\n disabled={uploading}\n className=\"flex items-center gap-1 sm:gap-2 px-2 sm:px-4 py-2 text-xs sm:text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-lg hover:bg-action-ghost-bg-hover hover:border-border-default transition-all duration-200 shadow-sm disabled:opacity-50 disabled:cursor-not-allowed\"\n data-tour=\"files-upload-files-button\"\n >\n {uploading ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(\n t,\n \"files.browser.upload.uploading\",\n \"Uploading...\",\n )}\n </span>\n </>\n ) : (\n <>\n <Upload className=\"size-4\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(\n t,\n \"files.browser.actions.uploadFiles\",\n \"Upload Files\",\n )}\n </span>\n </>\n )}\n </button>\n\n <button\n type=\"button\"\n onClick={handleFolderUploadClick}\n disabled={uploading}\n className=\"hidden sm:flex items-center gap-2 px-4 py-2.5 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-lg hover:bg-action-ghost-bg-hover hover:border-border-default transition-all duration-200 shadow-sm disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tWithFallback(\n t,\n \"files.browser.actions.uploadFolderTitle\",\n \"Upload entire folder with structure\",\n )}\n >\n <FolderUp className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.browser.actions.uploadFolder\",\n \"Upload Folder\",\n )}\n </button>\n\n {/* Export button removed per user request */}\n </>\n )}\n\n {/* View mode toggle */}\n <div\n className=\"flex items-center border border-border-default rounded-lg overflow-hidden\"\n data-tour=\"files-view-mode-toggle\"\n >\n <button\n type=\"button\"\n onClick={() => setViewMode(\"grid\")}\n className={`p-2 transition-colors duration-200 ${\n viewMode === \"grid\"\n ? \"bg-bg-accent text-text-link\"\n : \"text-text-secondary hover:bg-action-ghost-bg-hover\"\n }`}\n aria-label={tWithFallback(\n t,\n \"files.browser.view.grid\",\n \"Grid view\",\n )}\n >\n <Grid3x3 className=\"size-5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode(\"list\")}\n className={`p-2 transition-colors duration-200 ${\n viewMode === \"list\"\n ? \"bg-bg-accent text-text-link\"\n : \"text-text-secondary hover:bg-action-ghost-bg-hover\"\n }`}\n aria-label={tWithFallback(\n t,\n \"files.browser.view.list\",\n \"List view\",\n )}\n >\n <List className=\"size-5\" />\n </button>\n </div>\n </div>\n </div>\n\n {/* Search results indicator */}\n {isSearching && (\n <div className=\"mt-3 flex items-center justify-between p-3 bg-status-success-bg-subtle rounded-lg border border-status-success-border transition-all duration-200\">\n <span className=\"text-sm font-medium text-status-success-text\">\n {tWithFallback(\n t,\n \"files.browser.search.showingResults\",\n 'Showing search results for \"{{query}}\"',\n { query: searchQuery },\n )}\n {searchResults.data &&\n ` (${(searchResults.data.files?.length || 0) + (searchResults.data.folders?.length || 0)} items)`}\n </span>\n <button\n type=\"button\"\n onClick={() => setSearchQuery(\"\")}\n className=\"flex items-center gap-2 text-status-success-text hover:text-status-success-text/80 text-sm font-medium transition-colors duration-200\"\n >\n <X className=\"size-4\" />\n {tWithFallback(t, \"files.browser.search.clear\", \"Clear search\")}\n </button>\n </div>\n )}\n\n {/* Selection info with bulk actions */}\n {totalSelected > 0 && (\n <div className=\"mt-3 flex items-center justify-between p-3 bg-bg-accent rounded-lg border border-action-primary-border transition-all duration-200\">\n <div className=\"flex items-center gap-4\">\n <span className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(\n t,\n \"files.browser.selection.selectedCount\",\n \"{{count}} item{{suffix}} selected\",\n {\n count: totalSelected,\n suffix: totalSelected > 1 ? \"s\" : \"\",\n },\n )}\n </span>\n\n {/* Bulk action buttons */}\n <div className=\"flex items-center gap-2\">\n {activeTab === \"bin\" ? (\n <>\n <button\n type=\"button\"\n onClick={async () => {\n try {\n await Promise.all([\n ...Array.from(selectedFiles).map((fileId) =>\n restoreFileMutation.mutateAsync({ id: fileId }),\n ),\n ...Array.from(selectedFolders).map((folderId) =>\n restoreFolderMutation.mutateAsync({\n id: folderId,\n }),\n ),\n ]);\n\n clearSelection();\n toast.success(\n `${totalSelected} item${totalSelected > 1 ? \"s\" : \"\"} restored`,\n );\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : \"Failed to restore some items\",\n );\n }\n }}\n className=\"flex items-center gap-1.5 rounded-lg border border-status-success-border bg-bg-surface px-3 py-1.5 text-sm font-medium text-status-success-text transition-colors duration-200 hover:bg-status-success-bg-subtle\"\n >\n {tWithFallback(\n t,\n \"files.browser.selection.actions.restore\",\n \"Restore\",\n )}\n </button>\n\n <button\n type=\"button\"\n onClick={async () => {\n setConfirmDialog({\n title: tWithFallback(\n t,\n \"files.confirm.bulkPermanentDelete.title\",\n \"Permanently delete selected items\",\n ),\n description: tWithFallback(\n t,\n \"files.confirm.bulkPermanentDelete.description\",\n \"Permanently delete {{count}} selected item{{plural}}? This cannot be undone.\",\n {\n count: totalSelected,\n plural: totalSelected > 1 ? \"s\" : \"\",\n },\n ),\n confirmLabel: tWithFallback(\n t,\n \"files.common.actions.deletePermanently\",\n \"Delete permanently\",\n ),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n await Promise.all([\n ...Array.from(selectedFiles).map((fileId) =>\n permanentlyDeleteFileMutation.mutateAsync({\n id: fileId,\n }),\n ),\n ...Array.from(selectedFolders).map((folderId) =>\n permanentlyDeleteFolderMutation.mutateAsync({\n id: folderId,\n }),\n ),\n ]);\n\n clearSelection();\n toast.success(\n tWithFallback(\n t,\n \"files.bulk.permanentDelete.success\",\n \"{{count}} item{{plural}} deleted permanently\",\n {\n count: totalSelected,\n plural: totalSelected > 1 ? \"s\" : \"\",\n },\n ),\n );\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.bulk.errors.permanentDeleteFailed\",\n \"Failed to permanently delete some items\",\n ),\n );\n }\n },\n });\n }}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-status-error-text bg-bg-surface border border-status-error-border rounded-lg hover:bg-status-error-bg-subtle transition-colors duration-200\"\n >\n <Trash2 className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.browser.selection.actions.deletePermanently\",\n \"Delete permanently\",\n )}\n </button>\n </>\n ) : (\n <button\n type=\"button\"\n onClick={async () => {\n setConfirmDialog({\n title: tWithFallback(\n t,\n \"files.confirm.bulkDelete.title\",\n \"Delete selected items\",\n ),\n description: tWithFallback(\n t,\n \"files.confirm.bulkDelete.description\",\n \"Move {{count}} selected item{{plural}} to the bin?\",\n {\n count: totalSelected,\n plural: totalSelected > 1 ? \"s\" : \"\",\n },\n ),\n confirmLabel: tWithFallback(\n t,\n \"files.common.actions.delete\",\n \"Delete\",\n ),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n const fileIds = Array.from(selectedFiles);\n const folderIds = Array.from(selectedFolders);\n\n if (fileIds.length > 0) {\n await bulkDeleteMutation.mutateAsync(fileIds);\n }\n\n await Promise.all(\n folderIds.map((folderId) =>\n deleteFolderMutation.mutateAsync({\n id: folderId,\n recursive: true,\n }),\n ),\n );\n\n clearSelection();\n toast.success(\n tWithFallback(\n t,\n \"files.bulk.delete.success\",\n \"{{count}} item{{plural}} moved to bin\",\n {\n count: totalSelected,\n plural: totalSelected > 1 ? \"s\" : \"\",\n },\n ),\n );\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.bulk.errors.deleteFailed\",\n \"Failed to delete some items\",\n ),\n );\n }\n },\n });\n }}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-status-error-text bg-bg-surface border border-status-error-border rounded-lg hover:bg-status-error-bg-subtle transition-colors duration-200\"\n >\n <Trash2 className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.browser.selection.actions.delete\",\n \"Delete\",\n )}\n </button>\n )}\n </div>\n </div>\n\n <button\n type=\"button\"\n onClick={clearSelection}\n className=\"flex items-center gap-2 text-text-link hover:text-action-primary-bg-hover text-sm font-medium transition-colors duration-200\"\n >\n <X className=\"size-4\" />\n {tWithFallback(t, \"files.common.actions.clear\", \"Clear\")}\n </button>\n </div>\n )}\n </div>\n\n {/* Storage quota banner */}\n {(storageQuota.isExhausted || storageQuota.isNearLimit) && (\n <div className=\"px-4 sm:px-6 pt-3\">\n <StorageBanner quota={storageQuota} />\n </div>\n )}\n\n {/* File Browser */}\n <div\n className=\"px-2 sm:px-4 md:px-6 py-3 sm:py-4\"\n data-tour=\"files-browser-grid\"\n >\n <FileBrowser\n files={files}\n folders={folders}\n loading={loading}\n error={error}\n preserveSortOrder={preserveSortOrder}\n onFilePreview={handleFilePreview}\n onFileShare={activeTab === \"bin\" ? undefined : handleFileShare}\n onFileDelete={\n activeTab === \"bin\" ? handleFilePermanentDelete : handleFileDelete\n }\n onFileRestore={activeTab === \"bin\" ? handleFileRestore : undefined}\n onFileFavorite={activeTab === \"bin\" ? undefined : handleFileFavorite}\n onFileRename={activeTab === \"bin\" ? undefined : handleFileRename}\n onFileDownload={handleFileDownload}\n onFileCopy={activeTab === \"bin\" ? undefined : handleFileCopy}\n onFileMove={activeTab === \"bin\" ? undefined : handleFileMove}\n onFileInfo={handleFileInfoClick}\n onFolderOpen={handleFolderOpen}\n onFolderShare={activeTab === \"bin\" ? undefined : handleFolderShare}\n onFolderDelete={\n activeTab === \"bin\"\n ? handleFolderPermanentDelete\n : handleFolderDelete\n }\n onFolderRestore={\n activeTab === \"bin\" ? handleFolderRestore : undefined\n }\n onFolderRename={activeTab === \"bin\" ? undefined : handleFolderRename}\n onFolderFavorite={\n activeTab === \"bin\" ? undefined : handleFolderFavorite\n }\n onFolderMove={activeTab === \"bin\" ? undefined : handleFolderMove}\n onFolderInfo={handleFolderInfoClick}\n />\n\n {/* Results Summary */}\n {!loading && (files.length > 0 || folders.length > 0) && (\n <div className=\"mt-4 px-2 text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.browser.results.showingItems\",\n \"Showing {{count}} items\",\n { count: files.length + folders.length },\n )}\n {!isSearching &&\n activeTab === \"all\" &&\n totalItemsInCurrentView !== null &&\n ` ${tWithFallback(\n t,\n \"files.browser.results.ofItems\",\n \"of {{count}} items\",\n { count: totalItemsInCurrentView },\n )}`}\n </div>\n )}\n </div>\n\n {/* New Folder Dialog */}\n {showNewFolderDialog && (\n <div\n className=\"fixed inset-0 backdrop-blur-sm flex items-center justify-center z-[100] animate-in fade-in duration-200\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"files-new-folder-title\"\n >\n <div className=\"bg-bg-surface rounded-lg shadow-xl p-6 w-full max-w-xs mx-4 animate-in zoom-in-95 duration-200\">\n <h2\n id=\"files-new-folder-title\"\n className=\"text-base font-semibold text-text-primary mb-4\"\n >\n {tWithFallback(\n t,\n \"files.browser.newFolderDialog.title\",\n \"New folder\",\n )}\n </h2>\n <input\n type=\"text\"\n value={newFolderName}\n onChange={(e) => setNewFolderName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleCreateFolder();\n if (e.key === \"Escape\") {\n setShowNewFolderDialog(false);\n setNewFolderName(\"\");\n }\n }}\n aria-label={tWithFallback(\n t,\n \"files.browser.newFolderDialog.title\",\n \"New folder\",\n )}\n placeholder={tWithFallback(\n t,\n \"files.browser.newFolderDialog.placeholder\",\n \"Folder name\",\n )}\n className=\"w-full px-4 py-3 border-2 border-border-default rounded-lg focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200\"\n />\n <div className=\"flex justify-end gap-2 mt-6\">\n <button\n type=\"button\"\n onClick={() => {\n setShowNewFolderDialog(false);\n setNewFolderName(\"\");\n }}\n className=\"px-6 py-2.5 text-sm font-medium text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n >\n {tWithFallback(t, \"files.common.actions.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"button\"\n onClick={handleCreateFolder}\n disabled={\n !newFolderName.trim() || createFolderMutation.isPending\n }\n className=\"px-6 py-2.5 text-sm font-medium text-text-inverse bg-action-primary-bg rounded-lg hover:bg-action-primary-bg-hover disabled:bg-bg-muted disabled:text-text-tertiary disabled:cursor-not-allowed transition-colors duration-200 flex items-center gap-2\"\n >\n {createFolderMutation.isPending && (\n <Loader2 className=\"size-4 animate-spin\" />\n )}\n {createFolderMutation.isPending\n ? tWithFallback(\n t,\n \"files.browser.newFolderDialog.actions.creating\",\n \"Creating...\",\n )\n : tWithFallback(t, \"files.common.actions.create\", \"Create\")}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Rename Dialog */}\n {showRenameDialog && renameTarget && (\n <div\n className=\"fixed inset-0 backdrop-blur-sm flex items-center justify-center z-[100] animate-in fade-in duration-200\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"files-rename-title\"\n >\n <div className=\"bg-bg-surface rounded-lg shadow-xl p-6 w-full max-w-xs mx-4 animate-in zoom-in-95 duration-200\">\n <h2\n id=\"files-rename-title\"\n className=\"text-base font-semibold text-text-primary mb-4\"\n >\n {tWithFallback(\n t,\n \"files.browser.renameDialog.title\",\n \"Rename {{type}}\",\n { type: renameTarget.type },\n )}\n </h2>\n <input\n type=\"text\"\n value={newName}\n onChange={(e) => setNewName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleRenameSubmit();\n if (e.key === \"Escape\") {\n setShowRenameDialog(false);\n setRenameTarget(null);\n setNewName(\"\");\n }\n }}\n aria-label={tWithFallback(\n t,\n \"files.browser.renameDialog.title\",\n \"Rename\",\n )}\n placeholder={tWithFallback(\n t,\n \"files.browser.renameDialog.placeholder\",\n \"New name\",\n )}\n className=\"w-full px-4 py-3 border-2 border-border-default rounded-lg focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200\"\n />\n <div className=\"flex justify-end gap-2 mt-6\">\n <button\n type=\"button\"\n onClick={() => {\n setShowRenameDialog(false);\n setRenameTarget(null);\n setNewName(\"\");\n }}\n className=\"px-6 py-2.5 text-sm font-medium text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n >\n {tWithFallback(t, \"files.common.actions.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"button\"\n onClick={handleRenameSubmit}\n disabled={\n !newName.trim() ||\n newName.trim() === renameTarget.name ||\n renameFileMutation.isPending ||\n renameFolderMutation.isPending\n }\n className=\"px-6 py-2.5 text-sm font-medium text-text-inverse bg-action-primary-bg rounded-lg hover:bg-action-primary-bg-hover disabled:bg-bg-muted disabled:text-text-tertiary disabled:cursor-not-allowed transition-colors duration-200 flex items-center gap-2\"\n >\n {(renameFileMutation.isPending ||\n renameFolderMutation.isPending) && (\n <Loader2 className=\"size-4 animate-spin\" />\n )}\n {renameFileMutation.isPending || renameFolderMutation.isPending\n ? tWithFallback(\n t,\n \"files.browser.renameDialog.actions.renaming\",\n \"Renaming...\",\n )\n : tWithFallback(t, \"files.common.actions.rename\", \"Rename\")}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Share Dialog */}\n {showShareDialog && shareTarget && (\n <ShareDialog\n item={shareTarget}\n itemType={\"mimeType\" in shareTarget ? \"file\" : \"folder\"}\n onClose={() => {\n setShowShareDialog(false);\n setShareTarget(null);\n }}\n onCreateShare={handleCreateShare}\n isLoading={createShareMutation.isPending}\n />\n )}\n\n {confirmDialog && (\n <ConfirmDialog\n title={confirmDialog.title}\n description={confirmDialog.description}\n confirmLabel={confirmDialog.confirmLabel}\n cancelLabel={tWithFallback(\n t,\n \"files.common.actions.cancel\",\n \"Cancel\",\n )}\n variant={confirmDialog.variant}\n loading={confirmDialog.loading}\n onConfirm={runConfirmedAction}\n onCancel={() => {\n if (!confirmDialog.loading) {\n setConfirmDialog(null);\n }\n }}\n />\n )}\n\n {/* Folder Quota Exhausted Dialog */}\n {showFolderQuotaExhausted && (\n <ConfirmDialog\n title={tWithFallback(\n t,\n \"files.folder.quota.title\",\n \"Folder Limit Reached\",\n )}\n description={tWithFallback(\n t,\n \"files.folder.quota.description\",\n \"You've reached the folder limit for your plan. Upgrade your plan or purchase an add-on to create more folders.\",\n )}\n confirmLabel={tWithFallback(\n t,\n \"files.folder.quota.upgrade\",\n \"View Billing\",\n )}\n cancelLabel={tWithFallback(\n t,\n \"files.common.actions.dismiss\",\n \"Dismiss\",\n )}\n onConfirm={() => {\n setShowFolderQuotaExhausted(false);\n navigate(\"/billing\");\n }}\n onCancel={() => setShowFolderQuotaExhausted(false)}\n />\n )}\n\n {/* Move Dialog */}\n {showMoveDialog && moveTarget && (\n <MoveDialog\n item={moveTarget.item}\n type={moveTarget.type}\n onClose={() => {\n setShowMoveDialog(false);\n setMoveTarget(null);\n }}\n onMove={handleMoveSubmit}\n isLoading={moveFileMutation.isPending || moveFolderMutation.isPending}\n />\n )}\n\n {/* Copy Dialog */}\n {showCopyDialog && copyTarget && (\n <CopyDialog\n item={copyTarget.item}\n type={copyTarget.type}\n onClose={() => {\n setShowCopyDialog(false);\n setCopyTarget(null);\n }}\n onCopy={handleCopySubmit}\n isLoading={copyFileMutation.isPending}\n />\n )}\n\n {/* File Preview Modal */}\n {previewFile && (\n <FilePreviewModal\n file={previewFile}\n onClose={() => setPreviewFile(null)}\n onDownload={() => {\n const url = previewFile.downloadUrl || previewFile.url;\n void nativeOpenUrl(url);\n }}\n onShare={\n activeTab === \"bin\"\n ? undefined\n : () => {\n setShareTarget(previewFile);\n setShowShareDialog(true);\n }\n }\n onStar={\n activeTab === \"bin\"\n ? undefined\n : () => handleFileFavorite(previewFile)\n }\n onDelete={() =>\n activeTab === \"bin\"\n ? handleFilePermanentDelete(previewFile)\n : handleFileDelete(previewFile)\n }\n />\n )}\n\n {/* File Info Panel */}\n {infoPanel && (\n <FileInfoPanel\n item={infoPanel.item}\n type={infoPanel.type}\n onClose={() => setInfoPanel(null)}\n />\n )}\n\n {/* Upload Progress Indicator */}\n {/* Upload Queue */}\n <UploadQueue\n items={uploadQueue}\n isOpen={isUploadPanelOpen}\n onClose={() => setIsUploadPanelOpen(false)}\n onPause={handlePauseUpload}\n onResume={handleResumeUpload}\n onRetry={handleRetryUpload}\n onCancel={handleCancelUpload}\n />\n\n {/* Toast Notifications */}\n <ToastContainer toasts={toast.toasts} onClose={toast.removeToast} />\n\n {/* Empty State */}\n {!loading && files.length === 0 && folders.length === 0 && (\n <div className=\"px-6 py-12\">\n {isSearching ? (\n <div className=\"text-center animate-in fade-in duration-300\">\n <div className=\"inline-flex items-center justify-center size-16 bg-bg-muted rounded-full mb-4\">\n <Search className=\"size-8 text-text-tertiary\" />\n </div>\n <h3 className=\"text-lg font-medium text-text-primary mb-2\">\n {tWithFallback(\n t,\n \"files.browser.empty.search.title\",\n \"No results found\",\n )}\n </h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n {tWithFallback(\n t,\n \"files.browser.empty.search.description\",\n \"Try adjusting your search query or filters\",\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => {\n setSearchQuery(\"\");\n setSearchFilters(undefined);\n }}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-text-inverse bg-action-primary-bg rounded-lg hover:bg-action-primary-bg-hover transition-colors duration-200\"\n >\n {tWithFallback(t, \"files.browser.search.clear\", \"Clear search\")}\n </button>\n </div>\n ) : (\n <EmptyState\n type={activeTab}\n onUpload={activeTab === \"all\" ? handleUploadClick : undefined}\n onNewFolder={\n activeTab === \"all\" ? handleNewFolderClick : undefined\n }\n />\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BrowserPage.js","names":[],"sources":["../../../src/files/pages/BrowserPage.tsx"],"sourcesContent":["import { useState, useEffect, useMemo } from \"react\";\nimport { useParams, useNavigate } from \"react-router-dom\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { useKeyboardNavigation } from \"../hooks/useKeyboardNavigation\";\nimport {\n Upload,\n FolderPlus,\n FolderUp,\n Grid3x3,\n List,\n Search,\n X,\n Loader2,\n Trash2,\n Home,\n FileDown,\n FolderX,\n} from \"lucide-react\";\nimport { useFilesContext } from \"../providers/FilesProvider\";\nimport { useFilesStore } from \"../store/filesStore\";\nimport { useBrowsePage } from \"../hooks/useBrowsePage\";\nimport { useRecentFiles } from \"../hooks/useRecentFiles\";\nimport { useStarredItems } from \"../hooks/useStarredItems\";\nimport { useBinItems } from \"../hooks/useBinItems\";\nimport { useCreateFolder } from \"../hooks/useCreateFolder\";\nimport { useUploadFile } from \"../hooks/useUploadFile\";\nimport { useDeleteFile } from \"../hooks/useDeleteFile\";\nimport { useDeleteFolder } from \"../hooks/useDeleteFolder\";\nimport { useRestoreFile } from \"../hooks/useRestoreFile\";\nimport { useRestoreFolder } from \"../hooks/useRestoreFolder\";\nimport { usePermanentlyDeleteFile } from \"../hooks/usePermanentlyDeleteFile\";\nimport { usePermanentlyDeleteFolder } from \"../hooks/usePermanentlyDeleteFolder\";\nimport { useToggleStar } from \"../hooks/useToggleStar\";\nimport { useRenameFile } from \"../hooks/useRenameFile\";\nimport { useRenameFolder } from \"../hooks/useRenameFolder\";\nimport { type CreateShareInput, useCreateShare } from \"../hooks/useCreateShare\";\nimport {\n type CreateFolderShareInput,\n useCreateFolderShare,\n} from \"../hooks/useCreateFolderShare\";\nimport { useCopyFile } from \"../hooks/useCopyFile\";\nimport { useMoveFile } from \"../hooks/useMoveFile\";\nimport { useMoveFolder } from \"../hooks/useMoveFolder\";\nimport { useBulkDelete } from \"../hooks/useBatchOperations\";\nimport { type SearchFilters, useSearch } from \"../hooks/useSearch\";\nimport { useToast } from \"../hooks/useToast\";\nimport { FileBrowser } from \"../components/FileBrowser\";\nimport { TabBar, type TabView } from \"../components/TabBar\";\nimport { ShareDialog, type ShareDialogInput } from \"../components/ShareDialog\";\nimport { FilePreviewModal } from \"../components/FilePreviewModal\";\nimport { FileInfoPanel } from \"../components/FileInfoPanel\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { ToastContainer } from \"../components/Toast\";\nimport { SearchBar } from \"../components/SearchBar\";\nimport { MoveDialog } from \"../components/MoveDialog\";\nimport { CopyDialog } from \"../components/CopyDialog\";\nimport { ConfirmDialog } from \"../components/ConfirmDialog\";\nimport { UploadQueue } from \"../components/UploadQueue\";\nimport { StorageBanner } from \"../components/StorageBanner\";\nimport { useStorageQuota } from \"../hooks/useStorageQuota\";\nimport type { FileItem, FolderItem, UploadItem } from \"../types\";\nimport type { FilesEventName } from \"../utils/events\";\nimport { isQuotaExhaustedError } from \"../utils/quotaUtils\";\nimport {\n nativeImpact,\n nativeNotify,\n nativeOpenUrl,\n} from \"../../utils/nativeBridge\";\nimport { emitFilesEvent } from \"../utils/events\";\nimport { tWithFallback } from \"../utils/i18n\";\nimport { generateThumbnail } from \"../utils\";\n\ninterface ShareDialogResult {\n publicUrl: string;\n token: string;\n}\n\ninterface ConfirmDialogState {\n title: string;\n description: string;\n confirmLabel: string;\n variant?: \"danger\" | \"default\";\n onConfirm: () => Promise<void> | void;\n loading?: boolean;\n}\n\n/**\n * BrowserPage - Main file browser with toolbar and view modes\n */\nexport function BrowserPage() {\n const { t } = useI18n();\n const { folderId } = useParams<{ folderId: string }>();\n const navigate = useNavigate();\n const filesContext = useFilesContext();\n const { workspaceId } = filesContext;\n const storageQuota = useStorageQuota(workspaceId ?? \"\");\n const {\n viewMode,\n setViewMode,\n breadcrumbs,\n selectedFiles,\n selectedFolders,\n clearSelection,\n } = useFilesStore();\n\n // Tab state\n const [activeTab, setActiveTab] = useState<TabView>(\"all\");\n\n // All browser sections are shown unconditionally. Bin and Starred are first\n // -class tabs (not feature-flag-gated): the backend serves their queries\n // directly (subject to RBAC), so there is nothing to gate on the client.\n const enabledTabs = useMemo<TabView[]>(() => {\n return [\"all\", \"recent\", \"starred\", \"bin\"];\n }, []);\n\n // Defensive: if the active tab is ever not in the rendered set, fall back to\n // \"all\" instead of leaving the user on a blank screen.\n useEffect(() => {\n if (!enabledTabs.includes(activeTab)) {\n setActiveTab(\"all\");\n }\n }, [enabledTabs, activeTab]);\n\n const pageSize = 100;\n\n // Search state\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [searchFilters, setSearchFilters] = useState<SearchFilters>();\n const isSearching = searchQuery.trim().length >= 2;\n\n // Info panel state\n const [infoPanel, setInfoPanel] = useState<{\n item: FileItem | FolderItem;\n type: \"file\" | \"folder\";\n } | null>(null);\n\n // Preview state\n const [previewFile, setPreviewFile] = useState<FileItem | null>(null);\n\n // Toast notifications\n const toast = useToast();\n\n // Fetch data based on active tab. Each view owns one composed page query so\n // files, folders, and folder context travel in a single GraphQL operation.\n const browsePageQuery = useBrowsePage({\n folderId: folderId || undefined,\n pageSize,\n skip: activeTab !== \"all\" || isSearching,\n });\n const recentFilesQuery = useRecentFiles({\n skip: activeTab !== \"recent\" || isSearching,\n });\n const starredItemsQuery = useStarredItems({\n skip: activeTab !== \"starred\" || isSearching,\n });\n const binItemsQuery = useBinItems({\n skip: activeTab !== \"bin\" || isSearching,\n });\n\n const searchResults = useSearch(searchQuery, isSearching, searchFilters);\n\n // Update breadcrumbs when folder path data changes\n useEffect(() => {\n if (\n folderId &&\n browsePageQuery.data?.folderPath &&\n browsePageQuery.data?.currentFolder\n ) {\n // Combine parent folders with current folder\n const parentFolders = browsePageQuery.data.folderPath.folders || [];\n const currentFolder = browsePageQuery.data.currentFolder;\n const breadcrumbPath = [\n ...parentFolders.map((f: { id: string; name: string }) => ({\n id: f.id,\n name: f.name,\n })),\n { id: currentFolder.id, name: currentFolder.name },\n ];\n useFilesStore.getState().setBreadcrumbs(breadcrumbPath);\n } else {\n useFilesStore.getState().setBreadcrumbs([]);\n }\n }, [folderId, browsePageQuery.data]);\n\n // Determine which data to display based on search state\n let files: FileItem[] = [];\n let folders: FolderItem[] = [];\n let loading = false;\n let error: Error | null = null;\n let preserveSortOrder = false; // For Recent/Bin tabs - skip re-sorting\n\n // If searching, use search results\n if (isSearching && searchResults.data) {\n files = searchResults.data.files || [];\n folders = searchResults.data.folders || [];\n loading = searchResults.isLoading;\n error = searchResults.error as Error | null;\n } else {\n // Show data based on active tab\n switch (activeTab) {\n case \"all\":\n files = (browsePageQuery.data?.filesList?.files || []) as FileItem[];\n folders = (browsePageQuery.data?.foldersList?.folders ||\n []) as FolderItem[];\n loading = browsePageQuery.loading;\n error = (browsePageQuery.error as Error) || null;\n preserveSortOrder = false;\n break;\n case \"recent\":\n files = recentFilesQuery.files || [];\n folders = recentFilesQuery.folders || [];\n loading = recentFilesQuery.isLoading;\n error = (recentFilesQuery.error as Error) || null;\n preserveSortOrder = true; // Recent tab already sorted by backend\n break;\n case \"starred\":\n files = starredItemsQuery.files;\n folders = starredItemsQuery.folders;\n loading = starredItemsQuery.isLoading;\n error = (starredItemsQuery.error as Error) || null;\n preserveSortOrder = true;\n break;\n case \"bin\":\n files = binItemsQuery.files;\n folders = binItemsQuery.folders;\n loading = binItemsQuery.isLoading;\n error = (binItemsQuery.error as Error) || null;\n preserveSortOrder = true; // Bin tab already sorted by backend\n break;\n }\n }\n\n const totalItemsInCurrentView =\n activeTab === \"all\"\n ? (browsePageQuery.data?.filesList?.total ?? files.length) +\n (browsePageQuery.data?.foldersList?.total ?? folders.length)\n : null;\n\n const totalSelected = selectedFiles.size + selectedFolders.size;\n const [showNewFolderDialog, setShowNewFolderDialog] = useState(false);\n const [showFolderQuotaExhausted, setShowFolderQuotaExhausted] =\n useState(false);\n const [showRenameDialog, setShowRenameDialog] = useState(false);\n const [showShareDialog, setShowShareDialog] = useState(false);\n const [showMoveDialog, setShowMoveDialog] = useState(false);\n const [showCopyDialog, setShowCopyDialog] = useState(false);\n const [shareTarget, setShareTarget] = useState<FileItem | FolderItem | null>(\n null,\n );\n const [moveTarget, setMoveTarget] = useState<{\n item: FileItem | FolderItem;\n type: \"file\" | \"folder\";\n } | null>(null);\n const [copyTarget, setCopyTarget] = useState<{\n item: FileItem | FolderItem;\n type: \"file\" | \"folder\";\n } | null>(null);\n const [renameTarget, setRenameTarget] = useState<{\n id: string;\n name: string;\n type: \"file\" | \"folder\";\n } | null>(null);\n const [newName, setNewName] = useState(\"\");\n const [newFolderName, setNewFolderName] = useState(\"\");\n const [uploading, setUploading] = useState(false);\n const [uploadQueue, setUploadQueue] = useState<UploadItem[]>([]);\n const [isUploadPanelOpen, setIsUploadPanelOpen] = useState(false);\n const [confirmDialog, setConfirmDialog] = useState<ConfirmDialogState | null>(\n null,\n );\n\n const emitEvent = (\n name: FilesEventName,\n payload: Record<string, unknown>,\n ) => {\n emitFilesEvent(filesContext, name, payload);\n };\n\n const runConfirmedAction = async () => {\n if (!confirmDialog || confirmDialog.loading) return;\n\n const action = confirmDialog.onConfirm;\n setConfirmDialog({ ...confirmDialog, loading: true });\n try {\n await action();\n setConfirmDialog(null);\n } catch (error) {\n emitEvent(\"files.api.failed\", {\n action: \"confirmDialog\",\n message:\n error instanceof Error ? error.message : \"Confirmed action failed\",\n });\n setConfirmDialog((current) =>\n current ? { ...current, loading: false } : current,\n );\n }\n };\n\n // Mutations\n const createFolderMutation = useCreateFolder();\n const uploadFileMutation = useUploadFile();\n const deleteFileMutation = useDeleteFile();\n const deleteFolderMutation = useDeleteFolder();\n const restoreFileMutation = useRestoreFile();\n const restoreFolderMutation = useRestoreFolder();\n const permanentlyDeleteFileMutation = usePermanentlyDeleteFile();\n const permanentlyDeleteFolderMutation = usePermanentlyDeleteFolder();\n const toggleStarMutation = useToggleStar();\n const renameFileMutation = useRenameFile();\n const renameFolderMutation = useRenameFolder();\n const createShareMutation = useCreateShare();\n const createFolderShareMutation = useCreateFolderShare();\n const copyFileMutation = useCopyFile();\n const moveFileMutation = useMoveFile();\n const moveFolderMutation = useMoveFolder();\n const bulkDeleteMutation = useBulkDelete();\n\n // Action handlers\n const handleUploadClick = () => {\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.multiple = true;\n input.onchange = async (e) => {\n const files = (e.target as HTMLInputElement).files;\n if (files && files.length > 0) {\n const newItems: UploadItem[] = await Promise.all(\n Array.from(files).map(async (file, index) => {\n const thumbnailUrl = await generateThumbnail(file);\n return {\n id: `${Date.now()}-${index}-${file.name}`,\n file,\n status: \"pending\" as const,\n progress: 0,\n folderId: folderId || undefined,\n thumbnailUrl: thumbnailUrl ?? undefined,\n };\n }),\n );\n setUploadQueue((prev) => [...prev, ...newItems]);\n setIsUploadPanelOpen(true);\n setUploading(true);\n\n for (const item of newItems) {\n await processUpload(item);\n }\n setUploading(false);\n }\n };\n input.click();\n };\n\n const processUpload = async (item: UploadItem) => {\n const updateItem = (updates: Partial<UploadItem>) => {\n setUploadQueue((prev) =>\n prev.map((i) => (i.id === item.id ? { ...i, ...updates } : i)),\n );\n };\n\n updateItem({ status: \"uploading\" });\n\n try {\n const uploadedFile = await uploadFileMutation.mutateAsync({\n file: item.file,\n folderId: item.folderId,\n onProgress: (progress) => {\n updateItem({ progress });\n },\n });\n updateItem({ status: \"completed\", progress: 100 });\n emitEvent(\"files.file.uploaded\", {\n fileId: uploadedFile.id,\n fileName: uploadedFile.name,\n folderId: item.folderId,\n });\n } catch (error) {\n const errorMessage =\n error instanceof Error\n ? error.message\n : tWithFallback(t, \"files.upload.errors.failed\", \"Upload failed\");\n if (errorMessage === \"Upload paused\") {\n updateItem({ status: \"paused\" });\n } else if (errorMessage === \"QUOTA_EXHAUSTED\") {\n updateItem({\n status: \"failed\",\n error: tWithFallback(\n t,\n \"files.upload.errors.quotaExceeded\",\n \"Storage quota exceeded\",\n ),\n });\n toast.error(\n tWithFallback(\n t,\n \"files.upload.errors.quotaExceeded\",\n \"Storage quota exceeded. Please upgrade your plan or delete files.\",\n ),\n );\n } else {\n updateItem({ status: \"failed\", error: errorMessage });\n emitEvent(\"files.api.failed\", {\n action: \"uploadFile\",\n fileName: item.file.name,\n message: errorMessage,\n });\n }\n }\n };\n\n const handlePauseUpload = (id: string) => {\n uploadFileMutation.pause();\n setUploadQueue((prev) =>\n prev.map((i) => (i.id === id ? { ...i, status: \"paused\" as const } : i)),\n );\n };\n\n const handleResumeUpload = (id: string) => {\n const item = uploadQueue.find((i) => i.id === id);\n if (item) {\n processUpload(item);\n }\n };\n\n const handleRetryUpload = (id: string) => {\n const item = uploadQueue.find((i) => i.id === id);\n if (item) {\n setUploadQueue((prev) =>\n prev.map((i) =>\n i.id === id\n ? {\n ...i,\n status: \"pending\" as const,\n progress: 0,\n error: undefined,\n }\n : i,\n ),\n );\n processUpload(item);\n }\n };\n\n const handleCancelUpload = (id: string) => {\n setUploadQueue((prev) => {\n const remaining = prev.filter((i) => i.id !== id);\n if (remaining.length === 0) {\n setIsUploadPanelOpen(false);\n setUploading(false);\n }\n return remaining;\n });\n };\n\n // Folder upload handler - uploads entire folder with structure\n const handleFolderUploadClick = () => {\n if (!workspaceId) {\n toast.error(\n tWithFallback(\n t,\n \"files.workspace.errors.notSelected\",\n \"Workspace not selected\",\n ),\n );\n return;\n }\n\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.webkitdirectory = true;\n input.onchange = async (e) => {\n const files = (e.target as HTMLInputElement).files;\n if (files && files.length > 0) {\n const filesByFolder = new Map<string, File[]>();\n for (const file of Array.from(files)) {\n const relativePath = file.webkitRelativePath || file.name;\n const pathParts = relativePath.split(\"/\");\n const folderPath = pathParts.slice(0, -1).join(\"/\");\n\n if (!filesByFolder.has(folderPath)) {\n filesByFolder.set(folderPath, []);\n }\n filesByFolder.get(folderPath)!.push(file);\n }\n\n const folderIdMap = new Map<string, string>();\n const sortedPaths = Array.from(filesByFolder.keys()).sort();\n\n for (const folderPath of sortedPaths) {\n if (!folderPath) continue;\n\n const pathParts = folderPath.split(\"/\");\n let currentPath = \"\";\n let parentId = folderId || undefined;\n\n for (const part of pathParts) {\n currentPath = currentPath ? `${currentPath}/${part}` : part;\n\n if (!folderIdMap.has(currentPath)) {\n try {\n const { data: createdFolder } =\n await createFolderMutation.mutateAsync({\n name: part,\n parentFolderId: parentId,\n });\n if (createdFolder?.id) {\n folderIdMap.set(currentPath, createdFolder.id);\n parentId = createdFolder.id;\n }\n } catch {\n // Continue best-effort\n }\n } else {\n parentId = folderIdMap.get(currentPath);\n }\n }\n }\n\n const newItems: UploadItem[] = [];\n let index = 0;\n for (const [folderPath, folderFiles] of filesByFolder) {\n const targetFolderId = folderPath\n ? folderIdMap.get(folderPath)\n : folderId || undefined;\n\n for (const file of folderFiles) {\n const thumbnailUrl = await generateThumbnail(file);\n newItems.push({\n id: `${Date.now()}-${index++}-${file.name}`,\n file,\n status: \"pending\",\n progress: 0,\n folderId: targetFolderId,\n thumbnailUrl: thumbnailUrl ?? undefined,\n });\n }\n }\n\n setUploadQueue((prev) => [...prev, ...newItems]);\n setIsUploadPanelOpen(true);\n setUploading(true);\n\n for (const item of newItems) {\n await processUpload(item);\n }\n setUploading(false);\n\n toast.success(\n tWithFallback(\n t,\n \"files.upload.folder.success\",\n \"Uploaded {{count}} file{{plural}} from folder\",\n { count: newItems.length, plural: newItems.length > 1 ? \"s\" : \"\" },\n ),\n );\n }\n };\n input.click();\n };\n\n const handleNewFolderClick = () => {\n setShowNewFolderDialog(true);\n };\n\n const handleCreateFolder = async () => {\n if (newFolderName.trim() && workspaceId) {\n const { data: createdFolder, error } =\n await createFolderMutation.mutateAsync({\n name: newFolderName,\n parentFolderId: folderId,\n });\n\n if (error) {\n if (isQuotaExhaustedError(error)) {\n setShowNewFolderDialog(false);\n setNewFolderName(\"\");\n setShowFolderQuotaExhausted(true);\n } else {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.folder.errors.createFailed\",\n \"Folder creation failed\",\n ),\n );\n emitEvent(\"files.api.failed\", {\n action: \"createFolder\",\n message:\n error instanceof Error ? error.message : \"Folder creation failed\",\n });\n }\n return;\n }\n\n if (createdFolder) {\n setNewFolderName(\"\");\n setShowNewFolderDialog(false);\n emitEvent(\"files.folder.created\", {\n folderName: newFolderName,\n parentFolderId: folderId,\n });\n }\n }\n };\n\n // File action handlers\n const handleFilePreview = (file: FileItem) => {\n setPreviewFile(file);\n };\n\n const handleFileShare = (file: FileItem) => {\n setShareTarget(file);\n setShowShareDialog(true);\n };\n\n const handleFolderShare = (folder: FolderItem) => {\n setShareTarget(folder);\n setShowShareDialog(true);\n };\n\n const handleCreateShare = async (\n input: ShareDialogInput,\n ): Promise<ShareDialogResult> => {\n if (!input.fileId && !input.folderId) {\n throw new Error(\n tWithFallback(\n t,\n \"files.share.errors.targetRequired\",\n \"Share requires a file id or folder id\",\n ),\n );\n }\n\n const shareType = input.invitedEmail ? \"EMAIL_INVITE\" : \"PUBLIC_LINK\";\n let token = \"\";\n\n if (input.fileId) {\n // File share\n const shareInput: CreateShareInput = {\n fileId: input.fileId,\n expiresAt: input.expiresAt,\n maxDownloads: input.maxDownloads,\n password: input.password,\n invitedEmail: input.invitedEmail,\n shareType,\n accessLevel: \"DOWNLOAD\" as const,\n };\n const result = await createShareMutation.mutateAsync(shareInput);\n token = result?.token ?? \"\";\n emitEvent(\"files.share.created\", {\n entityType: \"file\",\n fileId: input.fileId,\n shareType,\n });\n } else if (input.folderId) {\n // Folder share\n const shareInput: CreateFolderShareInput = {\n folderId: input.folderId,\n expiresAt: input.expiresAt,\n maxDownloads: input.maxDownloads,\n password: input.password,\n invitedEmail: input.invitedEmail,\n shareType,\n accessLevel: \"DOWNLOAD\" as const,\n };\n const result = await createFolderShareMutation.mutateAsync(shareInput);\n token = result?.createFolderShare?.token ?? \"\";\n emitEvent(\"files.share.created\", {\n entityType: \"folder\",\n folderId: input.folderId,\n shareType,\n });\n }\n\n return {\n publicUrl: token ? `${window.location.origin}/files/share/${token}` : \"\",\n token,\n };\n };\n\n const handleFileDelete = async (file: FileItem) => {\n nativeImpact(\"medium\");\n setConfirmDialog({\n title: tWithFallback(t, \"files.confirm.deleteFile.title\", \"Delete file\"),\n description: tWithFallback(\n t,\n \"files.confirm.deleteFile.description\",\n 'Move \"{{name}}\" to the bin?',\n { name: file.name },\n ),\n confirmLabel: tWithFallback(t, \"files.common.actions.delete\", \"Delete\"),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n await deleteFileMutation.mutateAsync({ id: file.id });\n nativeNotify(\"success\");\n toast.success(\n tWithFallback(\n t,\n \"files.file.delete.success\",\n '\"{{name}}\" moved to bin',\n { name: file.name },\n ),\n );\n emitEvent(\"files.file.deleted\", {\n fileId: file.id,\n fileName: file.name,\n permanent: false,\n });\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.file.errors.deleteFailed\",\n 'Failed to delete \"{{name}}\"',\n { name: file.name },\n ),\n );\n nativeNotify(\"error\");\n emitEvent(\"files.api.failed\", {\n action: \"deleteFile\",\n fileId: file.id,\n message:\n error instanceof Error ? error.message : \"Failed to delete file\",\n });\n }\n },\n });\n };\n\n const handleFileRestore = async (file: FileItem) => {\n try {\n await restoreFileMutation.mutateAsync({ id: file.id });\n toast.success(\n tWithFallback(t, \"files.file.restore.success\", '\"{{name}}\" restored', {\n name: file.name,\n }),\n );\n emitEvent(\"files.file.restored\", {\n fileId: file.id,\n fileName: file.name,\n });\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.file.errors.restoreFailed\",\n \"Failed to restore file\",\n ),\n );\n }\n };\n\n const handleFilePermanentDelete = async (file: FileItem) => {\n nativeImpact(\"heavy\");\n setConfirmDialog({\n title: tWithFallback(\n t,\n \"files.confirm.permanentDeleteFile.title\",\n \"Permanently delete file\",\n ),\n description: tWithFallback(\n t,\n \"files.confirm.permanentDeleteFile.description\",\n 'Permanently delete \"{{name}}\"? This cannot be undone.',\n { name: file.name },\n ),\n confirmLabel: tWithFallback(\n t,\n \"files.common.actions.deletePermanently\",\n \"Delete permanently\",\n ),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n await permanentlyDeleteFileMutation.mutateAsync({ id: file.id });\n toast.success(\n tWithFallback(\n t,\n \"files.file.permanentDelete.success\",\n '\"{{name}}\" deleted permanently',\n { name: file.name },\n ),\n );\n emitEvent(\"files.file.deleted\", {\n fileId: file.id,\n fileName: file.name,\n permanent: true,\n });\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.file.errors.permanentDeleteFailed\",\n \"Failed to permanently delete file\",\n ),\n );\n }\n },\n });\n };\n\n const handleFileFavorite = async (file: FileItem) => {\n try {\n await toggleStarMutation.mutateAsync({ id: file.id, type: \"file\" });\n emitEvent(\"files.file.starred\", {\n fileId: file.id,\n fileName: file.name,\n starred: !file.starred,\n });\n } catch {\n toast.error(\n tWithFallback(\n t,\n \"files.file.errors.starFailed\",\n \"Failed to update starred state\",\n ),\n );\n emitEvent(\"files.api.failed\", {\n action: \"toggleFileStar\",\n fileId: file.id,\n });\n }\n };\n\n const handleFileRename = (file: FileItem) => {\n setRenameTarget({ id: file.id, name: file.name, type: \"file\" });\n setNewName(file.name);\n setShowRenameDialog(true);\n };\n\n const handleFileDownload = async (file: FileItem) => {\n try {\n const downloadUrl = file.downloadUrl || file.url;\n\n if (!downloadUrl) {\n throw new Error(\n tWithFallback(\n t,\n \"files.file.errors.downloadUrlUnavailable\",\n \"Download URL is unavailable\",\n ),\n );\n }\n\n const a = document.createElement(\"a\");\n a.style.display = \"none\";\n a.href = downloadUrl;\n a.download = file.name;\n a.rel = \"noopener noreferrer\";\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n\n toast.success(\n tWithFallback(t, \"files.file.download.success\", \"Downloaded {{name}}\", {\n name: file.name,\n }),\n );\n emitEvent(\"files.file.downloaded\", {\n fileId: file.id,\n fileName: file.name,\n });\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.file.errors.downloadFailed\",\n \"Failed to download file\",\n ),\n );\n }\n };\n\n const handleFileCopy = (file: FileItem) => {\n setCopyTarget({ item: file, type: \"file\" });\n setShowCopyDialog(true);\n };\n\n const handleFileMove = (file: FileItem) => {\n setMoveTarget({ item: file, type: \"file\" });\n setShowMoveDialog(true);\n };\n\n const handleFileInfoClick = (file: FileItem) => {\n setInfoPanel({ item: file, type: \"file\" });\n };\n\n // Folder action handlers\n const handleFolderOpen = (folder: FolderItem) => {\n // Navigate to folder by updating URL\n navigate(`/files/browse/${folder.id}`);\n };\n\n const handleFolderDelete = async (folder: FolderItem) => {\n nativeImpact(\"medium\");\n setConfirmDialog({\n title: tWithFallback(\n t,\n \"files.confirm.deleteFolder.title\",\n \"Delete folder\",\n ),\n description: tWithFallback(\n t,\n \"files.confirm.deleteFolder.description\",\n 'Move folder \"{{name}}\" and all its contents to the bin?',\n { name: folder.name },\n ),\n confirmLabel: tWithFallback(t, \"files.common.actions.delete\", \"Delete\"),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n await deleteFolderMutation.mutateAsync({\n id: folder.id,\n recursive: true,\n });\n toast.success(\n tWithFallback(\n t,\n \"files.folder.delete.success\",\n '\"{{name}}\" moved to bin',\n { name: folder.name },\n ),\n );\n emitEvent(\"files.folder.deleted\", {\n folderId: folder.id,\n folderName: folder.name,\n permanent: false,\n });\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.folder.errors.deleteFailed\",\n \"Failed to delete folder\",\n ),\n );\n }\n },\n });\n };\n\n const handleFolderRestore = async (folder: FolderItem) => {\n try {\n await restoreFolderMutation.mutateAsync({ id: folder.id });\n toast.success(\n tWithFallback(\n t,\n \"files.folder.restore.success\",\n '\"{{name}}\" restored',\n { name: folder.name },\n ),\n );\n emitEvent(\"files.folder.restored\", {\n folderId: folder.id,\n folderName: folder.name,\n });\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.folder.errors.restoreFailed\",\n \"Failed to restore folder\",\n ),\n );\n }\n };\n\n const handleFolderPermanentDelete = async (folder: FolderItem) => {\n setConfirmDialog({\n title: tWithFallback(\n t,\n \"files.confirm.permanentDeleteFolder.title\",\n \"Permanently delete folder\",\n ),\n description: tWithFallback(\n t,\n \"files.confirm.permanentDeleteFolder.description\",\n 'Permanently delete folder \"{{name}}\" and its contents? This cannot be undone.',\n { name: folder.name },\n ),\n confirmLabel: tWithFallback(\n t,\n \"files.common.actions.deletePermanently\",\n \"Delete permanently\",\n ),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n await permanentlyDeleteFolderMutation.mutateAsync({ id: folder.id });\n toast.success(\n tWithFallback(\n t,\n \"files.folder.permanentDelete.success\",\n '\"{{name}}\" deleted permanently',\n { name: folder.name },\n ),\n );\n emitEvent(\"files.folder.deleted\", {\n folderId: folder.id,\n folderName: folder.name,\n permanent: true,\n });\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.folder.errors.permanentDeleteFailed\",\n \"Failed to permanently delete folder\",\n ),\n );\n }\n },\n });\n };\n\n const handleFolderRename = (folder: FolderItem) => {\n setRenameTarget({ id: folder.id, name: folder.name, type: \"folder\" });\n setNewName(folder.name);\n setShowRenameDialog(true);\n };\n\n const handleFolderMove = (folder: FolderItem) => {\n setMoveTarget({ item: folder, type: \"folder\" });\n setShowMoveDialog(true);\n };\n\n const handleFolderFavorite = async (folder: FolderItem) => {\n try {\n await toggleStarMutation.mutateAsync({ id: folder.id, type: \"folder\" });\n emitEvent(\"files.folder.starred\", {\n folderId: folder.id,\n folderName: folder.name,\n starred: !folder.starred,\n });\n } catch {\n toast.error(\n tWithFallback(\n t,\n \"files.folder.errors.starFailed\",\n \"Failed to update starred state\",\n ),\n );\n emitEvent(\"files.api.failed\", {\n action: \"toggleFolderStar\",\n folderId: folder.id,\n });\n }\n };\n\n const handleFolderInfoClick = (folder: FolderItem) => {\n setInfoPanel({ item: folder, type: \"folder\" });\n };\n\n // Move and Copy handlers\n const handleMoveSubmit = async (destinationFolderId: string | null) => {\n if (!moveTarget) return;\n\n try {\n if (moveTarget.type === \"file\") {\n await moveFileMutation.mutateAsync({\n fileId: moveTarget.item.id,\n targetFolderId: destinationFolderId || null,\n });\n toast.success(\n tWithFallback(t, \"files.file.move.success\", '\"{{name}}\" moved', {\n name: moveTarget.item.name,\n }),\n );\n emitEvent(\"files.file.moved\", {\n fileId: moveTarget.item.id,\n fileName: moveTarget.item.name,\n destinationFolderId,\n });\n } else {\n await moveFolderMutation.mutateAsync({\n folderId: moveTarget.item.id,\n targetParentFolderId: destinationFolderId,\n });\n toast.success(\n tWithFallback(t, \"files.folder.move.success\", '\"{{name}}\" moved', {\n name: moveTarget.item.name,\n }),\n );\n emitEvent(\"files.folder.moved\", {\n folderId: moveTarget.item.id,\n folderName: moveTarget.item.name,\n destinationFolderId,\n });\n }\n setShowMoveDialog(false);\n setMoveTarget(null);\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.item.errors.moveFailed\",\n 'Failed to move \"{{name}}\"',\n {\n name: moveTarget.item.name,\n },\n ),\n );\n emitEvent(\"files.api.failed\", {\n action: moveTarget.type === \"file\" ? \"moveFile\" : \"moveFolder\",\n itemId: moveTarget.item.id,\n itemType: moveTarget.type,\n message: error instanceof Error ? error.message : undefined,\n });\n }\n };\n\n const handleCopySubmit = async (\n destinationFolderId: string | null,\n newName?: string,\n ) => {\n if (!copyTarget) return;\n\n try {\n if (copyTarget.type !== \"file\") {\n throw new Error(\n tWithFallback(\n t,\n \"files.folder.errors.copyUnsupported\",\n \"Folder copy is not supported\",\n ),\n );\n }\n\n await copyFileMutation.mutateAsync({\n id: copyTarget.item.id,\n newName: newName || `Copy of ${copyTarget.item.name}`,\n targetFolderId: destinationFolderId || undefined,\n });\n toast.success(\n tWithFallback(t, \"files.file.copy.success\", '\"{{name}}\" copied', {\n name: copyTarget.item.name,\n }),\n );\n emitEvent(\"files.file.copied\", {\n fileId: copyTarget.item.id,\n fileName: copyTarget.item.name,\n destinationFolderId,\n });\n\n setShowCopyDialog(false);\n setCopyTarget(null);\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.file.errors.copyFailed\",\n 'Failed to copy \"{{name}}\"',\n {\n name: copyTarget.item.name,\n },\n ),\n );\n emitEvent(\"files.api.failed\", {\n action: \"copyFile\",\n fileId: copyTarget.item.id,\n message: error instanceof Error ? error.message : undefined,\n });\n }\n };\n\n const handleRenameSubmit = async () => {\n if (!renameTarget || !newName.trim()) return;\n\n try {\n const previousName = renameTarget.name;\n const nextName = newName.trim();\n\n if (renameTarget.type === \"file\") {\n await renameFileMutation.mutateAsync({\n fileId: renameTarget.id,\n newName: nextName,\n });\n toast.success(\n tWithFallback(t, \"files.file.rename.success\", 'Renamed \"{{name}}\"', {\n name: nextName,\n }),\n );\n emitEvent(\"files.file.renamed\", {\n fileId: renameTarget.id,\n previousName,\n name: nextName,\n });\n } else {\n await renameFolderMutation.mutateAsync({\n folderId: renameTarget.id,\n newName: nextName,\n });\n toast.success(\n tWithFallback(\n t,\n \"files.folder.rename.success\",\n 'Renamed \"{{name}}\"',\n {\n name: nextName,\n },\n ),\n );\n emitEvent(\"files.folder.renamed\", {\n folderId: renameTarget.id,\n previousName,\n name: nextName,\n });\n }\n setShowRenameDialog(false);\n setRenameTarget(null);\n setNewName(\"\");\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.item.errors.renameFailed\",\n \"Failed to rename {{type}}\",\n {\n type: renameTarget.type,\n },\n ),\n );\n emitEvent(\"files.api.failed\", {\n action: renameTarget.type === \"file\" ? \"renameFile\" : \"renameFolder\",\n itemId: renameTarget.id,\n itemType: renameTarget.type,\n message: error instanceof Error ? error.message : undefined,\n });\n }\n };\n\n const handleSearch = (query: string, filters?: SearchFilters) => {\n setSearchQuery(query);\n setSearchFilters(filters);\n };\n\n useKeyboardNavigation({\n files,\n folders,\n onFilePreview: handleFilePreview,\n onFileDelete: handleFileDelete,\n onFolderOpen: handleFolderOpen,\n onFolderDelete: handleFolderDelete,\n activeTab,\n });\n\n // Guard: if the host shell hasn't pushed workspace context yet, render a\n // clear loading state rather than firing queries with an undefined workspace\n // (which used to manifest as a silent blank screen).\n // NOTE: We explicitly check for undefined (not yet provided) vs empty string\n // (explicitly no workspace selected) to show appropriate messaging.\n if (workspaceId === undefined) {\n return (\n <div className=\"flex flex-col items-center justify-center min-h-[60vh] gap-3 text-center px-6\">\n <Loader2 className=\"size-8 animate-spin text-text-tertiary\" />\n <p className=\"text-sm text-text-secondary max-w-md\">\n {tWithFallback(\n t,\n \"files.browser.workspace.waiting\",\n \"Waiting for workspace context. If this persists, the app shell may not have provided a workspaceId to the Files module. Try reloading the page or switching workspaces.\",\n )}\n </p>\n </div>\n );\n }\n\n // Empty string means host shell provided context but no workspace is selected\n if (workspaceId === \"\") {\n return (\n <div className=\"flex flex-col items-center justify-center min-h-[60vh] gap-3 text-center px-6\">\n <FolderX className=\"size-8 text-text-tertiary\" />\n <p className=\"text-sm text-text-secondary max-w-md\">\n {tWithFallback(\n t,\n \"files.browser.workspace.notSelected\",\n \"No workspace selected. Please select a workspace to view files.\",\n )}\n </p>\n </div>\n );\n }\n\n return (\n <div className=\"min-h-screen bg-bg-surface\">\n {/* Google Drive-style Header with Breadcrumb Navigation */}\n <div\n className=\"bg-bg-surface border-b border-border-default px-4 py-3 sm:px-6 sm:py-4\"\n data-tour=\"files-breadcrumb-nav\"\n >\n <div className=\"flex items-center gap-2 sm:gap-4\">\n <div className=\"flex items-center gap-1 sm:gap-2 text-sm overflow-x-auto\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/files/browse\")}\n className=\"flex items-center gap-1 text-text-primary hover:bg-action-ghost-bg-hover px-2 py-1 rounded transition-colors duration-200 whitespace-nowrap\"\n title={tWithFallback(\n t,\n \"files.browser.actions.goToRoot\",\n \"Go to root\",\n )}\n >\n <Home className=\"size-4\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(t, \"files.browser.rootLabel\", \"My Drive\")}\n </span>\n </button>\n {breadcrumbs.map((crumb, index) => (\n <div key={crumb.id} className=\"flex items-center gap-1 sm:gap-2\">\n <span className=\"text-text-tertiary\">/</span>\n <button\n type=\"button\"\n onClick={() => navigate(`/files/browse/${crumb.id}`)}\n className={`hover:bg-action-ghost-bg-hover px-2 py-1 rounded transition-colors duration-200 whitespace-nowrap ${\n index === breadcrumbs.length - 1\n ? \"text-text-primary font-medium\"\n : \"text-text-secondary\"\n }`}\n title={`Go to ${crumb.name}`}\n >\n {crumb.name}\n </button>\n </div>\n ))}\n </div>\n </div>\n </div>\n\n {/* Navigation Tabs */}\n <div data-tour=\"files-tab-bar\">\n <TabBar\n activeTab={activeTab}\n onTabChange={setActiveTab}\n tabs={enabledTabs}\n />\n </div>\n\n {/* Google Drive-style Toolbar */}\n <div className=\"bg-bg-surface border-b border-border-default px-4 py-2 sm:px-6 sm:py-3\">\n <div className=\"flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2 sm:gap-inline-gap\">\n {/* Left side - Search */}\n <div\n className=\"w-full sm:flex-1 min-w-0 order-2 sm:order-1\"\n data-tour=\"files-search-bar\"\n >\n <div className=\"relative w-full max-w-2xl\">\n <SearchBar\n onSearch={handleSearch}\n placeholder={tWithFallback(\n t,\n \"files.search.input.placeholder\",\n \"Search files and folders...\",\n )}\n initialQuery={searchQuery}\n showFilters={true}\n />\n </div>\n </div>\n\n {/* Right side - Actions */}\n <div className=\"flex items-center gap-2 order-1 sm:order-2 w-full sm:w-auto justify-between sm:justify-end\">\n {/* Select All button */}\n {!isSearching && (files.length > 0 || folders.length > 0) && (\n <button\n type=\"button\"\n onClick={() => {\n const allFileIds = files.map((f) => f.id);\n const allFolderIds = folders.map((f) => f.id);\n useFilesStore.getState().selectAllFiles(allFileIds);\n allFolderIds.forEach((id) =>\n useFilesStore.getState().selectFolder(id),\n );\n }}\n className=\"flex items-center gap-2 px-3 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-lg hover:bg-action-ghost-bg-hover hover:border-border-default transition-all duration-200\"\n >\n {tWithFallback(\n t,\n \"files.browser.actions.selectAll\",\n \"Select All\",\n )}\n </button>\n )}\n\n {/* Only show New Folder and Upload buttons in \"All Files\" tab */}\n {!isSearching && (\n <>\n <button\n type=\"button\"\n onClick={handleNewFolderClick}\n className=\"flex items-center gap-1 sm:gap-2 px-2 sm:px-4 py-2 text-xs sm:text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-lg hover:bg-action-ghost-bg-hover hover:border-border-default transition-all duration-200 shadow-sm\"\n data-tour=\"files-new-folder-button\"\n >\n <FolderPlus className=\"size-4\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(\n t,\n \"files.browser.actions.newFolder\",\n \"New Folder\",\n )}\n </span>\n </button>\n\n <button\n type=\"button\"\n onClick={handleUploadClick}\n disabled={uploading}\n className=\"flex items-center gap-1 sm:gap-2 px-2 sm:px-4 py-2 text-xs sm:text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-lg hover:bg-action-ghost-bg-hover hover:border-border-default transition-all duration-200 shadow-sm disabled:opacity-50 disabled:cursor-not-allowed\"\n data-tour=\"files-upload-files-button\"\n >\n {uploading ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(\n t,\n \"files.browser.upload.uploading\",\n \"Uploading...\",\n )}\n </span>\n </>\n ) : (\n <>\n <Upload className=\"size-4\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(\n t,\n \"files.browser.actions.uploadFiles\",\n \"Upload Files\",\n )}\n </span>\n </>\n )}\n </button>\n\n <button\n type=\"button\"\n onClick={handleFolderUploadClick}\n disabled={uploading}\n className=\"hidden sm:flex items-center gap-2 px-4 py-2.5 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-lg hover:bg-action-ghost-bg-hover hover:border-border-default transition-all duration-200 shadow-sm disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tWithFallback(\n t,\n \"files.browser.actions.uploadFolderTitle\",\n \"Upload entire folder with structure\",\n )}\n >\n <FolderUp className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.browser.actions.uploadFolder\",\n \"Upload Folder\",\n )}\n </button>\n\n {/* Export button removed per user request */}\n </>\n )}\n\n {/* View mode toggle */}\n <div\n className=\"flex items-center border border-border-default rounded-lg overflow-hidden\"\n data-tour=\"files-view-mode-toggle\"\n >\n <button\n type=\"button\"\n onClick={() => setViewMode(\"grid\")}\n className={`p-2 transition-colors duration-200 ${\n viewMode === \"grid\"\n ? \"bg-bg-accent text-text-link\"\n : \"text-text-secondary hover:bg-action-ghost-bg-hover\"\n }`}\n aria-label={tWithFallback(\n t,\n \"files.browser.view.grid\",\n \"Grid view\",\n )}\n >\n <Grid3x3 className=\"size-5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode(\"list\")}\n className={`p-2 transition-colors duration-200 ${\n viewMode === \"list\"\n ? \"bg-bg-accent text-text-link\"\n : \"text-text-secondary hover:bg-action-ghost-bg-hover\"\n }`}\n aria-label={tWithFallback(\n t,\n \"files.browser.view.list\",\n \"List view\",\n )}\n >\n <List className=\"size-5\" />\n </button>\n </div>\n </div>\n </div>\n\n {/* Search results indicator */}\n {isSearching && (\n <div className=\"mt-3 flex items-center justify-between p-3 bg-status-success-bg-subtle rounded-lg border border-status-success-border transition-all duration-200\">\n <span className=\"text-sm font-medium text-status-success-text\">\n {tWithFallback(\n t,\n \"files.browser.search.showingResults\",\n 'Showing search results for \"{{query}}\"',\n { query: searchQuery },\n )}\n {searchResults.data &&\n ` (${(searchResults.data.files?.length || 0) + (searchResults.data.folders?.length || 0)} items)`}\n </span>\n <button\n type=\"button\"\n onClick={() => setSearchQuery(\"\")}\n className=\"flex items-center gap-2 text-status-success-text hover:text-status-success-text/80 text-sm font-medium transition-colors duration-200\"\n >\n <X className=\"size-4\" />\n {tWithFallback(t, \"files.browser.search.clear\", \"Clear search\")}\n </button>\n </div>\n )}\n\n {/* Selection info with bulk actions */}\n {totalSelected > 0 && (\n <div className=\"mt-3 flex items-center justify-between p-3 bg-bg-accent rounded-lg border border-action-primary-border transition-all duration-200\">\n <div className=\"flex items-center gap-4\">\n <span className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(\n t,\n \"files.browser.selection.selectedCount\",\n \"{{count}} item{{suffix}} selected\",\n {\n count: totalSelected,\n suffix: totalSelected > 1 ? \"s\" : \"\",\n },\n )}\n </span>\n\n {/* Bulk action buttons */}\n <div className=\"flex items-center gap-2\">\n {activeTab === \"bin\" ? (\n <>\n <button\n type=\"button\"\n onClick={async () => {\n try {\n await Promise.all([\n ...Array.from(selectedFiles).map((fileId) =>\n restoreFileMutation.mutateAsync({ id: fileId }),\n ),\n ...Array.from(selectedFolders).map((folderId) =>\n restoreFolderMutation.mutateAsync({\n id: folderId,\n }),\n ),\n ]);\n\n clearSelection();\n toast.success(\n `${totalSelected} item${totalSelected > 1 ? \"s\" : \"\"} restored`,\n );\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : \"Failed to restore some items\",\n );\n }\n }}\n className=\"flex items-center gap-1.5 rounded-lg border border-status-success-border bg-bg-surface px-3 py-1.5 text-sm font-medium text-status-success-text transition-colors duration-200 hover:bg-status-success-bg-subtle\"\n >\n {tWithFallback(\n t,\n \"files.browser.selection.actions.restore\",\n \"Restore\",\n )}\n </button>\n\n <button\n type=\"button\"\n onClick={async () => {\n setConfirmDialog({\n title: tWithFallback(\n t,\n \"files.confirm.bulkPermanentDelete.title\",\n \"Permanently delete selected items\",\n ),\n description: tWithFallback(\n t,\n \"files.confirm.bulkPermanentDelete.description\",\n \"Permanently delete {{count}} selected item{{plural}}? This cannot be undone.\",\n {\n count: totalSelected,\n plural: totalSelected > 1 ? \"s\" : \"\",\n },\n ),\n confirmLabel: tWithFallback(\n t,\n \"files.common.actions.deletePermanently\",\n \"Delete permanently\",\n ),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n await Promise.all([\n ...Array.from(selectedFiles).map((fileId) =>\n permanentlyDeleteFileMutation.mutateAsync({\n id: fileId,\n }),\n ),\n ...Array.from(selectedFolders).map((folderId) =>\n permanentlyDeleteFolderMutation.mutateAsync({\n id: folderId,\n }),\n ),\n ]);\n\n clearSelection();\n toast.success(\n tWithFallback(\n t,\n \"files.bulk.permanentDelete.success\",\n \"{{count}} item{{plural}} deleted permanently\",\n {\n count: totalSelected,\n plural: totalSelected > 1 ? \"s\" : \"\",\n },\n ),\n );\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.bulk.errors.permanentDeleteFailed\",\n \"Failed to permanently delete some items\",\n ),\n );\n }\n },\n });\n }}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-status-error-text bg-bg-surface border border-status-error-border rounded-lg hover:bg-status-error-bg-subtle transition-colors duration-200\"\n >\n <Trash2 className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.browser.selection.actions.deletePermanently\",\n \"Delete permanently\",\n )}\n </button>\n </>\n ) : (\n <button\n type=\"button\"\n onClick={async () => {\n setConfirmDialog({\n title: tWithFallback(\n t,\n \"files.confirm.bulkDelete.title\",\n \"Delete selected items\",\n ),\n description: tWithFallback(\n t,\n \"files.confirm.bulkDelete.description\",\n \"Move {{count}} selected item{{plural}} to the bin?\",\n {\n count: totalSelected,\n plural: totalSelected > 1 ? \"s\" : \"\",\n },\n ),\n confirmLabel: tWithFallback(\n t,\n \"files.common.actions.delete\",\n \"Delete\",\n ),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n const fileIds = Array.from(selectedFiles);\n const folderIds = Array.from(selectedFolders);\n\n if (fileIds.length > 0) {\n await bulkDeleteMutation.mutateAsync(fileIds);\n }\n\n await Promise.all(\n folderIds.map((folderId) =>\n deleteFolderMutation.mutateAsync({\n id: folderId,\n recursive: true,\n }),\n ),\n );\n\n clearSelection();\n toast.success(\n tWithFallback(\n t,\n \"files.bulk.delete.success\",\n \"{{count}} item{{plural}} moved to bin\",\n {\n count: totalSelected,\n plural: totalSelected > 1 ? \"s\" : \"\",\n },\n ),\n );\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.bulk.errors.deleteFailed\",\n \"Failed to delete some items\",\n ),\n );\n }\n },\n });\n }}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-status-error-text bg-bg-surface border border-status-error-border rounded-lg hover:bg-status-error-bg-subtle transition-colors duration-200\"\n >\n <Trash2 className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.browser.selection.actions.delete\",\n \"Delete\",\n )}\n </button>\n )}\n </div>\n </div>\n\n <button\n type=\"button\"\n onClick={clearSelection}\n className=\"flex items-center gap-2 text-text-link hover:text-action-primary-bg-hover text-sm font-medium transition-colors duration-200\"\n >\n <X className=\"size-4\" />\n {tWithFallback(t, \"files.common.actions.clear\", \"Clear\")}\n </button>\n </div>\n )}\n </div>\n\n {/* Storage quota banner */}\n {(storageQuota.isExhausted || storageQuota.isNearLimit) && (\n <div className=\"px-4 sm:px-6 pt-3\">\n <StorageBanner quota={storageQuota} />\n </div>\n )}\n\n {/* File Browser */}\n <div\n className=\"px-2 sm:px-4 md:px-6 py-3 sm:py-4\"\n data-tour=\"files-browser-grid\"\n >\n <FileBrowser\n files={files}\n folders={folders}\n loading={loading}\n error={error}\n preserveSortOrder={preserveSortOrder}\n onFilePreview={handleFilePreview}\n onFileShare={activeTab === \"bin\" ? undefined : handleFileShare}\n onFileDelete={\n activeTab === \"bin\" ? handleFilePermanentDelete : handleFileDelete\n }\n onFileRestore={activeTab === \"bin\" ? handleFileRestore : undefined}\n onFileFavorite={activeTab === \"bin\" ? undefined : handleFileFavorite}\n onFileRename={activeTab === \"bin\" ? undefined : handleFileRename}\n onFileDownload={handleFileDownload}\n onFileCopy={activeTab === \"bin\" ? undefined : handleFileCopy}\n onFileMove={activeTab === \"bin\" ? undefined : handleFileMove}\n onFileInfo={handleFileInfoClick}\n onFolderOpen={handleFolderOpen}\n onFolderShare={activeTab === \"bin\" ? undefined : handleFolderShare}\n onFolderDelete={\n activeTab === \"bin\"\n ? handleFolderPermanentDelete\n : handleFolderDelete\n }\n onFolderRestore={\n activeTab === \"bin\" ? handleFolderRestore : undefined\n }\n onFolderRename={activeTab === \"bin\" ? undefined : handleFolderRename}\n onFolderFavorite={\n activeTab === \"bin\" ? undefined : handleFolderFavorite\n }\n onFolderMove={activeTab === \"bin\" ? undefined : handleFolderMove}\n onFolderInfo={handleFolderInfoClick}\n />\n\n {/* Results Summary */}\n {!loading && (files.length > 0 || folders.length > 0) && (\n <div className=\"mt-4 px-2 text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.browser.results.showingItems\",\n \"Showing {{count}} items\",\n { count: files.length + folders.length },\n )}\n {!isSearching &&\n activeTab === \"all\" &&\n totalItemsInCurrentView !== null &&\n ` ${tWithFallback(\n t,\n \"files.browser.results.ofItems\",\n \"of {{count}} items\",\n { count: totalItemsInCurrentView },\n )}`}\n </div>\n )}\n </div>\n\n {/* New Folder Dialog */}\n {showNewFolderDialog && (\n <div\n className=\"fixed inset-0 backdrop-blur-sm flex items-center justify-center z-[100] animate-in fade-in duration-200\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"files-new-folder-title\"\n >\n <div className=\"bg-bg-surface rounded-lg shadow-xl p-6 w-full max-w-xs mx-4 animate-in zoom-in-95 duration-200\">\n <h2\n id=\"files-new-folder-title\"\n className=\"text-base font-semibold text-text-primary mb-4\"\n >\n {tWithFallback(\n t,\n \"files.browser.newFolderDialog.title\",\n \"New folder\",\n )}\n </h2>\n <input\n type=\"text\"\n value={newFolderName}\n onChange={(e) => setNewFolderName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleCreateFolder();\n if (e.key === \"Escape\") {\n setShowNewFolderDialog(false);\n setNewFolderName(\"\");\n }\n }}\n aria-label={tWithFallback(\n t,\n \"files.browser.newFolderDialog.title\",\n \"New folder\",\n )}\n placeholder={tWithFallback(\n t,\n \"files.browser.newFolderDialog.placeholder\",\n \"Folder name\",\n )}\n className=\"w-full px-4 py-3 border-2 border-border-default rounded-lg focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200\"\n />\n <div className=\"flex justify-end gap-2 mt-6\">\n <button\n type=\"button\"\n onClick={() => {\n setShowNewFolderDialog(false);\n setNewFolderName(\"\");\n }}\n className=\"px-6 py-2.5 text-sm font-medium text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n >\n {tWithFallback(t, \"files.common.actions.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"button\"\n onClick={handleCreateFolder}\n disabled={\n !newFolderName.trim() || createFolderMutation.isPending\n }\n className=\"px-6 py-2.5 text-sm font-medium text-text-inverse bg-action-primary-bg rounded-lg hover:bg-action-primary-bg-hover disabled:bg-bg-muted disabled:text-text-tertiary disabled:cursor-not-allowed transition-colors duration-200 flex items-center gap-2\"\n >\n {createFolderMutation.isPending && (\n <Loader2 className=\"size-4 animate-spin\" />\n )}\n {createFolderMutation.isPending\n ? tWithFallback(\n t,\n \"files.browser.newFolderDialog.actions.creating\",\n \"Creating...\",\n )\n : tWithFallback(t, \"files.common.actions.create\", \"Create\")}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Rename Dialog */}\n {showRenameDialog && renameTarget && (\n <div\n className=\"fixed inset-0 backdrop-blur-sm flex items-center justify-center z-[100] animate-in fade-in duration-200\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"files-rename-title\"\n >\n <div className=\"bg-bg-surface rounded-lg shadow-xl p-6 w-full max-w-xs mx-4 animate-in zoom-in-95 duration-200\">\n <h2\n id=\"files-rename-title\"\n className=\"text-base font-semibold text-text-primary mb-4\"\n >\n {tWithFallback(\n t,\n \"files.browser.renameDialog.title\",\n \"Rename {{type}}\",\n { type: renameTarget.type },\n )}\n </h2>\n <input\n type=\"text\"\n value={newName}\n onChange={(e) => setNewName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleRenameSubmit();\n if (e.key === \"Escape\") {\n setShowRenameDialog(false);\n setRenameTarget(null);\n setNewName(\"\");\n }\n }}\n aria-label={tWithFallback(\n t,\n \"files.browser.renameDialog.title\",\n \"Rename\",\n )}\n placeholder={tWithFallback(\n t,\n \"files.browser.renameDialog.placeholder\",\n \"New name\",\n )}\n className=\"w-full px-4 py-3 border-2 border-border-default rounded-lg focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200\"\n />\n <div className=\"flex justify-end gap-2 mt-6\">\n <button\n type=\"button\"\n onClick={() => {\n setShowRenameDialog(false);\n setRenameTarget(null);\n setNewName(\"\");\n }}\n className=\"px-6 py-2.5 text-sm font-medium text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n >\n {tWithFallback(t, \"files.common.actions.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"button\"\n onClick={handleRenameSubmit}\n disabled={\n !newName.trim() ||\n newName.trim() === renameTarget.name ||\n renameFileMutation.isPending ||\n renameFolderMutation.isPending\n }\n className=\"px-6 py-2.5 text-sm font-medium text-text-inverse bg-action-primary-bg rounded-lg hover:bg-action-primary-bg-hover disabled:bg-bg-muted disabled:text-text-tertiary disabled:cursor-not-allowed transition-colors duration-200 flex items-center gap-2\"\n >\n {(renameFileMutation.isPending ||\n renameFolderMutation.isPending) && (\n <Loader2 className=\"size-4 animate-spin\" />\n )}\n {renameFileMutation.isPending || renameFolderMutation.isPending\n ? tWithFallback(\n t,\n \"files.browser.renameDialog.actions.renaming\",\n \"Renaming...\",\n )\n : tWithFallback(t, \"files.common.actions.rename\", \"Rename\")}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Share Dialog */}\n {showShareDialog && shareTarget && (\n <ShareDialog\n item={shareTarget}\n itemType={\"mimeType\" in shareTarget ? \"file\" : \"folder\"}\n onClose={() => {\n setShowShareDialog(false);\n setShareTarget(null);\n }}\n onCreateShare={handleCreateShare}\n isLoading={createShareMutation.isPending}\n />\n )}\n\n {confirmDialog && (\n <ConfirmDialog\n title={confirmDialog.title}\n description={confirmDialog.description}\n confirmLabel={confirmDialog.confirmLabel}\n cancelLabel={tWithFallback(\n t,\n \"files.common.actions.cancel\",\n \"Cancel\",\n )}\n variant={confirmDialog.variant}\n loading={confirmDialog.loading}\n onConfirm={runConfirmedAction}\n onCancel={() => {\n if (!confirmDialog.loading) {\n setConfirmDialog(null);\n }\n }}\n />\n )}\n\n {/* Folder Quota Exhausted Dialog */}\n {showFolderQuotaExhausted && (\n <ConfirmDialog\n title={tWithFallback(\n t,\n \"files.folder.quota.title\",\n \"Folder Limit Reached\",\n )}\n description={tWithFallback(\n t,\n \"files.folder.quota.description\",\n \"You've reached the folder limit for your plan. Upgrade your plan or purchase an add-on to create more folders.\",\n )}\n confirmLabel={tWithFallback(\n t,\n \"files.folder.quota.upgrade\",\n \"View Billing\",\n )}\n cancelLabel={tWithFallback(\n t,\n \"files.common.actions.dismiss\",\n \"Dismiss\",\n )}\n onConfirm={() => {\n setShowFolderQuotaExhausted(false);\n navigate(\"/billing\");\n }}\n onCancel={() => setShowFolderQuotaExhausted(false)}\n />\n )}\n\n {/* Move Dialog */}\n {showMoveDialog && moveTarget && (\n <MoveDialog\n item={moveTarget.item}\n type={moveTarget.type}\n onClose={() => {\n setShowMoveDialog(false);\n setMoveTarget(null);\n }}\n onMove={handleMoveSubmit}\n isLoading={moveFileMutation.isPending || moveFolderMutation.isPending}\n />\n )}\n\n {/* Copy Dialog */}\n {showCopyDialog && copyTarget && (\n <CopyDialog\n item={copyTarget.item}\n type={copyTarget.type}\n onClose={() => {\n setShowCopyDialog(false);\n setCopyTarget(null);\n }}\n onCopy={handleCopySubmit}\n isLoading={copyFileMutation.isPending}\n />\n )}\n\n {/* File Preview Modal */}\n {previewFile && (\n <FilePreviewModal\n file={previewFile}\n onClose={() => setPreviewFile(null)}\n onDownload={() => {\n const url = previewFile.downloadUrl || previewFile.url;\n void nativeOpenUrl(url);\n }}\n onShare={\n activeTab === \"bin\"\n ? undefined\n : () => {\n setShareTarget(previewFile);\n setShowShareDialog(true);\n }\n }\n onStar={\n activeTab === \"bin\"\n ? undefined\n : () => handleFileFavorite(previewFile)\n }\n onDelete={() =>\n activeTab === \"bin\"\n ? handleFilePermanentDelete(previewFile)\n : handleFileDelete(previewFile)\n }\n />\n )}\n\n {/* File Info Panel */}\n {infoPanel && (\n <FileInfoPanel\n item={infoPanel.item}\n type={infoPanel.type}\n onClose={() => setInfoPanel(null)}\n />\n )}\n\n {/* Upload Progress Indicator */}\n {/* Upload Queue */}\n <UploadQueue\n items={uploadQueue}\n isOpen={isUploadPanelOpen}\n onClose={() => setIsUploadPanelOpen(false)}\n onPause={handlePauseUpload}\n onResume={handleResumeUpload}\n onRetry={handleRetryUpload}\n onCancel={handleCancelUpload}\n />\n\n {/* Toast Notifications */}\n <ToastContainer toasts={toast.toasts} onClose={toast.removeToast} />\n\n {/* Empty State */}\n {!loading && files.length === 0 && folders.length === 0 && (\n <div className=\"px-6 py-12\">\n {isSearching ? (\n <div className=\"text-center animate-in fade-in duration-300\">\n <div className=\"inline-flex items-center justify-center size-16 bg-bg-muted rounded-full mb-4\">\n <Search className=\"size-8 text-text-tertiary\" />\n </div>\n <h3 className=\"text-lg font-medium text-text-primary mb-2\">\n {tWithFallback(\n t,\n \"files.browser.empty.search.title\",\n \"No results found\",\n )}\n </h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n {tWithFallback(\n t,\n \"files.browser.empty.search.description\",\n \"Try adjusting your search query or filters\",\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => {\n setSearchQuery(\"\");\n setSearchFilters(undefined);\n }}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-text-inverse bg-action-primary-bg rounded-lg hover:bg-action-primary-bg-hover transition-colors duration-200\"\n >\n {tWithFallback(t, \"files.browser.search.clear\", \"Clear search\")}\n </button>\n </div>\n ) : (\n <EmptyState\n type={activeTab}\n onUpload={activeTab === \"all\" ? handleUploadClick : undefined}\n onNewFolder={\n activeTab === \"all\" ? handleNewFolderClick : undefined\n }\n />\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-files",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.601.2",
|
|
4
4
|
"description": "Burdenoff Files Microfrontend - Standalone file management module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"imports": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@apollo/client": "^4.0.13",
|
|
48
|
-
"@burdenoff/fe-libs": "2026.
|
|
48
|
+
"@burdenoff/fe-libs": "2026.601.4",
|
|
49
49
|
"@tanstack/react-query": "^5.90.17",
|
|
50
50
|
"graphql": "^16.12.0",
|
|
51
51
|
"lucide-react": "^0.468.0",
|