Files
DentalManagement2025/apps/Frontend/src/pages/cloud-storage-page.tsx

178 lines
5.6 KiB
TypeScript

import { useState } from "react";
import { useQueryClient } from "@tanstack/react-query";
import { Button } from "@/components/ui/button";
import { Folder as FolderIcon, Search as SearchIcon } from "lucide-react";
import { apiRequest } from "@/lib/queryClient";
import { useToast } from "@/hooks/use-toast";
import { NewFolderModal } from "@/components/cloud-storage/new-folder-modal";
import FolderPanel from "@/components/cloud-storage/folder-panel";
import { useAuth } from "@/hooks/use-auth";
import RecentTopLevelFoldersCard, {
recentTopLevelFoldersQueryKey,
} from "@/components/cloud-storage/recent-top-level-folder-modal";
import CloudSearchBar, {
cloudSearchQueryKeyRoot,
} from "@/components/cloud-storage/search-bar";
import FilePreviewModal from "@/components/cloud-storage/file-preview-modal";
import { cloudFilesQueryKeyRoot } from "@/components/cloud-storage/files-section";
export default function CloudStoragePage() {
const { toast } = useToast();
const { user } = useAuth();
const qc = useQueryClient();
// panel open + initial folder id to show when opening
const [panelOpen, setPanelOpen] = useState(false);
const [panelInitialFolderId, setPanelInitialFolderId] = useState<
number | null
>(null);
// key to remount recent card to clear its internal selection when needed
const [recentKey, setRecentKey] = useState(0);
// New folder modal
const [isNewFolderOpen, setIsNewFolderOpen] = useState(false);
// searchbar - file preview modal state
const [previewFileId, setPreviewFileId] = useState<number | null>(null);
const [isPreviewOpen, setIsPreviewOpen] = useState(false);
// searchbar - handlers
function handleOpenFolder(folderId: number | null) {
setPanelInitialFolderId(folderId);
setPanelOpen(true);
}
function handleSelectFile(fileId: number) {
setPreviewFileId(fileId);
setIsPreviewOpen(true);
}
// create folder handler (page-level)
async function handleCreateFolder(name: string) {
try {
const userId = user?.id;
if (!userId) {
toast({
title: "Sign in required",
description: "Please sign in to create a folder.",
});
return;
}
const res = await apiRequest("POST", `/api/cloud-storage/folders`, {
userId,
name,
parentId: null,
});
const json = await res.json();
if (!res.ok) throw new Error(json?.message || "Failed to create folder");
toast({ title: "Folder created" });
// close modal
setIsNewFolderOpen(false);
// Invalidate recent folders page 1 so RecentFoldersCard will refresh.
qc.invalidateQueries({ queryKey: recentTopLevelFoldersQueryKey(1) });
} catch (err: any) {
toast({ title: "Error", description: err?.message || String(err) });
}
}
return (
<div className="container mx-auto space-y-6">
{/* Header / actions */}
<div className="flex justify-between items-center">
<div>
<h1 className="text-3xl font-bold tracking-tight">Cloud Storage</h1>
<p className="text-muted-foreground">
Manage Files and Folders in Cloud Storage.
</p>
</div>
<div className="flex gap-2 items-center">
<Button onClick={() => setIsNewFolderOpen(true)}>
<FolderIcon className="h-4 w-4 mr-2" /> New Folder
</Button>
</div>
</div>
<CloudSearchBar
onOpenFolder={(folderId) => {
handleOpenFolder(folderId);
}}
onSelectFile={(fileId) => {
handleSelectFile(fileId);
}}
/>
{/* Recent folders card (delegated component) */}
<RecentTopLevelFoldersCard
key={recentKey}
pageSize={10}
initialPage={1}
onSelect={(folderId) => {
setPanelInitialFolderId(folderId);
setPanelOpen(true);
}}
/>
{/* FolderPanel lives in page so it can be reused with other UI */}
{panelOpen && (
<FolderPanel
folderId={panelInitialFolderId}
onClose={() => setPanelOpen(false)}
onViewChange={(viewedId: any) => {
// If the panel navigates back to root, clear recent card selection by remounting it
if (viewedId === null) {
setRecentKey((k) => k + 1);
// clear the panel initial id and close the panel so child folder/file sections hide
setPanelInitialFolderId(null);
setPanelOpen(false);
}
}}
/>
)}
{/* File preview modal */}
<FilePreviewModal
fileId={previewFileId}
isOpen={isPreviewOpen}
onClose={() => {
setIsPreviewOpen(false);
setPreviewFileId(null);
}}
onDeleted={() => {
// close preview (modal may already close, but be explicit)
setIsPreviewOpen(false);
setPreviewFileId(null);
// refresh the recent folders card
qc.invalidateQueries({ queryKey: recentTopLevelFoldersQueryKey(1) });
// invalidate file lists and search results
qc.invalidateQueries({
queryKey: cloudFilesQueryKeyRoot,
exact: false,
});
qc.invalidateQueries({
queryKey: cloudSearchQueryKeyRoot,
exact: false,
});
// remount the recent card so it clears internal selection (UX nicety)
setRecentKey((k) => k + 1);
}}
/>
{/* New folder modal (reusable) */}
<NewFolderModal
isOpen={isNewFolderOpen}
onClose={() => setIsNewFolderOpen(false)}
onSubmit={handleCreateFolder}
/>
</div>
);
}