"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={(
{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={(
add data
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)}>
))}
); })}
Create a new dataset?
Please provide a name for the dataset being created.
{newDatasetError && {newDatasetError}}
closeNewDatasetModal()}>cancel create
Delete {datasetToRemove?.name} dataset?
Are you sure you want to delete {datasetToRemove?.name}? This action cannot be undone.
cancel delete
Delete {dataToRemove?.name} data?
Are you sure you want to delete {dataToRemove?.name}? This action cannot be undone.
cancel delete
); }