+
{(data && typeof window !== "undefined") ? (
+
+
+
+
+
+
+
+
back
+
+
+
+
Account
+
Manage your account's settings.
+
{account.name}
+
+
+
Plan
+
You are using open-source version. Subscribe to get access to hosted cognee with your data!
+
+
Select a plan
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/cognee-frontend/src/app/account/page.tsx b/cognee-frontend/src/app/account/page.tsx
new file mode 100644
index 000000000..f6323c313
--- /dev/null
+++ b/cognee-frontend/src/app/account/page.tsx
@@ -0,0 +1 @@
+export { default } from "./Account";
diff --git a/cognee-frontend/src/app/dashboard/AddDataToCognee.tsx b/cognee-frontend/src/app/dashboard/AddDataToCognee.tsx
new file mode 100644
index 000000000..4f9a3179e
--- /dev/null
+++ b/cognee-frontend/src/app/dashboard/AddDataToCognee.tsx
@@ -0,0 +1,116 @@
+import { FormEvent, useCallback, useState } from "react";
+import { CloseIcon, PlusIcon } from "@/ui/Icons";
+import { useModal } from "@/ui/elements/Modal";
+import { CTAButton, GhostButton, IconButton, Modal, NeutralButton, Select } from "@/ui/elements";
+
+import addData from "@/modules/ingestion/addData";
+import { Dataset } from "@/modules/ingestion/useDatasets";
+import cognifyDataset from "@/modules/datasets/cognifyDataset";
+
+interface AddDataToCogneeProps {
+ datasets: Dataset[];
+ refreshDatasets: () => void;
+ useCloud?: boolean;
+}
+
+export default function AddDataToCognee({ datasets, refreshDatasets, useCloud = false }: AddDataToCogneeProps) {
+ const [filesForUpload, setFilesForUpload] = useState
(null);
+
+ const prepareFiles = useCallback((event: FormEvent) => {
+ const formElements = event.currentTarget;
+ const files = formElements.files;
+
+ setFilesForUpload(files);
+ }, []);
+
+ const processDataWithCognee = useCallback((state: object, event?: FormEvent) => {
+ event!.preventDefault();
+
+ if (!filesForUpload) {
+ return;
+ }
+
+ const formElements = event!.currentTarget;
+ const datasetId = formElements.datasetName.value;
+
+ return addData(
+ datasetId ? {
+ id: datasetId,
+ } : {
+ name: "main_dataset",
+ },
+ Array.from(filesForUpload),
+ useCloud
+ )
+ .then(({ dataset_id, dataset_name }) => {
+ refreshDatasets();
+ setFilesForUpload(null);
+
+ return cognifyDataset({
+ id: dataset_id,
+ name: dataset_name,
+ data: [], // not important, just to mimick Dataset
+ status: "", // not important, just to mimick Dataset
+ }, useCloud);
+ });
+ }, [filesForUpload, refreshDatasets, useCloud]);
+
+ const {
+ isModalOpen: isAddDataModalOpen,
+ openModal: openAddDataModal,
+ closeModal: closeAddDataModal,
+ isActionLoading: isProcessingDataWithCognee,
+ confirmAction: submitDataToCognee,
+ } = useModal(false, processDataWithCognee);
+
+ return (
+ <>
+
+
+ Add data to cognee
+
+
+
+
+
+ Add new data to a dataset?
+
+
+
Please select a dataset to add data in.
If you don't have any, don't worry, we will create one for you.
+
+
+
+ >
+ );
+}
diff --git a/cognee-frontend/src/app/dashboard/CogneeInstancesAccordion.tsx b/cognee-frontend/src/app/dashboard/CogneeInstancesAccordion.tsx
new file mode 100644
index 000000000..037c9e828
--- /dev/null
+++ b/cognee-frontend/src/app/dashboard/CogneeInstancesAccordion.tsx
@@ -0,0 +1,31 @@
+"use client";
+
+import { useBoolean } from "@/utils";
+import { Accordion } from "@/ui/elements";
+
+interface CogneeInstancesAccordionProps {
+ children: React.ReactNode;
+}
+
+export default function CogneeInstancesAccordion({
+ children,
+}: CogneeInstancesAccordionProps) {
+ const {
+ value: isInstancesPanelOpen,
+ setTrue: openInstancesPanel,
+ setFalse: closeInstancesPanel,
+ } = useBoolean(true);
+
+ return (
+ <>
+ Cognee Instances}
+ isOpen={isInstancesPanelOpen}
+ openAccordion={openInstancesPanel}
+ closeAccordion={closeInstancesPanel}
+ >
+ {children}
+
+ >
+ );
+}
diff --git a/cognee-frontend/src/app/dashboard/Dashboard.tsx b/cognee-frontend/src/app/dashboard/Dashboard.tsx
new file mode 100644
index 000000000..97576f3b4
--- /dev/null
+++ b/cognee-frontend/src/app/dashboard/Dashboard.tsx
@@ -0,0 +1,169 @@
+"use client";
+
+import { useCallback, useEffect, useRef, useState } from "react";
+
+import { Header } from "@/ui/Layout";
+import { SearchIcon } from "@/ui/Icons";
+import { Notebook } from "@/ui/elements";
+import { fetch, isCloudEnvironment } from "@/utils";
+import { Notebook as NotebookType } from "@/ui/elements/Notebook/types";
+import { useAuthenticatedUser } from "@/modules/auth";
+import { Dataset } from "@/modules/ingestion/useDatasets";
+import useNotebooks from "@/modules/notebooks/useNotebooks";
+
+import AddDataToCognee from "./AddDataToCognee";
+import NotebooksAccordion from "./NotebooksAccordion";
+import CogneeInstancesAccordion from "./CogneeInstancesAccordion";
+import InstanceDatasetsAccordion from "./InstanceDatasetsAccordion";
+
+interface DashboardProps {
+ user?: {
+ id: string;
+ name: string;
+ email: string;
+ picture: string;
+ };
+ accessToken: string;
+}
+
+export default function Dashboard({ accessToken }: DashboardProps) {
+ fetch.setAccessToken(accessToken);
+ const { user } = useAuthenticatedUser();
+
+ const {
+ notebooks,
+ refreshNotebooks,
+ runCell,
+ addNotebook,
+ updateNotebook,
+ saveNotebook,
+ removeNotebook,
+ } = useNotebooks();
+
+ useEffect(() => {
+ if (!notebooks.length) {
+ refreshNotebooks()
+ .then((notebooks) => {
+ if (notebooks[0]) {
+ setSelectedNotebookId(notebooks[0].id);
+ }
+ });
+ }
+ }, [notebooks.length, refreshNotebooks]);
+
+ const [selectedNotebookId, setSelectedNotebookId] = useState(null);
+
+ const handleNotebookRemove = useCallback((notebookId: string) => {
+ setSelectedNotebookId((currentSelectedNotebookId) => (
+ currentSelectedNotebookId === notebookId ? null : currentSelectedNotebookId
+ ));
+ return removeNotebook(notebookId);
+ }, [removeNotebook]);
+
+ const saveNotebookTimeoutRef = useRef(null);
+ const saveNotebookThrottled = useCallback((notebook: NotebookType) => {
+ const throttleTime = 1000;
+
+ if (saveNotebookTimeoutRef.current) {
+ clearTimeout(saveNotebookTimeoutRef.current);
+ saveNotebookTimeoutRef.current = null;
+ }
+
+ saveNotebookTimeoutRef.current = setTimeout(() => {
+ saveNotebook(notebook);
+ }, throttleTime) as unknown as number;
+ }, [saveNotebook]);
+
+ useEffect(() => {
+ return () => {
+ if (saveNotebookTimeoutRef.current) {
+ clearTimeout(saveNotebookTimeoutRef.current);
+ saveNotebookTimeoutRef.current = null;
+ }
+ };
+ }, []);
+
+ const handleNotebookUpdate = useCallback((notebook: NotebookType) => {
+ updateNotebook(notebook);
+ saveNotebookThrottled(notebook);
+ }, [saveNotebookThrottled, updateNotebook]);
+
+ const selectedNotebook = notebooks.find((notebook) => notebook.id === selectedNotebookId);
+
+ // ############################
+ // Datasets logic
+
+ const [datasets, setDatasets] = useState([]);
+ const refreshDatasetsRef = useRef(() => {});
+
+ const handleDatasetsChange = useCallback((payload: { datasets: Dataset[], refreshDatasets: () => void }) => {
+ const {
+ datasets,
+ refreshDatasets,
+ } = payload;
+
+ refreshDatasetsRef.current = refreshDatasets;
+ setDatasets(datasets);
+ }, []);
+
+ const isCloudEnv = isCloudEnvironment();
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {selectedNotebook && (
+
+ )}
+
+
+
+ );
+}
diff --git a/cognee-frontend/src/app/dashboard/DatasetsAccordion.tsx b/cognee-frontend/src/app/dashboard/DatasetsAccordion.tsx
new file mode 100644
index 000000000..0c764ef92
--- /dev/null
+++ b/cognee-frontend/src/app/dashboard/DatasetsAccordion.tsx
@@ -0,0 +1,346 @@
+"use client";
+
+import { ChangeEvent, useCallback, useEffect, useState } from "react";
+import { useBoolean } from "@/utils";
+import { Accordion, CTAButton, GhostButton, IconButton, Input, Modal, PopupMenu } from "@/ui/elements";
+import { AccordionProps } from "@/ui/elements/Accordion";
+import { CloseIcon, DatasetIcon, MinusIcon, PlusIcon } from "@/ui/Icons";
+import useDatasets, { Dataset } from "@/modules/ingestion/useDatasets";
+import addData from "@/modules/ingestion/addData";
+import cognifyDataset from "@/modules/datasets/cognifyDataset";
+import { DataFile } from "@/modules/ingestion/useData";
+import { LoadingIndicator } from "@/ui/App";
+
+interface DatasetsChangePayload {
+ datasets: Dataset[]
+ refreshDatasets: () => void;
+}
+
+export interface DatasetsAccordionProps extends Omit {
+ onDatasetsChange?: (payload: DatasetsChangePayload) => void;
+ useCloud?: boolean;
+}
+
+export default function DatasetsAccordion({
+ title,
+ tools,
+ switchCaretPosition = false,
+ className,
+ contentClassName,
+ onDatasetsChange,
+ useCloud = false,
+}: DatasetsAccordionProps) {
+ const {
+ value: isDatasetsPanelOpen,
+ setTrue: openDatasetsPanel,
+ setFalse: closeDatasetsPanel,
+ } = useBoolean(true);
+
+ const {
+ datasets,
+ refreshDatasets,
+ addDataset,
+ removeDataset,
+ getDatasetData,
+ removeDatasetData,
+ } = useDatasets(useCloud);
+
+ useEffect(() => {
+ if (datasets.length === 0) {
+ refreshDatasets();
+ }
+ }, [datasets.length, refreshDatasets]);
+
+ const [openDatasets, openDataset] = useState>(new Set());
+
+ const toggleDataset = (id: string) => {
+ openDataset((prev) => {
+ const newState = new Set(prev);
+
+ if (newState.has(id)) {
+ newState.delete(id)
+ } else {
+ getDatasetData(id)
+ .then(() => {
+ newState.add(id);
+ });
+ }
+
+ return newState;
+ });
+ };
+
+ const refreshOpenDatasetsData = useCallback(() => {
+ return Promise.all(
+ openDatasets.values().map(
+ (datasetId) => getDatasetData(datasetId)
+ )
+ );
+ }, [getDatasetData, openDatasets]);
+
+ const refreshDatasetsAndData = useCallback(() => {
+ refreshDatasets()
+ .then(refreshOpenDatasetsData);
+ }, [refreshDatasets, refreshOpenDatasetsData]);
+
+ useEffect(() => {
+ onDatasetsChange?.({
+ datasets,
+ refreshDatasets: refreshDatasetsAndData,
+ });
+ }, [datasets, onDatasetsChange, refreshDatasets, refreshDatasetsAndData]);
+
+ const {
+ value: isNewDatasetModalOpen,
+ setTrue: openNewDatasetModal,
+ setFalse: closeNewDatasetModal,
+ } = useBoolean(false);
+
+ const handleDatasetAdd = () => {
+ openNewDatasetModal();
+ };
+
+ const [newDatasetError, setNewDatasetError] = useState("");
+
+ const handleNewDatasetSubmit = (event: React.FormEvent) => {
+ event.preventDefault();
+ setNewDatasetError("");
+
+ const formElements = event.currentTarget;
+
+ const datasetName = formElements.datasetName.value;
+
+ if (datasetName.trim().length === 0) {
+ setNewDatasetError("Dataset name cannot be empty.");
+ return;
+ }
+
+ if (datasetName.includes(" ") || datasetName.includes(".")) {
+ setNewDatasetError("Dataset name cannot contain spaces or periods.");
+ return;
+ }
+
+ addDataset(datasetName)
+ .then(() => {
+ closeNewDatasetModal();
+ refreshDatasetsAndData();
+ });
+ };
+
+ const {
+ value: isRemoveDatasetModalOpen,
+ setTrue: openRemoveDatasetModal,
+ setFalse: closeRemoveDatasetModal,
+ } = useBoolean(false);
+
+ const [datasetToRemove, setDatasetToRemove] = useState(null);
+
+ const handleDatasetRemove = (dataset: Dataset) => {
+ setDatasetToRemove(dataset);
+ openRemoveDatasetModal();
+ };
+
+ const handleDatasetRemoveCancel = () => {
+ setDatasetToRemove(null);
+ closeRemoveDatasetModal();
+ };
+
+ const handleRemoveDatasetConfirm = (event: React.FormEvent) => {
+ event.preventDefault();
+
+ if (datasetToRemove) {
+ removeDataset(datasetToRemove.id)
+ .then(() => {
+ closeRemoveDatasetModal();
+ setDatasetToRemove(null);
+ refreshDatasetsAndData();
+ });
+ }
+ };
+
+ const [datasetInProcessing, setProcessingDataset] = useState(null);
+
+ const handleAddFiles = (dataset: Dataset, event: ChangeEvent) => {
+ event.stopPropagation();
+
+ if (datasetInProcessing) {
+ return;
+ }
+
+ setProcessingDataset(dataset);
+
+ if (!event.target.files) {
+ return;
+ }
+
+ const files: File[] = Array.from(event.target.files);
+
+ if (!files.length) {
+ return;
+ }
+
+ return addData(dataset, files, useCloud)
+ .then(async () => {
+ await getDatasetData(dataset.id);
+
+ return cognifyDataset(dataset, useCloud)
+ .finally(() => {
+ setProcessingDataset(null);
+ });
+ });
+ };
+
+ const [dataToRemove, setDataToRemove] = useState(null);
+ const {
+ value: isRemoveDataModalOpen,
+ setTrue: openRemoveDataModal,
+ setFalse: closeRemoveDataModal,
+ } = useBoolean(false);
+
+ const handleDataRemove = (data: DataFile) => {
+ setDataToRemove(data);
+
+ openRemoveDataModal();
+ };
+ const handleDataRemoveCancel = () => {
+ setDataToRemove(null);
+ closeRemoveDataModal();
+ };
+ const handleDataRemoveConfirm = (event: React.FormEvent) => {
+ event.preventDefault();
+
+ if (dataToRemove) {
+ removeDatasetData(dataToRemove.datasetId, dataToRemove.id)
+ .then(() => {
+ closeRemoveDataModal();
+ setDataToRemove(null);
+ refreshDatasetsAndData();
+ });
+ }
+ }
+
+ return (
+ <>
+ Datasets}
+ isOpen={isDatasetsPanelOpen}
+ openAccordion={openDatasetsPanel}
+ closeAccordion={closeDatasetsPanel}
+ tools={(
+
+ )}
+ switchCaretPosition={switchCaretPosition}
+ className={className}
+ contentClassName={contentClassName}
+ >
+
+ {datasets.length === 0 && (
+
+ No datasets here, add one by clicking +
+
+ )}
+ {datasets.map((dataset) => {
+ return (
+
+ {datasetInProcessing?.id == dataset.id ? : }
+ {dataset.name}
+
+ )}
+ isOpen={openDatasets.has(dataset.id)}
+ openAccordion={() => toggleDataset(dataset.id)}
+ closeAccordion={() => toggleDataset(dataset.id)}
+ tools={(
+
+
+
+
+
handleDatasetRemove(dataset)} className="hover:bg-gray-100 w-full text-left px-2 cursor-pointer">delete
+
+
+
+ )}
+ className="first:pt-1.5"
+ switchCaretPosition={true}
+ >
+ <>
+ {dataset.data?.length === 0 && (
+
+ No data in a dataset, add by clicking "add data" in a dropdown menu
+
+ )}
+ {dataset.data?.map((data) => (
+
+
{data.name}
+
+ handleDataRemove(data)}>
+
+
+ ))}
+ >
+
+ );
+ })}
+
+
+
+