mirror of
https://github.com/docmost/docmost.git
synced 2025-11-17 03:51:09 +10:00
bug fixes and UI
This commit is contained in:
@ -1,18 +1,36 @@
|
||||
import { Modal, Button, SimpleGrid, FileButton } from "@mantine/core";
|
||||
import {
|
||||
Modal,
|
||||
Button,
|
||||
SimpleGrid,
|
||||
FileButton,
|
||||
Group,
|
||||
Text,
|
||||
Tooltip,
|
||||
} from "@mantine/core";
|
||||
import {
|
||||
IconBrandNotion,
|
||||
IconCheck,
|
||||
IconFileCode,
|
||||
IconFileTypeZip,
|
||||
IconMarkdown,
|
||||
IconX,
|
||||
} from "@tabler/icons-react";
|
||||
import { importPage } from "@/features/page/services/page-service.ts";
|
||||
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 from "react";
|
||||
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";
|
||||
|
||||
interface PageImportModalProps {
|
||||
spaceId: string;
|
||||
@ -59,6 +77,113 @@ interface ImportFormatSelection {
|
||||
function ImportFormatSelection({ spaceId, onClose }: ImportFormatSelection) {
|
||||
const { t } = useTranslation();
|
||||
const [treeData, setTreeData] = useAtom(treeDataAtom);
|
||||
const [workspace] = useAtom(workspaceAtom);
|
||||
const [fileTaskId, setFileTaskId] = useState<string | null>(null);
|
||||
|
||||
const canUseConfluence = isCloud() || workspace?.hasLicenseKey;
|
||||
|
||||
const handleZipUpload = async (selectedFile: File, source: string) => {
|
||||
if (!selectedFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
onClose();
|
||||
|
||||
try {
|
||||
const importTask = await importZip(selectedFile, spaceId, source);
|
||||
notifications.show({
|
||||
id: "import",
|
||||
title: t("Importing pages"),
|
||||
message: t(
|
||||
"Page import is in progress. Refresh this tab after a while.",
|
||||
),
|
||||
loading: true,
|
||||
withCloseButton: false,
|
||||
autoClose: false,
|
||||
});
|
||||
|
||||
setFileTaskId(importTask.id);
|
||||
console.log("taskId set", importTask.id);
|
||||
} catch (err) {
|
||||
console.log("Failed to import page", err);
|
||||
notifications.update({
|
||||
id: "import",
|
||||
color: "red",
|
||||
title: t("Failed to import pages"),
|
||||
message: t("Unable to import pages. Please try again."),
|
||||
icon: <IconX size={18} />,
|
||||
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: <IconCheck size={18} />,
|
||||
loading: false,
|
||||
withCloseButton: true,
|
||||
autoClose: 5000,
|
||||
});
|
||||
clearInterval(intervalId);
|
||||
setFileTaskId(null);
|
||||
}
|
||||
|
||||
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: <IconX size={18} />,
|
||||
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: <IconX size={18} />,
|
||||
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) {
|
||||
@ -120,6 +245,7 @@ function ImportFormatSelection({ spaceId, onClose }: ImportFormatSelection) {
|
||||
}
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
return (
|
||||
<>
|
||||
<SimpleGrid cols={2}>
|
||||
@ -148,7 +274,76 @@ function ImportFormatSelection({ spaceId, onClose }: ImportFormatSelection) {
|
||||
</Button>
|
||||
)}
|
||||
</FileButton>
|
||||
|
||||
<FileButton
|
||||
onChange={(file) => handleZipUpload(file, "notion")}
|
||||
accept="application/zip"
|
||||
>
|
||||
{(props) => (
|
||||
<Button
|
||||
justify="start"
|
||||
variant="default"
|
||||
leftSection={<IconBrandNotion size={18} />}
|
||||
{...props}
|
||||
>
|
||||
Notion
|
||||
</Button>
|
||||
)}
|
||||
</FileButton>
|
||||
<FileButton
|
||||
onChange={(file) => handleZipUpload(file, "confluence")}
|
||||
accept="application/zip"
|
||||
>
|
||||
{(props) => (
|
||||
<Tooltip
|
||||
label="Available in enterprise edition"
|
||||
disabled={canUseConfluence}
|
||||
>
|
||||
<Button
|
||||
disabled={!canUseConfluence}
|
||||
justify="start"
|
||||
variant="default"
|
||||
leftSection={<ConfluenceIcon size={18} />}
|
||||
{...props}
|
||||
>
|
||||
Confluence
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</FileButton>
|
||||
</SimpleGrid>
|
||||
|
||||
<Group justify="center" gap="xl" mih={150}>
|
||||
<div>
|
||||
<Text ta="center" size="lg" inline>
|
||||
Import zip file
|
||||
</Text>
|
||||
<Text ta="center" size="sm" c="dimmed" inline py="sm">
|
||||
{t(
|
||||
`Upload zip file containing Markdown and HTML files. Max: {{sizeLimit}}`,
|
||||
{
|
||||
sizeLimit: formatBytes(getFileImportSizeLimit()),
|
||||
},
|
||||
)}
|
||||
</Text>
|
||||
<FileButton
|
||||
onChange={(file) => handleZipUpload(file, "generic")}
|
||||
accept="application/zip"
|
||||
>
|
||||
{(props) => (
|
||||
<Group justify="center">
|
||||
<Button
|
||||
justify="center"
|
||||
leftSection={<IconFileTypeZip size={18} />}
|
||||
{...props}
|
||||
>
|
||||
{t("Upload file")}
|
||||
</Button>
|
||||
</Group>
|
||||
)}
|
||||
</FileButton>
|
||||
</div>
|
||||
</Group>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -7,9 +7,10 @@ import {
|
||||
IPage,
|
||||
IPageInput,
|
||||
SidebarPagesParams,
|
||||
} from "@/features/page/types/page.types";
|
||||
} from '@/features/page/types/page.types';
|
||||
import { IAttachment, IPagination } from "@/lib/types.ts";
|
||||
import { saveAs } from "file-saver";
|
||||
import { IFileTask } from '@/features/file-task/types/file-task.types.ts';
|
||||
|
||||
export async function createPage(data: Partial<IPage>): Promise<IPage> {
|
||||
const req = await api.post<IPage>("/pages/create", data);
|
||||
@ -92,6 +93,25 @@ export async function importPage(file: File, spaceId: string) {
|
||||
return req.data;
|
||||
}
|
||||
|
||||
export async function importZip(
|
||||
file: File,
|
||||
spaceId: string,
|
||||
source?: string,
|
||||
): Promise<IFileTask> {
|
||||
const formData = new FormData();
|
||||
formData.append("spaceId", spaceId);
|
||||
formData.append("source", source);
|
||||
formData.append("file", file);
|
||||
|
||||
const req = await api.post<any>("/pages/import-zip", formData, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
|
||||
return req.data;
|
||||
}
|
||||
|
||||
export async function uploadFile(
|
||||
file: File,
|
||||
pageId: string,
|
||||
|
||||
Reference in New Issue
Block a user