merlyn/frontend/src/components/Modals/LegacySettings/ExportImport/index.jsx
Timothy Carambat 708068a09e
AnythingLLM UI overhaul (#278)
* v2 Login screen (#254)

* adding gradients for modal and sidebar

* adding font setup

* redesigned login screen for MultiUserAuth

* completed multi user mode login screen

* linting

* login screen for single user auth redesign complete

* created reusable gradient for login screen

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>

* v2 sidebar (#262)

* adding gradients for modal and sidebar

* adding font setup

* redesigned login screen for MultiUserAuth

* completed multi user mode login screen

* linting

* login screen for single user auth redesign complete

* WIP sidebar redesign

* created reusable gradient for login screen

* remove dark mode items

* update new workspace button

* completed sidebar for desktop view

* add interactivity states

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>

* remove duplicated pkg

* v2 settings (#264)

* adding gradients for modal and sidebar

* adding font setup

* redesigned login screen for MultiUserAuth

* completed multi user mode login screen

* linting

* login screen for single user auth redesign complete

* WIP sidebar redesign

* created reusable gradient for login screen

* remove dark mode items

* update new workspace button

* completed sidebar for desktop view

* WIP added colors/gradients to admin settings

* WIP fix discord logo import

* WIP settings redesign - added routes for general settings and restyled components

* WIP settings for LLM Preference, VectorDB, ExportImport

* settings menu UI complete WIP functionality

* settings fully functional/removed dark mode logo

* linting

* removing unneeded dependency

* Fix admin sidebar visibility
Fix API Keys location and work with single/mum
Fix Appearance location - WIP on funcitonality

* update api key page

* fix permissions for appearance

* Single user mode fixes

* fix multi user mode enabled

* fix import export

* Rename AdminSidebar to SettingsSidebar

* Fix mobile sidebar links

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>

* V2 user logout (#265)

* Add user logout button

* hide other 3 dot button

* wrap admin routes

* V2 workspace modal (#267)

Update new workspace modal
remove duplicate tailwind colors

* v2 Settings modal styles (#266)

* EditUserModal styles complete

* workspaces modals styles complete

* create invite link modal styles complete

* create new api key modal styles complete

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>

* v2 Chats Redesign (#270)

* fix default message for new workspace

* prompt input box ui redesign complete

* ui tweak to prompt input

* WIP chat msg redesign

* chat container and historical chat messages redesign

* manage workspace modal appears when clicking upload a document on empty workspace

* fixed loading skeleton styles

* citations redesign complete

* restyle pending chat and prompt reply components

* default chat messages styles updated

* linting

* update how chats are returned

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>

* Onboarding modal flow for first time setup (#274)

* WIP onboarding modal flow

* onboarding flow complete and private route redirection for onboarding setep

* redirect to home on onboarding complete

* add onboarding redirect using paths.onboarding()

* Apply changes to auth flow, onboarding determination, and flows

* remove formref

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>

* v2 document picker (#275)

* remove unneeded comments

* WIP document picker UI

* WIP basic UI complete for document picker tab and settings tab

* linting

* settings menu complete, document row WIP

* WIP document picker loading from localFiles

* WIP file picker logic

* refactoring document picker to work with backend

* WIP refactoring document picker

* WIP refactor document picker to work with backend

* file uploading with dropzone working

* WIP deleting file when not embedded

* WIP embeddings

* WIP embedding with temp button and hardcoded paths

* WIP placeholder for WorkspaceDirectory component

* WIP WorkspaceDirectory

* WIP

* sort workspaceDocs and availibleDocs complete

* added directories util

* add and remove document from ws working

* v2 document picker complete

* reference modal ui bug fixes

* truncate function bug fix

* ManageWorkspace modal bug fixes

* blocking mobile users modal for workspace settings

* mobile ui fixes

* linting

* ui padding fixes

* citation bug fixes

* code review changes

* debounce handlers

* change tempFile object to array

* selection count fix

* Convert workspace modal to div
Memo workspace settings
update conditional rendering of workspace settings

* Show no documents

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>

* mobile sidebar styles

* padding on Mobile view
mobile sidebar items

* UI touchup

* suggestion implementations

* CSS fixes and animation perfomance change to GPU accelerated and 60fps

* change will-change

* remove transitions from onboarding modals, simplify on-change handlers

* Swap onboarding to memoized components and debounce onchange handlers

* remove console log

* remove Avenir font

---------

Co-authored-by: Sean Hatfield <seanhatfield5@gmail.com>
2023-10-23 13:10:34 -07:00

217 lines
7.0 KiB
JavaScript

import React, { useState, useEffect, useRef } from "react";
import { AlertCircle, CheckCircle, Download, Loader, X } from "react-feather";
import System from "../../../../models/system";
import { API_BASE } from "../../../../utils/constants";
import paths from "../../../../utils/paths";
const noop = () => false;
export default function ExportOrImportData({ hideModal = noop }) {
return (
<div className="relative w-full w-full max-h-full">
<div className="relative bg-white rounded-lg shadow dark:bg-stone-700">
<div className="flex flex-col items-start justify-between px-6 py-4">
<p className="text-gray-800 dark:text-stone-200 text-base ">
Have multiple AnythingLLM instances or simply want to backup or
re-import data from another instance? You can do so here.
<br />
<i>
This will not automatically sync your vector database embeddings!
</i>
</p>
<a
className="text-gray-400 dark:text-stone-500 my-2 text-xs"
href={paths.exports()}
target="_blank"
>
View previous exports &rarr;
</a>
</div>
<div className="px-6 pb-6 space-y-6 flex h-full w-full">
<div className="flex flex-col w-full gap-y-2">
<ExportData />
<div className="h-[1px] bg-slate-400 dark:bg-stone-600 w-full my-2" />
<ImportData />
</div>
</div>
<div className="flex items-center p-6 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600">
<button
onClick={hideModal}
type="button"
className="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"
>
Close
</button>
</div>
</div>
</div>
);
}
function ExportData() {
const [loading, setLoading] = useState(false);
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
const exportData = async function () {
setLoading(true);
const { filename, error } = await System.dataExport();
setLoading(false);
if (!filename) {
setError(error);
} else {
setResult(filename);
const link = document.createElement("a");
link.href = `${API_BASE}/system/data-exports/${filename}`;
link.target = "_blank";
document.body.appendChild(link);
link.click();
}
};
if (loading) {
return (
<div className="w-full flex flex-col gap-y-1 items-center px-6 py-4 border border-gray-200 rounded-lg dark:border-gray-600 bg-slate-200 group animate-pulse">
<p className="text-gray-800 text-lg">Exporting....</p>
<p className="text-gray-800 text-sm italic">
A download will start automatically.
</p>
</div>
);
}
if (error) {
return (
<button
type="button"
onClick={() => setError(null)}
className="w-full flex flex-col gap-y-1 items-center px-6 py-4 border border-red-200 rounded-lg dark:border-red-600 bg-red-200 group"
>
<p className="text-red-800 text-sm">{error}</p>
</button>
);
}
if (!!result) {
return (
<a
target="_blank"
href={`${API_BASE}/system/data-exports/${result}`}
className="w-full flex gap-1 justify-center items-center px-6 py-4 border border-green-200 rounded-lg dark:border-green-600 bg-green-200 group"
>
<Download className="h-4 w-4 text-green-800 " />
<p className="text-green-800 text-sm">Download Data Export</p>
</a>
);
}
return (
<button
onClick={exportData}
type="button"
className="w-full flex justify-center px-6 py-4 border border-gray-200 rounded-lg dark:border-gray-600 hover:bg-slate-200 group"
>
<p className="text-gray-800 dark:text-stone-200 group-hover:text-gray-800 text-lg">
Export AnythingLLM data
</p>
</button>
);
}
function ImportData() {
const inputRef = useRef(null);
const [loading, setLoading] = useState(false);
const [file, setFile] = useState(null);
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
const startInput = () => inputRef?.current?.click();
const handleUpload = async (e) => {
e.preventDefault();
setError(null);
setFile(null);
setResult(null);
const file = e.target.files?.[0];
if (!file) {
setError("Invalid file upload");
return false;
}
setFile(file);
setLoading(true);
const formData = new FormData();
formData.append("file", file, file.name);
const { success, error } = await System.importData(formData);
if (!success) {
setError(error);
} else {
setResult(true);
}
setLoading(false);
setFile(null);
};
if (loading) {
return (
<div className="w-full flex flex-col gap-y-1 items-center px-6 py-4 border border-gray-200 rounded-lg dark:border-gray-600 bg-slate-200 group animate-pulse">
<p className="text-gray-800 text-lg">Importing....</p>
<p className="text-gray-800 text-sm italic">{file.name}</p>
</div>
);
}
if (error) {
return (
<button
type="button"
onClick={() => setError(null)}
className="w-full flex flex-col gap-y-1 items-center px-6 py-4 border border-red-200 rounded-lg dark:border-red-600 bg-red-200 group"
>
<p className="text-red-800 text-sm">{error}</p>
</button>
);
}
if (!!result) {
return (
<div className="w-full flex flex-col gap-y-1 gap-1 justify-center items-center px-6 py-4 border border-green-200 rounded-lg dark:border-green-600 bg-green-200 group">
<div className="flex items-center gap-x-1">
<CheckCircle className="h-4 w-4 text-green-800 " />
<p className="text-green-800 text-sm">
Import was completed successfully
</p>
</div>
<p className="text-green-800 text-xs italic">
please reload the page to see the results of the import.
</p>
</div>
);
}
return (
<>
<input
ref={inputRef}
onChange={handleUpload}
name="import"
type="file"
multiple="false"
accept=".zip"
hidden={true}
/>
<button
type="button"
onClick={startInput}
className="w-full flex flex-col gap-y-1 items-center px-6 py-4 border border-gray-200 rounded-lg dark:border-gray-600 hover:bg-slate-200 group"
>
<p className="text-gray-800 dark:text-stone-200 group-hover:text-gray-800 text-lg">
Import AnythingLLM data
</p>
<p className="text-gray-800 dark:text-stone-200 group-hover:text-gray-800 text-xs italic">
this must be an export from an AnythingLLM instance.
</p>
</button>
</>
);
}