import { Modal, Button, SimpleGrid, FileButton, Group, Text, Tooltip, } from "@mantine/core"; import { IconBrandNotion, IconCheck, IconFileCode, IconFileTypeZip, IconMarkdown, IconX, } from "@tabler/icons-react"; import { importPage, importZip, } from "@/features/page/services/page-service.ts"; import { notifications } from "@mantine/notifications"; import { treeDataAtom } from "@/features/page/tree/atoms/tree-data-atom.ts"; import { useAtom } from "jotai"; import { buildTree } from "@/features/page/tree/utils"; import { IPage } from "@/features/page/types/page.types.ts"; import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { ConfluenceIcon } from "@/components/icons/confluence-icon.tsx"; import { getFileImportSizeLimit, isCloud } from "@/lib/config.ts"; import { formatBytes } from "@/lib"; import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts"; import { getFileTaskById } from "@/features/file-task/services/file-task-service.ts"; import { queryClient } from "@/main.tsx"; import { useQueryEmit } from "@/features/websocket/use-query-emit.ts"; interface PageImportModalProps { spaceId: string; open: boolean; onClose: () => void; } export default function PageImportModal({ spaceId, open, onClose, }: PageImportModalProps) { const { t } = useTranslation(); return ( <> {t("Import pages")} ); } interface ImportFormatSelection { spaceId: string; onClose: () => void; } function ImportFormatSelection({ spaceId, onClose }: ImportFormatSelection) { const { t } = useTranslation(); const [treeData, setTreeData] = useAtom(treeDataAtom); const [workspace] = useAtom(workspaceAtom); const [fileTaskId, setFileTaskId] = useState(null); const emit = useQueryEmit(); const canUseConfluence = isCloud() || workspace?.hasLicenseKey; const handleZipUpload = async (selectedFile: File, source: string) => { if (!selectedFile) { return; } try { onClose(); notifications.show({ id: "import", message: t("Uploading import file"), loading: true, withCloseButton: false, autoClose: false, }); const importTask = await importZip(selectedFile, spaceId, source); notifications.update({ id: "import", title: t("Importing pages"), message: t("Page import is in progress."), loading: true, withCloseButton: false, autoClose: false, }); setFileTaskId(importTask.id); onClose(); } catch (err) { console.log("Failed to upload import file", err); notifications.update({ id: "import", color: "red", title: t("Failed to upload import file"), message: err?.response.data.message, icon: , loading: false, withCloseButton: true, autoClose: 5000, }); } }; useEffect(() => { if (!fileTaskId) return; const intervalId = setInterval(async () => { try { const fileTask = await getFileTaskById(fileTaskId); const status = fileTask.status; if (status === "success") { notifications.update({ id: "import", color: "teal", title: t("Import complete"), message: t("Your pages were successfully imported."), icon: , loading: false, withCloseButton: true, autoClose: 5000, }); clearInterval(intervalId); setFileTaskId(null); await queryClient.refetchQueries({ queryKey: ["root-sidebar-pages", fileTask.spaceId], }); setTimeout(() => { emit({ operation: "refetchRootTreeNodeEvent", spaceId: spaceId, }); }, 50); } if (status === "failed") { notifications.update({ id: "import", color: "red", title: t("Page import failed"), message: t( "Something went wrong while importing pages: {{reason}}.", { reason: fileTask.errorMessage, }, ), icon: , loading: false, withCloseButton: true, autoClose: 5000, }); clearInterval(intervalId); setFileTaskId(null); console.error(fileTask.errorMessage); } } catch (err) { notifications.update({ id: "import", color: "red", title: t("Import failed"), message: t( "Something went wrong while importing pages: {{reason}}.", { reason: err.response?.data.message, }, ), icon: , loading: false, withCloseButton: true, autoClose: 5000, }); clearInterval(intervalId); setFileTaskId(null); console.error("Failed to fetch import status", err); } }, 3000); }, [fileTaskId]); const handleFileUpload = async (selectedFiles: File[]) => { if (!selectedFiles) { return; } onClose(); const alert = notifications.show({ title: t("Importing pages"), message: t("Page import is in progress. Please do not close this tab."), loading: true, autoClose: false, }); const pages: IPage[] = []; let pageCount = 0; for (const file of selectedFiles) { try { const page = await importPage(file, spaceId); pages.push(page); pageCount += 1; } catch (err) { console.log("Failed to import page", err); } } if (pages?.length > 0 && pageCount > 0) { const newTreeNodes = buildTree(pages); const fullTree = treeData.concat(newTreeNodes); if (newTreeNodes?.length && fullTree?.length > 0) { setTreeData(fullTree); } const pageCountText = pageCount === 1 ? `1 ${t("page")}` : `${pageCount} ${t("pages")}`; notifications.update({ id: alert, color: "teal", title: `${t("Successfully imported")} ${pageCountText}`, message: t("Your import is complete."), icon: , loading: false, autoClose: 5000, }); } else { notifications.update({ id: alert, color: "red", title: t("Failed to import pages"), message: t("Unable to import pages. Please try again."), icon: , loading: false, autoClose: 5000, }); } }; // @ts-ignore return ( <> {(props) => ( )} {(props) => ( )} handleZipUpload(file, "notion")} accept="application/zip" > {(props) => ( )} handleZipUpload(file, "confluence")} accept="application/zip" > {(props) => ( )}
Import zip file {t( `Upload zip file containing Markdown and HTML files. Max: {{sizeLimit}}`, { sizeLimit: formatBytes(getFileImportSizeLimit()), }, )} handleZipUpload(file, "generic")} accept="application/zip" > {(props) => ( )}
); }