From d86df06f9b6659181b447559cc26f6d2eeff19c5 Mon Sep 17 00:00:00 2001 From: Richard R Date: Wed, 10 Jun 2026 11:14:33 -0600 Subject: [PATCH] style(ui): unify modal dialogs with SidebarDialog component Replace ModalFrame and related modal patterns in settings and upload dialogs with the new SidebarDialog component to establish a consistent sidebar-based dialog layout. Refactor SettingsModal and UploadMenuDialog to use SidebarDialog, updating header, navigation, and content handling accordingly. Update DocumentList to use DocumentUploader in compact mode for sidebar upload action. Export SidebarDialog from the UI module. Minor fixes in web-loader for IP parsing and option naming. This change standardizes dialog UI structure and improves maintainability. --- src/app/api/documents/import-url/route.ts | 1 - src/components/SettingsModal.tsx | 133 ++--- src/components/doclist/DocumentList.tsx | 10 +- src/components/documents/DocumentUploader.tsx | 12 +- src/components/documents/UploadMenuDialog.tsx | 477 ++++++++---------- src/components/ui/index.ts | 1 + src/components/ui/sidebar-dialog.tsx | 128 +++++ src/lib/server/documents/web-loader.ts | 4 +- 8 files changed, 402 insertions(+), 364 deletions(-) create mode 100644 src/components/ui/sidebar-dialog.tsx diff --git a/src/app/api/documents/import-url/route.ts b/src/app/api/documents/import-url/route.ts index e66dc43..62e4df1 100644 --- a/src/app/api/documents/import-url/route.ts +++ b/src/app/api/documents/import-url/route.ts @@ -2,7 +2,6 @@ import { NextRequest, NextResponse } from 'next/server'; import { requireAuthContext } from '@/lib/server/auth/auth'; import { fetchAndParseUrl } from '@/lib/server/documents/web-loader'; import { errorToLog, serverLogger } from '@/lib/server/logger'; -import { errorResponse } from '@/lib/server/errors/next-response'; export const dynamic = 'force-dynamic'; diff --git a/src/components/SettingsModal.tsx b/src/components/SettingsModal.tsx index 5ccc55d..f4a0b89 100644 --- a/src/components/SettingsModal.tsx +++ b/src/components/SettingsModal.tsx @@ -42,7 +42,7 @@ import { flushUserPreferencesSync } from '@/lib/client/api/user-state'; import toast from 'react-hot-toast'; import { useLibraryDocumentsQuery } from '@/hooks/useLibraryDocumentsQuery'; import { - SidebarNav, + SidebarDialog, SidebarNavItem, SegmentedControl, Button, @@ -50,8 +50,6 @@ import { IconButton, Input, Textarea, - ModalFrame, - ModalTitle, Select, } from '@/components/ui'; import ReactMarkdown from 'react-markdown'; @@ -562,90 +560,55 @@ export function SettingsModal({ return ( <> - + Settings + + + } + headerRight={ + + } + showCloseButton={false} + sections={visibleSections} + activeSectionId={activeSection} + onSectionChange={setActiveSection} + className="h-[490px]" + contentClassName={ + activeSection === 'admin' + ? 'bg-[radial-gradient(circle_at_top_right,color-mix(in_srgb,var(--accent),transparent_92%),transparent_35%)]' + : '' + } + customContent={ + isChangelogOpen ? ( + setIsChangelogOpen(false)} + /> + ) : undefined + } > - {/* Header */} -
-
- Settings - -
-
- -
-
- - {isChangelogOpen ? ( - setIsChangelogOpen(false)} - /> - ) : ( - <> - {/* Mobile nav */} - - {visibleSections.map((section) => { - const Icon = section.icon; - return ( - setActiveSection(section.id)} - active={activeSection === section.id} - icon={} - label={section.label} - /> - ); - })} - - -
- {/* Desktop: vertical sidebar */} - - - {/* Content */} -
{/* API Section */} {activeSection === 'api' && (
@@ -1174,11 +1137,7 @@ export function SettingsModal({
)} -
- - - )} -
+ setIsUploadDialogOpen(true)} - icon={} - label="Upload documents" /> )} bottomSlot={( diff --git a/src/components/documents/DocumentUploader.tsx b/src/components/documents/DocumentUploader.tsx index ffa1b27..21950d6 100644 --- a/src/components/documents/DocumentUploader.tsx +++ b/src/components/documents/DocumentUploader.tsx @@ -12,6 +12,7 @@ interface DocumentUploaderProps { variant?: 'default' | 'compact' | 'overlay'; children?: ReactNode; onUploadBatchChange?: (state: UploadBatchState) => void; + onClick?: () => void; } export interface UploadBatchState { @@ -28,6 +29,7 @@ export function DocumentUploader({ variant = 'default', children, onUploadBatchChange, + onClick, }: DocumentUploaderProps) { const uploaderId = useId(); const enableDocx = useFeatureFlag('enableDocxConversion'); @@ -88,8 +90,8 @@ export function DocumentUploader({ }, multiple: true, disabled: isUploading, - noClick: variant === 'overlay', - noKeyboard: variant === 'overlay' + noClick: variant === 'overlay' || !!onClick, + noKeyboard: variant === 'overlay' || !!onClick }); const isDisabled = isUploading; @@ -132,6 +134,12 @@ export function DocumentUploader({ return (
{ + if (onClick) { + e.stopPropagation(); + onClick(); + } + }} className={dropzoneSurfaceClass({ variant: variant === 'compact' ? 'compact' : 'default', active: isDragActive, diff --git a/src/components/documents/UploadMenuDialog.tsx b/src/components/documents/UploadMenuDialog.tsx index da492f1..760f755 100644 --- a/src/components/documents/UploadMenuDialog.tsx +++ b/src/components/documents/UploadMenuDialog.tsx @@ -5,10 +5,7 @@ import toast from 'react-hot-toast'; import { useDocuments } from '@/contexts/DocumentContext'; import { importUrl } from '@/lib/client/api/documents'; import { - ModalFrame, - ModalTitle, - SidebarNav, - SidebarNavItem, + SidebarDialog, SegmentedControl, Input, Textarea, @@ -163,285 +160,233 @@ export function UploadMenuDialog({ }; return ( - -
- {/* Header */} -
- Add Documents - + + {/* TAB 1: File Uploader */} + {activeTab === 'file' && ( +
+

+ Select files from your computer or drag and drop them anywhere. Supported formats include PDF, EPUB, TXT, and MD. +

+ { + onUploadBatchChange?.(state); + // Automatically close modal when files successfully start uploading + if (state.isActive) { + onClose(); + } + }} + />
+ )} - {/* Mobile Navigation (Horizontal row of items) */} - - {SIDEBAR_SECTIONS.map((section) => { - const Icon = section.icon; - return ( - handleTabChange(section.id)} - active={activeTab === section.id} - icon={} - label={section.label} + {/* TAB 2: Create Custom Document */} + {activeTab === 'create' && ( +
+
+
+
+ setDocName(e.target.value)} + placeholder="Document title (e.g. notes)" + className="w-full font-medium" + controlSize="md" + disabled={isCreatingDoc} + /> +
+
+ + ariaLabel="Document format selection" + value={docExtension} + options={[ + { value: '.md', label: 'Markdown (.md)' }, + { value: '.txt', label: 'Plain Text (.txt)' }, + ]} + onChange={setDocExtension} + className="grid-cols-2" + /> +
+
+ +
+