From 133bdc4b7237158bf3d36158bc7de47a283773db Mon Sep 17 00:00:00 2001 From: Ayush8923 <80516839+Ayush8923@users.noreply.github.com> Date: Fri, 27 Mar 2026 22:49:15 +0530 Subject: [PATCH 1/9] feat(*): change grouping name routes->main --- app/{(routes) => (main)}/coming-soon/guardrails/page.tsx | 0 app/{(routes) => (main)}/coming-soon/model-testing/page.tsx | 0 app/{(routes) => (main)}/coming-soon/redteaming/page.tsx | 0 app/{(routes) => (main)}/coming-soon/text-to-speech/page.tsx | 0 app/{(routes) => (main)}/configurations/page.tsx | 0 .../configurations/prompt-editor/page.tsx | 0 .../configurations/prompt-editor/types.ts | 0 .../configurations/prompt-editor/utils.ts | 0 app/{(routes) => (main)}/datasets/page.tsx | 0 app/{(routes) => (main)}/document/page.tsx | 2 +- app/{(routes) => (main)}/evaluations/[id]/page.tsx | 0 app/{(routes) => (main)}/evaluations/page.tsx | 2 +- app/{(routes) => (main)}/keystore/page.tsx | 0 app/{(routes) => (main)}/knowledge-base/page.tsx | 0 app/{(routes) => (main)}/settings/credentials/page.tsx | 0 app/{(routes) => (main)}/speech-to-text/page.tsx | 0 app/{(routes) => (main)}/test-evaluation/page.tsx | 0 app/{(routes) => (main)}/text-to-speech/page.tsx | 0 app/components/evaluations/DatasetsTab.tsx | 2 +- app/components/evaluations/EvaluationsTab.tsx | 2 +- app/components/prompt-editor/ABTestTab.tsx | 2 +- app/components/prompt-editor/BranchModal.tsx | 2 +- app/components/prompt-editor/CommitNode.tsx | 4 ++-- app/components/prompt-editor/ConfigDrawer.tsx | 2 +- app/components/prompt-editor/ConfigEditorPane.tsx | 2 +- app/components/prompt-editor/CurrentConfigTab.tsx | 5 +---- app/components/prompt-editor/EditorView.tsx | 4 ++-- app/components/prompt-editor/HistoryTab.tsx | 2 +- app/components/prompt-editor/MergeModal.tsx | 4 ++-- 29 files changed, 16 insertions(+), 19 deletions(-) rename app/{(routes) => (main)}/coming-soon/guardrails/page.tsx (100%) rename app/{(routes) => (main)}/coming-soon/model-testing/page.tsx (100%) rename app/{(routes) => (main)}/coming-soon/redteaming/page.tsx (100%) rename app/{(routes) => (main)}/coming-soon/text-to-speech/page.tsx (100%) rename app/{(routes) => (main)}/configurations/page.tsx (100%) rename app/{(routes) => (main)}/configurations/prompt-editor/page.tsx (100%) rename app/{(routes) => (main)}/configurations/prompt-editor/types.ts (100%) rename app/{(routes) => (main)}/configurations/prompt-editor/utils.ts (100%) rename app/{(routes) => (main)}/datasets/page.tsx (100%) rename app/{(routes) => (main)}/document/page.tsx (99%) rename app/{(routes) => (main)}/evaluations/[id]/page.tsx (100%) rename app/{(routes) => (main)}/evaluations/page.tsx (99%) rename app/{(routes) => (main)}/keystore/page.tsx (100%) rename app/{(routes) => (main)}/knowledge-base/page.tsx (100%) rename app/{(routes) => (main)}/settings/credentials/page.tsx (100%) rename app/{(routes) => (main)}/speech-to-text/page.tsx (100%) rename app/{(routes) => (main)}/test-evaluation/page.tsx (100%) rename app/{(routes) => (main)}/text-to-speech/page.tsx (100%) diff --git a/app/(routes)/coming-soon/guardrails/page.tsx b/app/(main)/coming-soon/guardrails/page.tsx similarity index 100% rename from app/(routes)/coming-soon/guardrails/page.tsx rename to app/(main)/coming-soon/guardrails/page.tsx diff --git a/app/(routes)/coming-soon/model-testing/page.tsx b/app/(main)/coming-soon/model-testing/page.tsx similarity index 100% rename from app/(routes)/coming-soon/model-testing/page.tsx rename to app/(main)/coming-soon/model-testing/page.tsx diff --git a/app/(routes)/coming-soon/redteaming/page.tsx b/app/(main)/coming-soon/redteaming/page.tsx similarity index 100% rename from app/(routes)/coming-soon/redteaming/page.tsx rename to app/(main)/coming-soon/redteaming/page.tsx diff --git a/app/(routes)/coming-soon/text-to-speech/page.tsx b/app/(main)/coming-soon/text-to-speech/page.tsx similarity index 100% rename from app/(routes)/coming-soon/text-to-speech/page.tsx rename to app/(main)/coming-soon/text-to-speech/page.tsx diff --git a/app/(routes)/configurations/page.tsx b/app/(main)/configurations/page.tsx similarity index 100% rename from app/(routes)/configurations/page.tsx rename to app/(main)/configurations/page.tsx diff --git a/app/(routes)/configurations/prompt-editor/page.tsx b/app/(main)/configurations/prompt-editor/page.tsx similarity index 100% rename from app/(routes)/configurations/prompt-editor/page.tsx rename to app/(main)/configurations/prompt-editor/page.tsx diff --git a/app/(routes)/configurations/prompt-editor/types.ts b/app/(main)/configurations/prompt-editor/types.ts similarity index 100% rename from app/(routes)/configurations/prompt-editor/types.ts rename to app/(main)/configurations/prompt-editor/types.ts diff --git a/app/(routes)/configurations/prompt-editor/utils.ts b/app/(main)/configurations/prompt-editor/utils.ts similarity index 100% rename from app/(routes)/configurations/prompt-editor/utils.ts rename to app/(main)/configurations/prompt-editor/utils.ts diff --git a/app/(routes)/datasets/page.tsx b/app/(main)/datasets/page.tsx similarity index 100% rename from app/(routes)/datasets/page.tsx rename to app/(main)/datasets/page.tsx diff --git a/app/(routes)/document/page.tsx b/app/(main)/document/page.tsx similarity index 99% rename from app/(routes)/document/page.tsx rename to app/(main)/document/page.tsx index 01ce54f..7d8c1d6 100644 --- a/app/(routes)/document/page.tsx +++ b/app/(main)/document/page.tsx @@ -743,7 +743,7 @@ function DocumentPreview({ document, isLoading }: DocumentPreviewProps) { // Reset error state when document changes useEffect(() => { - // eslint-disable-next-line react-hooks/set-state-in-effect + setImageLoadError(false); }, [document?.id]); diff --git a/app/(routes)/evaluations/[id]/page.tsx b/app/(main)/evaluations/[id]/page.tsx similarity index 100% rename from app/(routes)/evaluations/[id]/page.tsx rename to app/(main)/evaluations/[id]/page.tsx diff --git a/app/(routes)/evaluations/page.tsx b/app/(main)/evaluations/page.tsx similarity index 99% rename from app/(routes)/evaluations/page.tsx rename to app/(main)/evaluations/page.tsx index 12bcf45..ee6d853 100644 --- a/app/(routes)/evaluations/page.tsx +++ b/app/(main)/evaluations/page.tsx @@ -10,7 +10,7 @@ import { useState, useEffect, useCallback, Suspense } from "react"; import { colors } from "@/app/lib/colors"; import { useSearchParams } from "next/navigation"; -import { Dataset } from "@/app/(routes)/datasets/page"; +import { Dataset } from "@/app/(main)/datasets/page"; import Sidebar from "@/app/components/Sidebar"; import TabNavigation from "@/app/components/TabNavigation"; import { useToast } from "@/app/components/Toast"; diff --git a/app/(routes)/keystore/page.tsx b/app/(main)/keystore/page.tsx similarity index 100% rename from app/(routes)/keystore/page.tsx rename to app/(main)/keystore/page.tsx diff --git a/app/(routes)/knowledge-base/page.tsx b/app/(main)/knowledge-base/page.tsx similarity index 100% rename from app/(routes)/knowledge-base/page.tsx rename to app/(main)/knowledge-base/page.tsx diff --git a/app/(routes)/settings/credentials/page.tsx b/app/(main)/settings/credentials/page.tsx similarity index 100% rename from app/(routes)/settings/credentials/page.tsx rename to app/(main)/settings/credentials/page.tsx diff --git a/app/(routes)/speech-to-text/page.tsx b/app/(main)/speech-to-text/page.tsx similarity index 100% rename from app/(routes)/speech-to-text/page.tsx rename to app/(main)/speech-to-text/page.tsx diff --git a/app/(routes)/test-evaluation/page.tsx b/app/(main)/test-evaluation/page.tsx similarity index 100% rename from app/(routes)/test-evaluation/page.tsx rename to app/(main)/test-evaluation/page.tsx diff --git a/app/(routes)/text-to-speech/page.tsx b/app/(main)/text-to-speech/page.tsx similarity index 100% rename from app/(routes)/text-to-speech/page.tsx rename to app/(main)/text-to-speech/page.tsx diff --git a/app/components/evaluations/DatasetsTab.tsx b/app/components/evaluations/DatasetsTab.tsx index 7e41a1a..f89fd29 100644 --- a/app/components/evaluations/DatasetsTab.tsx +++ b/app/components/evaluations/DatasetsTab.tsx @@ -3,7 +3,7 @@ import { useState, useEffect, useRef } from "react"; import { colors } from "@/app/lib/colors"; import { APIKey } from "@/app/lib/types/credentials"; -import { Dataset } from "@/app/(routes)/datasets/page"; +import { Dataset } from "@/app/(main)/datasets/page"; import { useToast } from "@/app/components/Toast"; import EvalDatasetDescription from "./EvalDatasetDescription"; import Loader from "@/app/components/Loader"; diff --git a/app/components/evaluations/EvaluationsTab.tsx b/app/components/evaluations/EvaluationsTab.tsx index a694bc0..76f6401 100644 --- a/app/components/evaluations/EvaluationsTab.tsx +++ b/app/components/evaluations/EvaluationsTab.tsx @@ -2,7 +2,7 @@ import { useState, useEffect, useCallback } from "react"; import { colors } from "@/app/lib/colors"; -import { Dataset } from "@/app/(routes)/datasets/page"; +import { Dataset } from "@/app/(main)/datasets/page"; import { EvalJob, AssistantConfig } from "@/app/components/types"; import ConfigSelector from "@/app/components/ConfigSelector"; import Loader from "@/app/components/Loader"; diff --git a/app/components/prompt-editor/ABTestTab.tsx b/app/components/prompt-editor/ABTestTab.tsx index 9d87867..f390ef6 100644 --- a/app/components/prompt-editor/ABTestTab.tsx +++ b/app/components/prompt-editor/ABTestTab.tsx @@ -4,7 +4,7 @@ import { Commit, LegacyVariant, TestResult, -} from "@/app/(routes)/configurations/prompt-editor/types"; +} from "@/app/(main)/configurations/prompt-editor/types"; interface ABTestTabProps { variants: LegacyVariant[]; diff --git a/app/components/prompt-editor/BranchModal.tsx b/app/components/prompt-editor/BranchModal.tsx index c5af63b..1816c60 100644 --- a/app/components/prompt-editor/BranchModal.tsx +++ b/app/components/prompt-editor/BranchModal.tsx @@ -1,5 +1,5 @@ import { colors } from "@/app/lib/colors"; -import { UnifiedCommit } from "@/app/(routes)/configurations/prompt-editor/types"; +import { UnifiedCommit } from "@/app/(main)/configurations/prompt-editor/types"; interface BranchModalProps { isOpen: boolean; diff --git a/app/components/prompt-editor/CommitNode.tsx b/app/components/prompt-editor/CommitNode.tsx index 7cc3deb..468f27c 100644 --- a/app/components/prompt-editor/CommitNode.tsx +++ b/app/components/prompt-editor/CommitNode.tsx @@ -1,9 +1,9 @@ import { colors } from "@/app/lib/colors"; -import { UnifiedCommit } from "@/app/(routes)/configurations/prompt-editor/types"; +import { UnifiedCommit } from "@/app/(main)/configurations/prompt-editor/types"; import { getUnifiedBranchColor, formatTime, -} from "@/app/(routes)/configurations/prompt-editor/utils"; +} from "@/app/(main)/configurations/prompt-editor/utils"; interface CommitNodeProps { commit: UnifiedCommit; diff --git a/app/components/prompt-editor/ConfigDrawer.tsx b/app/components/prompt-editor/ConfigDrawer.tsx index 1d2c65b..5e98d7f 100644 --- a/app/components/prompt-editor/ConfigDrawer.tsx +++ b/app/components/prompt-editor/ConfigDrawer.tsx @@ -6,7 +6,7 @@ import { Commit, LegacyVariant, TestResult, -} from "@/app/(routes)/configurations/prompt-editor/types"; +} from "@/app/(main)/configurations/prompt-editor/types"; import CurrentConfigTab from "./CurrentConfigTab"; import HistoryTab from "./HistoryTab"; import ABTestTab from "./ABTestTab"; diff --git a/app/components/prompt-editor/ConfigEditorPane.tsx b/app/components/prompt-editor/ConfigEditorPane.tsx index a619cc8..05acd2f 100644 --- a/app/components/prompt-editor/ConfigEditorPane.tsx +++ b/app/components/prompt-editor/ConfigEditorPane.tsx @@ -3,7 +3,7 @@ import { colors } from "@/app/lib/colors"; import { ConfigBlob, Tool, -} from "@/app/(routes)/configurations/prompt-editor/types"; +} from "@/app/(main)/configurations/prompt-editor/types"; import { ConfigPublic, SavedConfig, diff --git a/app/components/prompt-editor/CurrentConfigTab.tsx b/app/components/prompt-editor/CurrentConfigTab.tsx index fa47ae1..f263939 100644 --- a/app/components/prompt-editor/CurrentConfigTab.tsx +++ b/app/components/prompt-editor/CurrentConfigTab.tsx @@ -1,9 +1,6 @@ import { useState } from "react"; import { colors } from "@/app/lib/colors"; -import { - Config, - Tool, -} from "@/app/(routes)/configurations/prompt-editor/types"; +import { Config, Tool } from "@/app/(main)/configurations/prompt-editor/types"; interface CurrentConfigTabProps { configs: Config[]; diff --git a/app/components/prompt-editor/EditorView.tsx b/app/components/prompt-editor/EditorView.tsx index fc423c1..a17545a 100644 --- a/app/components/prompt-editor/EditorView.tsx +++ b/app/components/prompt-editor/EditorView.tsx @@ -1,6 +1,6 @@ import { colors } from "@/app/lib/colors"; -import { Commit } from "@/app/(routes)/configurations/prompt-editor/types"; -import { getBranchColor } from "@/app/(routes)/configurations/prompt-editor/utils"; +import { Commit } from "@/app/(main)/configurations/prompt-editor/types"; +import { getBranchColor } from "@/app/(main)/configurations/prompt-editor/utils"; interface EditorViewProps { currentBranch: string; diff --git a/app/components/prompt-editor/HistoryTab.tsx b/app/components/prompt-editor/HistoryTab.tsx index d58a2c0..c861517 100644 --- a/app/components/prompt-editor/HistoryTab.tsx +++ b/app/components/prompt-editor/HistoryTab.tsx @@ -1,5 +1,5 @@ import { colors } from "@/app/lib/colors"; -import { Config } from "@/app/(routes)/configurations/prompt-editor/types"; +import { Config } from "@/app/(main)/configurations/prompt-editor/types"; interface HistoryTabProps { configs: Config[]; diff --git a/app/components/prompt-editor/MergeModal.tsx b/app/components/prompt-editor/MergeModal.tsx index 7b72d46..21096f5 100644 --- a/app/components/prompt-editor/MergeModal.tsx +++ b/app/components/prompt-editor/MergeModal.tsx @@ -1,9 +1,9 @@ import { colors } from "@/app/lib/colors"; -import { UnifiedCommit } from "@/app/(routes)/configurations/prompt-editor/types"; +import { UnifiedCommit } from "@/app/(main)/configurations/prompt-editor/types"; import { getAllUnifiedBranches, getUnifiedBranchColor, -} from "@/app/(routes)/configurations/prompt-editor/utils"; +} from "@/app/(main)/configurations/prompt-editor/utils"; interface MergeModalProps { isOpen: boolean; From 8a385040ebcaeda267786dfc51120e39f6d29eac Mon Sep 17 00:00:00 2001 From: Ayush8923 <80516839+Ayush8923@users.noreply.github.com> Date: Fri, 27 Mar 2026 22:53:54 +0530 Subject: [PATCH 2/9] fix(*): formatting --- app/(main)/document/page.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/(main)/document/page.tsx b/app/(main)/document/page.tsx index 7d8c1d6..67aa33a 100644 --- a/app/(main)/document/page.tsx +++ b/app/(main)/document/page.tsx @@ -743,7 +743,6 @@ function DocumentPreview({ document, isLoading }: DocumentPreviewProps) { // Reset error state when document changes useEffect(() => { - setImageLoadError(false); }, [document?.id]); From 034efc3e18072d544848f76eb5fd1b6679a787b1 Mon Sep 17 00:00:00 2001 From: Ayush8923 <80516839+Ayush8923@users.noreply.github.com> Date: Sun, 29 Mar 2026 21:40:13 +0530 Subject: [PATCH 3/9] feat(*): document pagination params & document folder restructuring & cleanups --- app/(main)/configurations/page.tsx | 51 +- .../configurations/prompt-editor/page.tsx | 72 +- .../configurations/prompt-editor/utils.ts | 326 ----- app/(main)/datasets/page.tsx | 71 +- app/(main)/document/page.tsx | 1211 +---------------- app/(main)/evaluations/page.tsx | 53 +- app/(main)/keystore/page.tsx | 53 +- app/(main)/knowledge-base/page.tsx | 71 +- app/(main)/speech-to-text/page.tsx | 48 +- app/(main)/text-to-speech/page.tsx | 48 +- app/components/PageHeader.tsx | 68 + app/components/Sidebar.tsx | 392 +----- app/components/document/DocumentListing.tsx | 152 +++ app/components/document/DocumentPreview.tsx | 209 +++ .../document/UploadDocumentModal.tsx | 149 ++ app/components/icons/document/CloseIcon.tsx | 21 + .../icons/document/CloudUploadIcon.tsx | 21 + .../icons/document/DocumentFileIcon.tsx | 21 + .../icons/document/DocumentTextIcon.tsx | 21 + app/components/icons/document/KeyIcon.tsx | 21 + app/components/icons/document/TrashIcon.tsx | 21 + app/components/icons/document/WarningIcon.tsx | 21 + app/components/icons/index.tsx | 17 + .../prompt-editor/ArrowBackCircleIcon.tsx | 21 + .../icons/prompt-editor/CheckCircleIcon.tsx | 21 + .../icons/prompt-editor/PlayCircleIcon.tsx | 26 + app/components/icons/sidebar/BookOpenIcon.tsx | 21 + .../icons/sidebar/ClipboardIcon.tsx | 21 + app/components/icons/sidebar/SlidersIcon.tsx | 21 + app/components/prompt-editor/ABTestTab.tsx | 2 +- app/components/prompt-editor/BranchModal.tsx | 2 +- app/components/prompt-editor/CommitNode.tsx | 10 +- app/components/prompt-editor/ConfigDrawer.tsx | 4 +- .../prompt-editor/ConfigEditorPane.tsx | 5 +- .../prompt-editor/CurrentConfigTab.tsx | 2 +- app/components/prompt-editor/EditorView.tsx | 4 +- app/components/prompt-editor/Header.tsx | 250 +--- app/components/prompt-editor/HistoryTab.tsx | 2 +- app/components/prompt-editor/MergeModal.tsx | 4 +- app/lib/promptEditorUtils.ts | 53 + .../types.ts => lib/types/promptEditor.ts} | 34 +- 41 files changed, 1185 insertions(+), 2456 deletions(-) delete mode 100644 app/(main)/configurations/prompt-editor/utils.ts create mode 100644 app/components/PageHeader.tsx create mode 100644 app/components/document/DocumentListing.tsx create mode 100644 app/components/document/DocumentPreview.tsx create mode 100644 app/components/document/UploadDocumentModal.tsx create mode 100644 app/components/icons/document/CloseIcon.tsx create mode 100644 app/components/icons/document/CloudUploadIcon.tsx create mode 100644 app/components/icons/document/DocumentFileIcon.tsx create mode 100644 app/components/icons/document/DocumentTextIcon.tsx create mode 100644 app/components/icons/document/KeyIcon.tsx create mode 100644 app/components/icons/document/TrashIcon.tsx create mode 100644 app/components/icons/document/WarningIcon.tsx create mode 100644 app/components/icons/prompt-editor/ArrowBackCircleIcon.tsx create mode 100644 app/components/icons/prompt-editor/CheckCircleIcon.tsx create mode 100644 app/components/icons/prompt-editor/PlayCircleIcon.tsx create mode 100644 app/components/icons/sidebar/BookOpenIcon.tsx create mode 100644 app/components/icons/sidebar/ClipboardIcon.tsx create mode 100644 app/components/icons/sidebar/SlidersIcon.tsx create mode 100644 app/lib/promptEditorUtils.ts rename app/{(main)/configurations/prompt-editor/types.ts => lib/types/promptEditor.ts} (58%) diff --git a/app/(main)/configurations/page.tsx b/app/(main)/configurations/page.tsx index 12fbf32..09a93f3 100644 --- a/app/(main)/configurations/page.tsx +++ b/app/(main)/configurations/page.tsx @@ -8,6 +8,7 @@ import { useState, useEffect, useCallback, useMemo } from "react"; import { useRouter } from "next/navigation"; import Sidebar from "@/app/components/Sidebar"; +import PageHeader from "@/app/components/PageHeader"; import { colors } from "@/app/lib/colors"; import { usePaginatedList } from "@/app/hooks/usePaginatedList"; import { useInfiniteScroll } from "@/app/hooks/useInfiniteScroll"; @@ -27,7 +28,6 @@ import { } from "@/app/lib/store/configStore"; import { flattenConfigVersion } from "@/app/lib/utils"; import { - SidebarToggleIcon, SearchIcon, RefreshIcon, PlusIcon, @@ -45,7 +45,7 @@ export default function ConfigLibraryPage() { const [evaluationCounts, setEvaluationCounts] = useState< Record >({}); - const { sidebarCollapsed, setSidebarCollapsed } = useApp(); + const { sidebarCollapsed } = useApp(); const { activeKey } = useAuth(); const apiKey = activeKey?.key; const [searchInput, setSearchInput] = useState(""); @@ -196,49 +196,10 @@ export default function ConfigLibraryPage() {
-
-
- -
-

- Configuration Library -

-

- Manage your prompts and model configurations -

-
-
-
+ {/* Toolbar */}
(defaultConfig); + useState(DEFAULT_CONFIG); const [currentConfigName, setCurrentConfigName] = useState(""); const [selectedConfigId, setSelectedConfigId] = useState(""); // Selected version ID const [currentConfigParentId, setCurrentConfigParentId] = - useState(""); // Parent config ID for evaluation + useState(""); const [currentConfigVersion, setCurrentConfigVersion] = useState(0); // Version number for evaluation const [provider, setProvider] = useState("openai"); const [temperature, setTemperature] = useState(0.7); @@ -107,7 +105,6 @@ function PromptEditorContent() { const versionItemsMap = stableVersionItemsMap; - // Populate the editor from a fully-loaded SavedConfig const applyConfig = React.useCallback( (config: SavedConfig, selectInHistory?: boolean) => { setCurrentContent(config.promptContent); @@ -141,13 +138,12 @@ function PromptEditorContent() { [], ); - // Load a config directly from a SavedConfig object (no savedConfigs lookup needed) const handleLoadConfig = React.useCallback( (config: SavedConfig | null) => { if (!config) { // Reset to new config setCurrentContent(""); - setCurrentConfigBlob(defaultConfig); + setCurrentConfigBlob(DEFAULT_CONFIG); setProvider("openai"); setTemperature(0.7); setSelectedConfigId(""); @@ -157,11 +153,10 @@ function PromptEditorContent() { setTools([]); return; } - // Load the lightweight version list for the history sidebar (1 call or no-op if cached) loadVersionsForConfig(config.config_id); applyConfig(config); }, - [applyConfig, loadVersionsForConfig, defaultConfig], + [applyConfig, loadVersionsForConfig, DEFAULT_CONFIG], ); // Initialize editor from URL params — runs once, on first load completion @@ -173,7 +168,7 @@ function PromptEditorContent() { // If new config is requested, reset to defaults if (isNewConfig) { setCurrentContent(""); - setCurrentConfigBlob(defaultConfig); + setCurrentConfigBlob(DEFAULT_CONFIG); setProvider("openai"); setTemperature(0.7); setSelectedConfigId(""); @@ -191,7 +186,6 @@ function PromptEditorContent() { } (async () => { - // Load version list for history sidebar (1 call, cached on subsequent runs) await loadVersionsForConfig(urlConfigId); const items = configState.versionItemsCache[urlConfigId] ?? []; @@ -200,7 +194,6 @@ function PromptEditorContent() { return; } - // Resolve the target version number (latest if no specific version requested) const versionNum = urlVersion ? parseInt(urlVersion) : items.reduce((a, b) => (b.version > a.version ? b : a)).version; @@ -218,7 +211,7 @@ function PromptEditorContent() { loadVersionsForConfig, loadSingleVersion, applyConfig, - defaultConfig, + DEFAULT_CONFIG, ]); // Re-populate version items when missing (e.g. after background cache revalidation wipes versionItemsCache) @@ -228,10 +221,8 @@ function PromptEditorContent() { } }, [currentConfigParentId, versionItemsMap, loadVersionsForConfig]); - // Detect unsaved changes useEffect(() => { if (!selectedConfigId) { - // New config - always has unsaved changes until saved setHasUnsavedChanges(true); return; } @@ -268,7 +259,6 @@ function PromptEditorContent() { savedConfigs, ]); - // Save current configuration const handleSaveConfig = async () => { if (!currentConfigName.trim()) { toast.error("Please enter a configuration name"); @@ -284,16 +274,12 @@ function PromptEditorContent() { setIsSaving(true); try { - // Build config blob (store prompt in instructions field) - // Extract tools array and flatten to direct params fields for backend compatibility const tools = currentConfigBlob.completion.params.tools || []; - // Collect ALL knowledge_base_ids from ALL tools into a single array const allKnowledgeBaseIds: string[] = []; - let maxNumResults = 20; // default + let maxNumResults = 20; tools.forEach((tool) => { - // Add all knowledge_base_ids from this tool allKnowledgeBaseIds.push(...tool.knowledge_base_ids); // Use max_num_results from first tool (could be made configurable) if (allKnowledgeBaseIds.length === tool.knowledge_base_ids.length) { @@ -304,12 +290,11 @@ function PromptEditorContent() { const configBlob: ConfigBlob = { completion: { provider: currentConfigBlob.completion.provider, - type: currentConfigBlob.completion.type || "text", // Default to 'text' + type: currentConfigBlob.completion.type || "text", params: { model: currentConfigBlob.completion.params.model, - instructions: currentContent, // Store prompt as instructions + instructions: currentContent, temperature: currentConfigBlob.completion.params.temperature, - // Flatten tools array to direct fields for backend - support multiple knowledge bases ...(allKnowledgeBaseIds.length > 0 && { knowledge_base_ids: allKnowledgeBaseIds, max_num_results: maxNumResults, @@ -318,13 +303,11 @@ function PromptEditorContent() { }, }; - // Check if updating existing config (same name exists) using allConfigMeta const existingConfigMeta = allConfigMeta.find( (m) => m.name === currentConfigName.trim(), ); if (existingConfigMeta) { - // Create new version for existing config const versionCreate: ConfigVersionCreate = { config_blob: configBlob, commit_message: commitMessage.trim() || `Updated prompt and config`, @@ -350,7 +333,6 @@ function PromptEditorContent() { `Configuration "${currentConfigName}" updated! New version created.`, ); } else { - // Create new config const configCreate: ConfigCreate = { name: currentConfigName.trim(), description: `${provider} configuration with prompt`, @@ -379,11 +361,9 @@ function PromptEditorContent() { ); } - // Invalidate config cache and refresh from shared hook invalidateConfigCache(); await refetchConfigs(true); - // Reset unsaved changes flag and commit message after successful save setHasUnsavedChanges(false); setCommitMessage(""); } catch (e) { @@ -407,8 +387,6 @@ function PromptEditorContent() {
{ - const oldLines = oldText.split("\n"); - const newLines = newText.split("\n"); - - const n = oldLines.length; - const m = newLines.length; - const max = n + m; - const v: Record = {}; - const trace: Record[] = []; - - v[1] = 0; - - for (let d = 0; d <= max; d++) { - trace.push({ ...v }); - for (let k = -d; k <= d; k += 2) { - let x; - if (k === -d || (k !== d && v[k - 1] < v[k + 1])) { - x = v[k + 1]; - } else { - x = v[k - 1] + 1; - } - let y = x - k; - - while (x < n && y < m && oldLines[x] === newLines[y]) { - x++; - y++; - } - - v[k] = x; - - if (x >= n && y >= m) { - const result: DiffLine[] = []; - let x = n, - y = m; - - for (let d = trace.length - 1; d >= 0; d--) { - const v = trace[d]; - const k = x - y; - - let prevK; - if (k === -d || (k !== d && v[k - 1] < v[k + 1])) { - prevK = k + 1; - } else { - prevK = k - 1; - } - - const prevX = v[prevK]; - const prevY = prevX - prevK; - - while (x > prevX && y > prevY) { - result.unshift({ - type: "unchanged", - oldLine: oldLines[x - 1], - newLine: newLines[y - 1], - oldNum: x, - newNum: y, - }); - x--; - y--; - } - - if (d > 0) { - if (x === prevX) { - result.unshift({ - type: "added", - oldLine: null, - newLine: newLines[y - 1], - oldNum: null, - newNum: y, - }); - y--; - } else { - result.unshift({ - type: "removed", - oldLine: oldLines[x - 1], - newLine: null, - oldNum: x, - newNum: null, - }); - x--; - } - } - } - - return result; - } - } - } - - return []; -}; - -/** - * Get all unique branches from commits - */ -export const getAllBranches = (commits: Commit[]): string[] => { - return [...new Set(commits.map((c) => c.branch))]; -}; - -/** - * Get the latest commit on a specific branch - */ -export const getLatestCommitOnBranch = ( - commits: Commit[], - branchName: string, -): Commit | null => { - const branchCommits = commits.filter((c) => c.branch === branchName); - return branchCommits.length > 0 - ? branchCommits[branchCommits.length - 1] - : null; -}; - -/** - * Get color for a branch based on its index - */ -export const getBranchColor = ( - commits: Commit[], - branchName: string, -): string => { - const branchColors = ["#0969da", "#8250df", "#1f883d", "#d1242f", "#bf8700"]; - const index = getAllBranches(commits).indexOf(branchName); - return branchColors[index % branchColors.length]; -}; - -/** - * Get color for a branch based on its index (unified version) - */ -export const getUnifiedBranchColor = ( - commits: UnifiedCommit[], - branchName: string, -): string => { - const branchColors = ["#0969da", "#8250df", "#1f883d", "#d1242f", "#bf8700"]; - const index = getAllUnifiedBranches(commits).indexOf(branchName); - return branchColors[index % branchColors.length]; -}; - -/** - * Format timestamp as relative time - */ -export const formatTime = (timestamp: number): string => { - const diff = Date.now() - timestamp; - const hours = Math.floor(diff / 3600000); - const days = Math.floor(diff / 86400000); - if (hours < 1) return "Just now"; - if (hours < 24) return `${hours}h ago`; - if (days < 7) return `${days}d ago`; - return new Date(timestamp).toLocaleDateString(); -}; - -/** - * Compare two ConfigBlob objects and return the differences - */ -export const getConfigDiff = ( - oldConfig: ConfigBlob, - newConfig: ConfigBlob, -): ConfigDiff[] => { - const diffs: ConfigDiff[] = []; - - // Compare provider - if (oldConfig.completion.provider !== newConfig.completion.provider) { - diffs.push({ - field: "provider", - oldValue: oldConfig.completion.provider, - newValue: newConfig.completion.provider, - path: "completion.provider", - changed: true, - }); - } - - // Compare model - if (oldConfig.completion.params.model !== newConfig.completion.params.model) { - diffs.push({ - field: "model", - oldValue: oldConfig.completion.params.model, - newValue: newConfig.completion.params.model, - path: "completion.params.model", - changed: true, - }); - } - - // Compare instructions - if ( - oldConfig.completion.params.instructions !== - newConfig.completion.params.instructions - ) { - diffs.push({ - field: "instructions", - oldValue: oldConfig.completion.params.instructions, - newValue: newConfig.completion.params.instructions, - path: "completion.params.instructions", - changed: true, - }); - } - - // Compare temperature - if ( - oldConfig.completion.params.temperature !== - newConfig.completion.params.temperature - ) { - diffs.push({ - field: "temperature", - oldValue: oldConfig.completion.params.temperature, - newValue: newConfig.completion.params.temperature, - path: "completion.params.temperature", - changed: true, - }); - } - - // Compare tools (array comparison using JSON stringify for simplicity) - const toolsDifferent = - JSON.stringify(oldConfig.completion.params.tools) !== - JSON.stringify(newConfig.completion.params.tools); - if (toolsDifferent) { - diffs.push({ - field: "tools", - oldValue: oldConfig.completion.params.tools, - newValue: newConfig.completion.params.tools, - path: "completion.params.tools", - changed: true, - }); - } - - return diffs; -}; - -/** - * Get unified diff combining prompt and config changes - */ -export const getUnifiedDiff = ( - oldCommit: UnifiedCommit, - newCommit: UnifiedCommit, -): UnifiedDiff => { - const promptDiff = getLineDiff( - oldCommit.promptContent, - newCommit.promptContent, - ); - const configDiff = getConfigDiff(oldCommit.configBlob, newCommit.configBlob); - - return { - promptDiff, - configDiff, - stats: { - promptAdditions: promptDiff.filter((d) => d.type === "added").length, - promptDeletions: promptDiff.filter((d) => d.type === "removed").length, - configChanges: configDiff.filter((d) => d.changed).length, - }, - }; -}; - -/** - * Check if prompt has changes compared to committed version - */ -export const hasPromptChanges = ( - current: string, - committed: string, -): boolean => { - return current !== committed; -}; - -/** - * Check if config has changes compared to committed version - */ -export const hasConfigChanges = ( - current: ConfigBlob, - committed: ConfigBlob, -): boolean => { - return JSON.stringify(current) !== JSON.stringify(committed); -}; - -/** - * Auto-generate commit message based on what changed - */ -export const generateCommitMessage = ( - promptChanged: boolean, - configChanged: boolean, - configDiff: ConfigDiff[], -): string => { - if (promptChanged && configChanged) { - const configSummary = configDiff - .slice(0, 2) - .map((d) => `${d.field}`) - .join(", "); - return `Updated prompt and config (${configSummary})`; - } - if (promptChanged) return "Updated prompt"; - if (configChanged) { - if (configDiff.length === 1) { - const diff = configDiff[0]; - return `Updated ${diff.field}`; - } - return `Updated config (${configDiff.length} changes)`; - } - return "No changes"; -}; - -/** - * Get all unique branches from unified commits - */ -export const getAllUnifiedBranches = (commits: UnifiedCommit[]): string[] => { - return [...new Set(commits.map((c) => c.branch))]; -}; - -/** - * Get the latest unified commit on a specific branch - */ -export const getLatestUnifiedCommitOnBranch = ( - commits: UnifiedCommit[], - branchName: string, -): UnifiedCommit | null => { - const branchCommits = commits.filter((c) => c.branch === branchName); - return branchCommits.length > 0 - ? branchCommits[branchCommits.length - 1] - : null; -}; diff --git a/app/(main)/datasets/page.tsx b/app/(main)/datasets/page.tsx index 7ead23b..ad241bb 100644 --- a/app/(main)/datasets/page.tsx +++ b/app/(main)/datasets/page.tsx @@ -12,6 +12,7 @@ import { useAuth } from "@/app/lib/context/AuthContext"; import { useApp } from "@/app/lib/context/AppContext"; import { APIKey } from "@/app/lib/types/credentials"; import Sidebar from "@/app/components/Sidebar"; +import PageHeader from "@/app/components/PageHeader"; import { useToast } from "@/app/components/Toast"; // Backend response interface @@ -31,7 +32,7 @@ export const DATASETS_STORAGE_KEY = "kaapi_datasets"; export default function Datasets() { const toast = useToast(); - const { sidebarCollapsed, setSidebarCollapsed } = useApp(); + const { sidebarCollapsed } = useApp(); const [isModalOpen, setIsModalOpen] = useState(false); const [datasets, setDatasets] = useState([]); const [selectedFile, setSelectedFile] = useState(null); @@ -232,70 +233,10 @@ export default function Datasets() { {/* Main Content */}
- {/* Title Section with Collapse Button */} -
-
- -
-

- Datasets -

-

- Manage your evaluation datasets -

-
-
-
+ {/* Content Area */}
([]); const [selectedDocument, setSelectedDocument] = useState( null, ); const [isLoadingDocument, setIsLoadingDocument] = useState(false); const [selectedFile, setSelectedFile] = useState(null); const [isUploading, setIsUploading] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); const { activeKey: apiKey } = useAuth(); - // Pagination state - const [currentPage, setCurrentPage] = useState(1); - const [itemsPerPage] = useState(10); - - // Fetch documents from backend when API key is available - useEffect(() => { - if (apiKey) { - fetchDocuments(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [apiKey]); - - const fetchDocuments = async () => { - if (!apiKey) { - setError("No API key found. Please add an API key in the Keystore."); - return; - } - - setIsLoading(true); - setError(null); - - try { - const response = await fetch("/api/document", { - method: "GET", - headers: { - "X-API-KEY": apiKey.key, - }, - }); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error( - errorData.error || - errorData.message || - `Failed to fetch documents: ${response.status}`, - ); - } - - const data = await response.json(); - const documentList = Array.isArray(data) ? data : data.data || []; - - // Load file sizes from localStorage - const fileSizeMap = JSON.parse( - localStorage.getItem("document_file_sizes") || "{}", - ); - const documentsWithSize = documentList.map((doc: Document) => ({ - ...doc, - file_size: fileSizeMap[doc.id] || doc.file_size, - })); - - // Sort by inserted_at in descending order (latest first) - const sortedDocuments = documentsWithSize.sort( - (a: Document, b: Document) => - new Date(b.inserted_at || 0).getTime() - - new Date(a.inserted_at || 0).getTime(), - ); - - setDocuments(sortedDocuments); - } catch (err: unknown) { - console.error("Failed to fetch documents:", err); - setError( - err instanceof Error ? err.message : "Failed to fetch documents", - ); - } finally { - setIsLoading(false); - } - }; + const { + items: documents, + isLoading, + isLoadingMore, + hasMore, + error, + loadMore, + refetch, + } = usePaginatedList({ + endpoint: "/api/document", + limit: DEFAULT_PAGE_LIMIT, + }); + + const scrollRef = useInfiniteScroll({ + onLoadMore: loadMore, + hasMore, + isLoading: isLoading || isLoadingMore, + }); const handleFileSelect = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; @@ -115,30 +68,14 @@ export default function DocumentPage() { setIsUploading(true); try { - // Prepare FormData for upload const formData = new FormData(); formData.append("src", selectedFile); - // Upload to backend - const response = await fetch("/api/document", { - method: "POST", - body: formData, - headers: { - "X-API-KEY": apiKey.key, - }, - }); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error( - errorData.error || - errorData.message || - `Upload failed with status ${response.status}`, - ); - } - - const data = await response.json(); - // Store file size in localStorage for the uploaded document + const data = await apiFetch<{ data?: { id: string } }>( + "/api/document", + apiKey.key, + { method: "POST", body: formData }, + ); if (selectedFile && data.data?.id) { const fileSizeMap = JSON.parse( localStorage.getItem("document_file_sizes") || "{}", @@ -150,13 +87,8 @@ export default function DocumentPage() { ); } - // Refresh documents list - await fetchDocuments(); - - // Reset form + refetch(); setSelectedFile(null); - - // Close modal setIsModalOpen(false); toast.success("Document uploaded successfully!"); @@ -176,35 +108,20 @@ export default function DocumentPage() { return; } - // Using browser confirm for now - could be replaced with a custom modal later if (!confirm("Are you sure you want to delete this document?")) { return; } try { - const response = await fetch(`/api/document/${documentId}`, { + await apiFetch(`/api/document/${documentId}`, apiKey.key, { method: "DELETE", - headers: { - "X-API-KEY": apiKey.key, - }, }); - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error( - errorData.error || - errorData.message || - `Delete failed with status ${response.status}`, - ); - } - - // Clear selected document if it was deleted if (selectedDocument?.id === documentId) { setSelectedDocument(null); } - // Refresh documents list - await fetchDocuments(); + refetch(); toast.success("Document deleted successfully"); } catch (error) { console.error("Delete error:", error); @@ -214,35 +131,18 @@ export default function DocumentPage() { } }; - // Pagination calculations - const indexOfLastItem = currentPage * itemsPerPage; - const indexOfFirstItem = indexOfLastItem - itemsPerPage; - const currentDocuments = documents.slice(indexOfFirstItem, indexOfLastItem); - const totalPages = Math.ceil(documents.length / itemsPerPage); - - const paginate = (pageNumber: number) => setCurrentPage(pageNumber); - - // Fetch full document details including signed_url const handleSelectDocument = async (doc: Document) => { if (!apiKey) return; setIsLoadingDocument(true); try { - const response = await fetch(`/api/document/${doc.id}`, { - method: "GET", - headers: { - "X-API-KEY": apiKey.key, - }, - }); - - if (!response.ok) { - throw new Error("Failed to fetch document details"); - } - - const data = await response.json(); - const documentDetails = data.data || data; + const data = await apiFetch<{ data?: Document }>( + `/api/document/${doc.id}`, + apiKey.key, + ); + const documentDetails: Document = + data.data ?? (data as unknown as Document); - // Load file size from localStorage const fileSizeMap = JSON.parse( localStorage.getItem("document_file_sizes") || "{}", ); @@ -255,7 +155,6 @@ export default function DocumentPage() { } catch (err) { console.error("Failed to fetch document details:", err); toast.error("Failed to load document preview"); - // Fallback to the basic document info setSelectedDocument(doc); } finally { setIsLoadingDocument(false); @@ -263,107 +162,32 @@ export default function DocumentPage() { }; return ( -
+
- {/* Sidebar */} - {/* Main Content */}
- {/* Title Section with Collapse Button */} -
-
- -
-

- Documents -

-

- Manage your uploaded documents -

-
-
-
+ - {/* Content Area - Split View */} -
- {/* Left Side: Document List */} -
+
+
setIsModalOpen(true)} isLoading={isLoading} + isLoadingMore={isLoadingMore} error={error} apiKey={apiKey} - totalPages={totalPages} - currentPage={currentPage} - onPageChange={paginate} + scrollRef={scrollRef} />
- {/* Right Side: Document Preview */}
- {/* Upload Document Modal */} {isModalOpen && ( ); } - -// ============ DOCUMENT LISTING COMPONENT ============ -interface DocumentListingProps { - documents: Document[]; - selectedDocument: Document | null; - onSelect: (document: Document) => void; - onDelete: (documentId: string) => void; - onUploadNew: () => void; - isLoading: boolean; - error: string | null; - apiKey: APIKey | null; - totalPages: number; - currentPage: number; - onPageChange: (page: number) => void; -} - -function DocumentListing({ - documents, - selectedDocument, - onSelect, - onDelete, - onUploadNew, - isLoading, - error, - apiKey, - totalPages, - currentPage, - onPageChange, -}: DocumentListingProps) { - return ( -
-
-
-

- Your Documents -

- -
-
- -
- {/* Loading State */} - {isLoading && documents.length === 0 ? ( -
- - - -

Loading documents...

-
- ) : !apiKey ? ( -
- - - -

- No API key found -

-

- Please add an API key in the Keystore -

- - Go to Keystore - -
- ) : error ? ( -
-

- Error: {error} -

-
- ) : documents.length === 0 ? ( -
- - - -

- No documents found -

-

- Upload your first document to get started -

- -
- ) : ( -
- {documents.map((doc) => ( -
onSelect(doc)} - className={`border rounded-lg p-3 cursor-pointer transition-colors ${ - selectedDocument?.id === doc.id ? "ring-2 ring-offset-1" : "" - }`} - style={{ - backgroundColor: - selectedDocument?.id === doc.id - ? "hsl(202, 100%, 95%)" - : "hsl(0, 0%, 100%)", - borderColor: - selectedDocument?.id === doc.id - ? "hsl(202, 100%, 50%)" - : "hsl(0, 0%, 85%)", - }} - > -
-
-
- - - -

- {doc.fname} -

-
-
-
{formatDate(doc.inserted_at)}
-
-
- -
-
- ))} -
- )} - - {/* Pagination */} - {!isLoading && - !error && - apiKey && - documents.length > 0 && - totalPages > 1 && ( -
-

- Page {currentPage} of {totalPages} -

-
- - -
-
- )} -
-
- ); -} - -// ============ DOCUMENT PREVIEW COMPONENT ============ -interface DocumentPreviewProps { - document: Document | null; - isLoading: boolean; -} - -function DocumentPreview({ document, isLoading }: DocumentPreviewProps) { - const [imageLoadError, setImageLoadError] = useState(false); - - // Reset error state when document changes - useEffect(() => { - setImageLoadError(false); - }, [document?.id]); - - const getFileExtension = (filename: string) => { - const parts = filename.split("."); - return parts.length > 1 ? parts[parts.length - 1].toLowerCase() : ""; - }; - - // const getFileSize = - - const getMimeType = (filename: string) => { - const ext = getFileExtension(filename); - const mimeTypes: { [key: string]: string } = { - pdf: "application/pdf", - jpg: "image/jpeg", - jpeg: "image/jpeg", - png: "image/png", - gif: "image/gif", - txt: "text/plain", - doc: "application/msword", - docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - }; - return mimeTypes[ext] || "application/octet-stream"; - }; - - if (isLoading) { - return ( -
-
- - - -

Loading document...

-
-
- ); - } - - if (!document) { - return ( -
-
- - - -

- No document selected -

-

- Select a document from the list to preview -

-
-
- ); - } - - return ( -
-
-
-

- {document.fname} -

-
-
-
- File Type -
-
- {getFileExtension(document.fname).toUpperCase() || "Unknown"} -
-
-
-
- File Size -
-
- {document.file_size - ? document.file_size < 1024 * 1024 - ? `${Math.round(document.file_size / 1024)} KB` - : `${(document.file_size / (1024 * 1024)).toFixed(2)} MB` - : "N/A"} -
-
-
-
- Uploaded at -
-
- {formatDate(document.inserted_at)} -
-
-
-
- - {/* Preview Area */} -
-

- Preview -

- - {/* Info message if signed_url is not available */} - {!document.signed_url && document.object_store_url && ( -
-

- ⚠️ Direct preview unavailable. The backend is not generating - signed URLs. Please download the file to view it. -

-
- )} - - {document.signed_url ? ( - <> - {getMimeType(document.fname).startsWith("image/") ? ( - imageLoadError ? ( -
-

- Failed to load image preview. Check console for details. -

-
- ) : ( - {document.fname} { - setImageLoadError(true); - }} - /> - ) - ) : getMimeType(document.fname) === "application/pdf" ? ( -