Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 99 additions & 77 deletions DashAI/front/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import RAGPromptsPage from "./pages/generative/RAG/RAGPromptsPage";
import RAGSessionPage from "./pages/generative/RAGSession/RAGSessionPage";
import SessionRouter from "./pages/generative/SessionRouter";
import { DatasetsAndNotebooksProvider } from "./components/custom/contexts/DatasetsAndNotebooksContext";
import { DatasetsProvider } from "./contexts/DatasetsContext";
import { ModelsProvider } from "./components/models/ModelsContext";

function DataSectionLayout() {
return (
Expand All @@ -44,83 +46,103 @@ function App() {
<BrowserRouter
future={{ v7_relativeSplatPath: true, v7_startTransition: true }}
>
<ResponsiveAppBar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/app" element={<Home />} />
<Route path="/app/data" element={<DataSectionLayout />}>
<Route index element={<DatasetsPage />} />
<Route path="datasets/new" element={<DatasetsPage />} />
<Route
path="datasets/new/:dataloaderName"
element={<DatasetsPage />}
/>
<Route path="datasets/:id" element={<DatasetsPage />} />
<Route path="notebooks/new" element={<DatasetsPage />} />
<Route path="notebooks/:id" element={<DatasetsPage />} />
<Route path="hub" element={<HubContent />} />
<Route
path="hub/import/:datafileId/*"
element={<HubImportPage />}
/>
<Route path="hub/:sourceName" element={<HubContent />} />
</Route>
<Route path="/app/models" element={<ModelsPage />} />
<Route path="/app/models/datasets/:id" element={<ModelsPage />} />
<Route path="/app/models/sessions/:id" element={<ModelsPage />} />
<Route
path="/app/models/sessions/new/:taskName"
element={<ModelsPage />}
/>
<Route
path="/app/models/sessions/:id/model/:runId"
element={<ModelsPage />}
/>
<Route path="/app/generative" element={<Generative />} />
<Route
path="/app/generative/RAG"
element={<Navigate to="/app/generative/sessions/new" replace />}
/>
<Route
path="/app/generative/RAG/documents"
element={
<GenerativeProvider>
<RAGDocumentsPage />
</GenerativeProvider>
}
/>
<Route
path="/app/generative/RAG/prompts"
element={
<GenerativeProvider>
<RAGPromptsPage />
</GenerativeProvider>
}
/>
<Route path="/app/generative/sessions/new" element={<Generative />} />
<Route
path="/app/generative/sessions/new/:modelName"
element={<Generative />}
/>
<Route
path="/app/generative/sessions/:id"
element={<SessionRouter />}
/>
<Route path="/app/pipelines" element={<PipelinesPage />} />
<Route path="/app/pipelines/new" element={<NewPipelineWrapper />} />
<Route
path="/app/pipelines/:pipelineId"
element={<NewPipelineWrapper />}
/>
<Route path="/app/plugins">
<Route index element={<PluginsPage />} />
<Route path=":category">
<Route index element={<PluginsPage />} />
<Route path="details/:id" element={<PluginsDetails />} />
</Route>
</Route>
</Routes>
<JobQueueWidget />
<DatasetsProvider>
<ModelsProvider>
<GenerativeProvider>
<ResponsiveAppBar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/app" element={<Home />} />
<Route path="/app/data" element={<DataSectionLayout />}>
<Route index element={<DatasetsPage />} />
<Route path="datasets/new" element={<DatasetsPage />} />
<Route
path="datasets/new/:dataloaderName"
element={<DatasetsPage />}
/>
<Route path="datasets/:id" element={<DatasetsPage />} />
<Route path="notebooks/new" element={<DatasetsPage />} />
<Route path="notebooks/:id" element={<DatasetsPage />} />
<Route path="hub" element={<HubContent />} />
<Route
path="hub/import/:datafileId/*"
element={<HubImportPage />}
/>
<Route path="hub/:sourceName" element={<HubContent />} />
</Route>
<Route path="/app/models" element={<ModelsPage />} />
<Route
path="/app/models/datasets/:id"
element={<ModelsPage />}
/>
<Route
path="/app/models/sessions/:id"
element={<ModelsPage />}
/>
<Route
path="/app/models/sessions/new/:taskName"
element={<ModelsPage />}
/>
<Route
path="/app/models/sessions/:id/model/:runId"
element={<ModelsPage />}
/>
<Route path="/app/generative" element={<Generative />} />
<Route
path="/app/generative/RAG"
element={
<Navigate to="/app/generative/sessions/new" replace />
}
/>
<Route
path="/app/generative/RAG/documents"
element={
<GenerativeProvider>
<RAGDocumentsPage />
</GenerativeProvider>
}
/>
<Route
path="/app/generative/RAG/prompts"
element={
<GenerativeProvider>
<RAGPromptsPage />
</GenerativeProvider>
}
/>
<Route
path="/app/generative/sessions/new"
element={<Generative />}
/>
<Route
path="/app/generative/sessions/new/:modelName"
element={<Generative />}
/>
<Route
path="/app/generative/sessions/:id"
element={<SessionRouter />}
/>
<Route path="/app/pipelines" element={<PipelinesPage />} />
<Route
path="/app/pipelines/new"
element={<NewPipelineWrapper />}
/>
<Route
path="/app/pipelines/:pipelineId"
element={<NewPipelineWrapper />}
/>
<Route path="/app/plugins">
<Route index element={<PluginsPage />} />
<Route path=":category">
<Route index element={<PluginsPage />} />
<Route path="details/:id" element={<PluginsDetails />} />
</Route>
</Route>
</Routes>
<JobQueueWidget />
</GenerativeProvider>
</ModelsProvider>
</DatasetsProvider>
</BrowserRouter>
</TourRegistryProvider>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { createContext, useContext, useEffect, useState } from "react";

import { useTranslation } from "react-i18next";
import { useDatasets } from "../../../hooks/datasets/useDatasets";
import { useFolders } from "../../../hooks/datasets/useFolders";
import { useSharedDatasets } from "../../../contexts/DatasetsContext";
import { useNotebooks } from "../../../hooks/datasets/useNotebooks";
import { useDownloads } from "../../../hooks/datasets/useDownloads";

Expand Down Expand Up @@ -34,29 +33,14 @@ export const DatasetsAndNotebooksProvider = ({ children }) => {
addDatasetOptimistically,
replaceDatasets,
startDatasetPolling,
} = useDatasets({ t });

const {
folders,
fetchFolders,
createFolder,
renameFolder,
deleteFolderById: deleteFolderByIdRaw,
} = useFolders({ t });

// Deleting a folder moves its datasets to "no folder" server-side
// (folder_id set to null via the FK's ON DELETE SET NULL), but the local
// `datasets` state still holds the old folder_id until this clears it —
// otherwise those datasets vanish from the list until a full refetch.
const deleteFolderById = async (id) => {
const success = await deleteFolderByIdRaw(id);
if (success) {
replaceDatasets((prev) =>
prev.map((d) => (d.folder_id === id ? { ...d, folder_id: null } : d)),
);
}
return success;
};
deleteFolderById,
openFolderIds,
setOpenFolderIds,
} = useSharedDatasets();

const {
downloads,
Expand Down Expand Up @@ -158,6 +142,8 @@ export const DatasetsAndNotebooksProvider = ({ children }) => {
setScrollToColumn,
uploadDataloader,
setUploadDataloader,
openFolderIds,
setOpenFolderIds,
};

return (
Expand Down
3 changes: 3 additions & 0 deletions DashAI/front/src/components/generative/GenerativeContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export function GenerativeProvider({ children }) {
editSession,
} = useSessions({ t });
const [stepIndex, setStepIndex] = useState(0);
const [openSections, setOpenSections] = useState({});

useEffect(() => {
fetchSessions();
Expand All @@ -54,6 +55,8 @@ export function GenerativeProvider({ children }) {
fetchTasks,
stepIndex,
setStepIndex,
openSections,
setOpenSections,
deleteSessionById,
deleteSessionsByIds,
editSession,
Expand Down
65 changes: 37 additions & 28 deletions DashAI/front/src/components/generative/SessionBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useNavigate } from "react-router-dom";
import FolderIcon from "@mui/icons-material/Folder";
import ViewModuleIcon from "@mui/icons-material/ViewModule";
import SearchBar from "../threeSectionLayout/SearchBar";
import { useEffect, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import InfoSessionModal from "./InfoSessionModal";
import GroupedCollapsibleList from "../threeSectionLayout/GroupedCollapsibleList";
import Footer from "../threeSectionLayout/Footer";
Expand All @@ -31,14 +31,15 @@ export default function SessionBar({
deleteSessionById,
deleteSessionsByIds,
editSession,
openSections,
setOpenSections,
} = useGenerative();

const sessions = sessionsProp ?? sessionsCtx ?? [];
const selectedSessionId = selectedSessionIdProp ?? selectedSessionIdCtx;
const [searchQuery, setSearchQuery] = useState("");
const [filteredSessions, setFilteredSessions] = useState(sessions);
const [selectedInfoSession, setSelectedInfoSession] = useState(null);
const [openSections, setOpenSections] = useState({});
const { t } = useTranslation(["generative", "common"]);

const SEARCH_THRESHOLD = 10;
Expand All @@ -48,11 +49,14 @@ export default function SessionBar({
}, [sessions.length]);

// Create a map of task_name to display_name for quick lookup
const taskDisplayNameMap =
tasks?.reduce((map, task) => {
map[task.name] = task.display_name;
return map;
}, {}) || {};
const taskDisplayNameMap = useMemo(
() =>
tasks?.reduce((map, task) => {
map[task.name] = task.display_name;
return map;
}, {}) || {},
[tasks],
);

useEffect(() => {
// Initialize all sections as closed based on unique task display names
Expand Down Expand Up @@ -158,28 +162,32 @@ export default function SessionBar({
return ok;
};

// Group sessions by task display_name
const groupedSessions = filteredSessions?.reduce((groups, session) => {
// Get the display name from the task using the session's task_name
const displayName =
taskDisplayNameMap[session.task_name] || t("common:other");
// Group sessions by task display_name, sorted to maintain a consistent order.
// Memoized so the object identity stays stable across renders that don't
// change the underlying data — GroupedCollapsibleList uses `groups` as an
// effect dependency, so a fresh literal every render would re-run it.
const sortedGroupedSessions = useMemo(() => {
const groupedSessions = filteredSessions?.reduce((groups, session) => {
// Get the display name from the task using the session's task_name
const displayName =
taskDisplayNameMap[session.task_name] || t("common:other");

if (!groups[displayName]) {
groups[displayName] = [];
}
groups[displayName].push(session);
return groups;
}, {});
if (!groups[displayName]) {
groups[displayName] = [];
}
groups[displayName].push(session);
return groups;
}, {});

// Sort grouped sessions to maintain consistent order
const sortedGroupedSessions = groupedSessions
? Object.keys(groupedSessions)
.sort()
.reduce((sorted, key) => {
sorted[key] = groupedSessions[key];
return sorted;
}, {})
: {};
return groupedSessions
? Object.keys(groupedSessions)
.sort()
.reduce((sorted, key) => {
sorted[key] = groupedSessions[key];
return sorted;
}, {})
: {};
}, [filteredSessions, taskDisplayNameMap, t]);

return (
<SideBar>
Expand Down Expand Up @@ -231,7 +239,8 @@ export default function SessionBar({
onItemInfo={handleSessionInfo}
title={t("common:generative")}
Icon={FolderIcon}
initialOpenGroups={openSections}
openGroups={openSections}
onOpenGroupsChange={setOpenSections}
getItemDescription={(session) => session.model_name}
getDeleteConfirmationContent={getSessionDeleteConfirmationContent}
onBulkDelete={handleBulkSessionDelete}
Expand Down
Loading
Loading