documentation-hub 5.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +43 -0
- package/.github/workflows/build.yml +64 -0
- package/.github/workflows/ci.yml +39 -0
- package/.vscode/extensions.json +3 -0
- package/Current.md +97 -0
- package/DocHub_Image.png +0 -0
- package/README.md +666 -0
- package/USER_GUIDE.md +1173 -0
- package/Updater.md +311 -0
- package/build/256x256.png +0 -0
- package/build/512x512.png +0 -0
- package/build/app-update.yml +4 -0
- package/build/create-icon.js +208 -0
- package/build/icon.ico +0 -0
- package/build/icon.png +0 -0
- package/build/icon_1024x1024.png +0 -0
- package/dist/assets/Analytics-BpsG9895.js +1 -0
- package/dist/assets/Card-IAZin8kp.js +1 -0
- package/dist/assets/CurrentSession-B-rFkHvf.js +12 -0
- package/dist/assets/Dashboard-C_5gMb0q.js +1 -0
- package/dist/assets/Documents-CqZ25axS.js +1 -0
- package/dist/assets/Input-l89xwXBi.js +1 -0
- package/dist/assets/Reporting-DqdHJY_a.js +1 -0
- package/dist/assets/Search-XNbu5z_3.js +1 -0
- package/dist/assets/SessionManager-lH9hZfzH.js +1 -0
- package/dist/assets/Sessions-ClZOPYNc.js +1 -0
- package/dist/assets/Settings-DUEHGURa.js +11 -0
- package/dist/assets/index-8xUe8ptc.js +24 -0
- package/dist/assets/index-RYyJqF7O.css +1 -0
- package/dist/assets/path-BkOl0AGO.js +1 -0
- package/dist/assets/promises-ID_B9S-h.js +1 -0
- package/dist/assets/urlHelpers-TvgahX0r.js +1 -0
- package/dist/assets/useToast-yRSO1dkm.js +1 -0
- package/dist/assets/vendor-charts-RkGK5ROP.js +36 -0
- package/dist/assets/vendor-db-l0sNRNKZ.js +1 -0
- package/dist/assets/vendor-react-BVZ_anCF.js +4 -0
- package/dist/assets/vendor-search-Dw8P0qyA.js +1 -0
- package/dist/assets/vendor-ui-BU7NfluV.js +53 -0
- package/dist/electron/PowerAutomateApiService-LfW09ZGr.js +147 -0
- package/dist/electron/main-CXkNtyv-.js +19789 -0
- package/dist/electron/main.js +5 -0
- package/dist/electron/preload.js +1 -0
- package/dist/icon.png +0 -0
- package/dist/index.html +27 -0
- package/docs/CODEBASE_ANALYSIS_REPORT.md +309 -0
- package/docs/DEBUG_LOGGING_GUIDE.md +244 -0
- package/docs/README.md +115 -0
- package/docs/TOC_WIRING_GUIDE.md +344 -0
- package/docs/analysis/Bullet_Symbol_Bug_Analysis.md +136 -0
- package/docs/analysis/DOCXMLATER_ANALYSIS_SUMMARY.txt +169 -0
- package/docs/analysis/Document_Processing_Issues_Analysis.md +704 -0
- package/docs/analysis/FIELD_PRESERVATION_ANALYSIS.md +1200 -0
- package/docs/analysis/INDENTATION_PRESERVE_ANALYSIS.md +181 -0
- package/docs/analysis/INDENTATION_PRESERVE_IMPLEMENTATION.md +207 -0
- package/docs/analysis/List_Implementation.md +206 -0
- package/docs/analysis/List_Implementation_Accuracy_Report.md +366 -0
- package/docs/analysis/PROCESSING_OPTIONS_UI_UPDATES.md +220 -0
- package/docs/analysis/RefactorStyles.md +852 -0
- package/docs/analysis/STYLE_PARAMETER_ENHANCEMENT.md +143 -0
- package/docs/analysis/docxmlater-comparison-todo-2025-11-13.md +636 -0
- package/docs/analysis/docxmlater-implementation-analysis-2025-11-13.md +340 -0
- package/docs/analysis/docxmlater-template_ui-integration-analysis.md +263 -0
- package/docs/analysis/github-issues-to-create.md +237 -0
- package/docs/api/API_README.md +538 -0
- package/docs/api/API_REFERENCE.md +751 -0
- package/docs/api/TYPE_DEFINITIONS.md +869 -0
- package/docs/architecture/FONT_EMBEDDING_GUIDE.md +318 -0
- package/docs/architecture/docxmlater-functions-and-structure.md +726 -0
- package/docs/docxmlater-readme.md +1341 -0
- package/docs/fixes/EXECUTION_LOG_TEST_BASE.md +573 -0
- package/docs/fixes/HYPERLINK_TEXT_SANITIZATION.md +253 -0
- package/docs/fixes/README.md +37 -0
- package/docs/github-issues/issue-1-body.md +125 -0
- package/docs/github-issues/issue-10-body.md +850 -0
- package/docs/github-issues/issue-2-body.md +200 -0
- package/docs/github-issues/issue-3-body.md +270 -0
- package/docs/github-issues/issue-4-body.md +169 -0
- package/docs/github-issues/issue-5-body.md +173 -0
- package/docs/github-issues/issue-6-body.md +158 -0
- package/docs/github-issues/issue-7-body.md +171 -0
- package/docs/github-issues/issue-8-body.md +407 -0
- package/docs/github-issues/issue-9-body.md +515 -0
- package/docs/github-issues/issue-tracker.md +274 -0
- package/docs/github-issues/predictive-analysis-2025-10-18.md +2131 -0
- package/docs/implementation/List_Framework_Refactor_Plan.md +336 -0
- package/docs/implementation/PRIMARY_TEXT_COLOR_FEATURE.md +217 -0
- package/docs/implementation/RELEASE_PLAN_v2.1.0.md +362 -0
- package/docs/implementation/RefactorStyles.md +588 -0
- package/docs/implementation/implement-plan.md +489 -0
- package/docs/implementation/missing-helpers-implementation.md +391 -0
- package/docs/implementation/refactor-plan.md +520 -0
- package/docs/implementation/session-implementation-complete.md +233 -0
- package/docs/implementation/session-management-plan.md +250 -0
- package/docs/setup-checklist.md +77 -0
- package/docs/versions/changelog.md +345 -0
- package/electron/customUpdater.ts +656 -0
- package/electron/main.ts +2441 -0
- package/electron/memoryConfig.ts +187 -0
- package/electron/preload.ts +394 -0
- package/electron/proxyConfig.ts +340 -0
- package/electron/services/BackupService.ts +452 -0
- package/electron/services/DictionaryService.ts +402 -0
- package/electron/services/LocalDictionaryLookupService.ts +147 -0
- package/electron/services/PowerAutomateApiService.ts +231 -0
- package/electron/services/SharePointSyncService.ts +474 -0
- package/electron/windowsCertStore.ts +427 -0
- package/electron/zscalerConfig.ts +381 -0
- package/eslint.config.js +92 -0
- package/jest.config.js +52 -0
- package/package.json +214 -0
- package/postcss.config.mjs +6 -0
- package/public/icon.png +0 -0
- package/publish-release.ps1 +5 -0
- package/renovate.json +30 -0
- package/src/App.tsx +216 -0
- package/src/__mocks__/p-limit.js +12 -0
- package/src/__mocks__/styleMock.js +1 -0
- package/src/components/common/BugReportButton.tsx +44 -0
- package/src/components/common/BugReportDialog.tsx +193 -0
- package/src/components/common/Button.tsx +153 -0
- package/src/components/common/Card.tsx +86 -0
- package/src/components/common/ColorPickerDialog.tsx +177 -0
- package/src/components/common/ConfirmDialog.tsx +96 -0
- package/src/components/common/DebugConsole.tsx +275 -0
- package/src/components/common/EmptyState.tsx +183 -0
- package/src/components/common/ErrorBoundary.tsx +98 -0
- package/src/components/common/ErrorDetailsDialog.tsx +153 -0
- package/src/components/common/ErrorFallback.tsx +218 -0
- package/src/components/common/Input.tsx +109 -0
- package/src/components/common/Skeleton.tsx +184 -0
- package/src/components/common/SplashScreen.tsx +81 -0
- package/src/components/common/Toast.tsx +155 -0
- package/src/components/common/Tooltip.tsx +79 -0
- package/src/components/common/UpdateNotification.tsx +320 -0
- package/src/components/comparison/ComparisonWindow.tsx +374 -0
- package/src/components/comparison/SideBySideDiff.tsx +486 -0
- package/src/components/comparison/index.ts +8 -0
- package/src/components/document/DocumentUploader.tsx +288 -0
- package/src/components/document/HyperlinkPreview.tsx +430 -0
- package/src/components/document/HyperlinkService.md +1484 -0
- package/src/components/document/Hyperlink_Technical_Documentation.md +496 -0
- package/src/components/document/InlineChangesView.tsx +707 -0
- package/src/components/document/ProcessingProgress.tsx +303 -0
- package/src/components/document/ProcessingResults.tsx +256 -0
- package/src/components/document/TrackedChangesDetail.tsx +530 -0
- package/src/components/document/TrackedChangesPanel.tsx +546 -0
- package/src/components/document/VirtualDocumentList.tsx +240 -0
- package/src/components/editor/DocumentEditor.tsx +723 -0
- package/src/components/editor/DocumentEditorModal.tsx +640 -0
- package/src/components/editor/EditorQuickActions.tsx +502 -0
- package/src/components/editor/EditorToolbar.tsx +312 -0
- package/src/components/editor/TableEditor.tsx +926 -0
- package/src/components/editor/index.ts +18 -0
- package/src/components/layout/Header.tsx +190 -0
- package/src/components/layout/Sidebar.tsx +313 -0
- package/src/components/layout/TitleBar.tsx +190 -0
- package/src/components/navigation/CommandPalette.tsx +233 -0
- package/src/components/navigation/KeyboardShortcutsModal.tsx +173 -0
- package/src/components/sessions/ChangeItem.tsx +408 -0
- package/src/components/sessions/ChangeViewer.tsx +1155 -0
- package/src/components/sessions/DocumentComparisonModal.tsx +314 -0
- package/src/components/sessions/ProcessingOptions.tsx +297 -0
- package/src/components/sessions/ReplacementsTab.tsx +438 -0
- package/src/components/sessions/RevisionHandlingOptions.tsx +87 -0
- package/src/components/sessions/SessionManager.tsx +188 -0
- package/src/components/sessions/StylesEditor.tsx +1335 -0
- package/src/components/sessions/TabContainer.tsx +151 -0
- package/src/components/sessions/VirtualSessionList.tsx +157 -0
- package/src/components/sessions/sessionToProcessorManager.tsx +420 -0
- package/src/components/settings/CertificateManager.tsx +410 -0
- package/src/components/settings/SegmentedControl.tsx +88 -0
- package/src/components/settings/SettingRow.tsx +52 -0
- package/src/contexts/GlobalStatsContext.tsx +396 -0
- package/src/contexts/SessionContext.tsx +2129 -0
- package/src/contexts/ThemeContext.tsx +428 -0
- package/src/contexts/UserSettingsContext.tsx +290 -0
- package/src/contexts/__tests__/GlobalStatsContext.test.tsx +390 -0
- package/src/global.d.ts +273 -0
- package/src/hooks/useDocumentQueue.tsx +210 -0
- package/src/hooks/useToast.tsx +55 -0
- package/src/main.tsx +10 -0
- package/src/pages/Analytics.tsx +386 -0
- package/src/pages/CurrentSession.tsx +1174 -0
- package/src/pages/Dashboard.tsx +319 -0
- package/src/pages/Documents.tsx +317 -0
- package/src/pages/Projects.tsx +250 -0
- package/src/pages/Reporting.tsx +386 -0
- package/src/pages/Search.tsx +349 -0
- package/src/pages/Sessions.tsx +285 -0
- package/src/pages/Settings.tsx +2662 -0
- package/src/services/HyperlinkService.ts +1085 -0
- package/src/services/document/DocXMLaterProcessor.ts +617 -0
- package/src/services/document/DocumentProcessingComparison.ts +856 -0
- package/src/services/document/DocumentSnapshotService.ts +575 -0
- package/src/services/document/WordDocumentProcessor.ts +10509 -0
- package/src/services/document/__tests__/DocXMLaterProcessor.hyperlinks.test.md +311 -0
- package/src/services/document/__tests__/WordDocumentProcessor.integration.test.ts +515 -0
- package/src/services/document/__tests__/WordDocumentProcessor.test.ts +812 -0
- package/src/services/document/blanklines/BlankLineManager.ts +658 -0
- package/src/services/document/blanklines/__tests__/paragraphChecks.test.ts +281 -0
- package/src/services/document/blanklines/helpers/blankLineInsertion.ts +87 -0
- package/src/services/document/blanklines/helpers/blankLineSnapshot.ts +251 -0
- package/src/services/document/blanklines/helpers/clearCustom.ts +121 -0
- package/src/services/document/blanklines/helpers/contextChecks.ts +117 -0
- package/src/services/document/blanklines/helpers/imageChecks.ts +51 -0
- package/src/services/document/blanklines/helpers/paragraphChecks.ts +236 -0
- package/src/services/document/blanklines/helpers/removeBlanksBetweenListItems.ts +91 -0
- package/src/services/document/blanklines/helpers/removeTrailingBlanks.ts +35 -0
- package/src/services/document/blanklines/helpers/tableGuards.ts +21 -0
- package/src/services/document/blanklines/index.ts +67 -0
- package/src/services/document/blanklines/rules/additionRules.ts +337 -0
- package/src/services/document/blanklines/rules/indentationRules.ts +317 -0
- package/src/services/document/blanklines/rules/removalRules.ts +362 -0
- package/src/services/document/blanklines/rules/ruleTypes.ts +92 -0
- package/src/services/document/blanklines/types.ts +29 -0
- package/src/services/document/helpers/ImageBorderCropper.ts +377 -0
- package/src/services/document/helpers/__tests__/whitespace.test.ts +272 -0
- package/src/services/document/helpers/whitespace.ts +117 -0
- package/src/services/document/list/ListNormalizer.ts +947 -0
- package/src/services/document/list/index.ts +45 -0
- package/src/services/document/list/list-detection.ts +275 -0
- package/src/services/document/list/list-types.ts +162 -0
- package/src/services/document/processors/HyperlinkProcessor.ts +370 -0
- package/src/services/document/processors/ListProcessor.ts +257 -0
- package/src/services/document/processors/StructureProcessor.ts +176 -0
- package/src/services/document/processors/StyleProcessor.ts +389 -0
- package/src/services/document/processors/TableProcessor.ts +2238 -0
- package/src/services/document/processors/__tests__/HyperlinkProcessor.test.ts +314 -0
- package/src/services/document/processors/__tests__/ListProcessor.test.ts +291 -0
- package/src/services/document/processors/__tests__/StructureProcessor.test.ts +257 -0
- package/src/services/document/processors/__tests__/TableProcessor.hlp-tips-bullets.test.ts +459 -0
- package/src/services/document/processors/__tests__/TableProcessor.test.ts +1604 -0
- package/src/services/document/processors/index.ts +28 -0
- package/src/services/document/types/docx-processing.ts +310 -0
- package/src/services/editor/EditorActionHandlers.ts +901 -0
- package/src/services/editor/index.ts +13 -0
- package/src/setupTests.ts +47 -0
- package/src/styles/global.css +782 -0
- package/src/types/backup.ts +132 -0
- package/src/types/dictionary.ts +125 -0
- package/src/types/document-processing.ts +331 -0
- package/src/types/docxmlater-augments.d.ts +142 -0
- package/src/types/editor.ts +280 -0
- package/src/types/electron.ts +340 -0
- package/src/types/globalStats.ts +155 -0
- package/src/types/hyperlink.ts +471 -0
- package/src/types/operations.ts +354 -0
- package/src/types/session.ts +427 -0
- package/src/types/settings.ts +112 -0
- package/src/utils/MemoryMonitor.ts +248 -0
- package/src/utils/cn.ts +6 -0
- package/src/utils/colorConvert.ts +306 -0
- package/src/utils/diffUtils.ts +347 -0
- package/src/utils/documentUtils.ts +202 -0
- package/src/utils/electronGuard.ts +62 -0
- package/src/utils/indexedDB.ts +915 -0
- package/src/utils/logger.ts +717 -0
- package/src/utils/pathSecurity.ts +232 -0
- package/src/utils/pathValidator.ts +236 -0
- package/src/utils/processingTimeEstimator.ts +153 -0
- package/src/utils/safeJsonParse.ts +62 -0
- package/src/utils/textSanitizer.ts +162 -0
- package/src/utils/urlHelpers.ts +304 -0
- package/src/utils/urlPatterns.ts +198 -0
- package/src/utils/urlSanitizer.ts +152 -0
- package/src/vite-env.d.ts +11 -0
- package/tsconfig.electron.json +19 -0
- package/tsconfig.json +36 -0
- package/tsconfig.node.json +12 -0
- package/typedoc.json +45 -0
- package/vite.config.ts +152 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { useState, useMemo, useEffect, useRef, memo } from 'react';
|
|
2
|
+
import { motion } from 'framer-motion';
|
|
3
|
+
import Fuse from 'fuse.js';
|
|
4
|
+
import {
|
|
5
|
+
Card,
|
|
6
|
+
CardContent,
|
|
7
|
+
CardDescription,
|
|
8
|
+
CardHeader,
|
|
9
|
+
CardTitle,
|
|
10
|
+
} from '@/components/common/Card';
|
|
11
|
+
import { Input } from '@/components/common/Input';
|
|
12
|
+
import { Button } from '@/components/common/Button';
|
|
13
|
+
import { useSession } from '@/contexts/SessionContext';
|
|
14
|
+
import { cn } from '@/utils/cn';
|
|
15
|
+
import {
|
|
16
|
+
Search as SearchIcon,
|
|
17
|
+
Filter,
|
|
18
|
+
FileText,
|
|
19
|
+
Calendar,
|
|
20
|
+
FolderOpen,
|
|
21
|
+
CheckCircle2,
|
|
22
|
+
Clock,
|
|
23
|
+
XCircle,
|
|
24
|
+
ChevronRight,
|
|
25
|
+
} from 'lucide-react';
|
|
26
|
+
import { useNavigate } from 'react-router-dom';
|
|
27
|
+
import { Document } from '@/types/session';
|
|
28
|
+
|
|
29
|
+
type StatusFilter = 'all' | 'completed' | 'pending' | 'error';
|
|
30
|
+
type DateRange = 'all' | 'today' | 'week' | 'month' | 'custom';
|
|
31
|
+
|
|
32
|
+
const containerVariants = {
|
|
33
|
+
hidden: { opacity: 0 },
|
|
34
|
+
visible: {
|
|
35
|
+
opacity: 1,
|
|
36
|
+
transition: {
|
|
37
|
+
staggerChildren: 0.05,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const itemVariants = {
|
|
43
|
+
hidden: { opacity: 0, y: 10 },
|
|
44
|
+
visible: {
|
|
45
|
+
opacity: 1,
|
|
46
|
+
y: 0,
|
|
47
|
+
transition: {
|
|
48
|
+
duration: 0.3,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// PERFORMANCE: Wrap in memo to prevent re-renders when parent state changes
|
|
54
|
+
export const Search = memo(function Search() {
|
|
55
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
56
|
+
const [statusFilter, setStatusFilter] = useState<StatusFilter>('all');
|
|
57
|
+
const [selectedSessionId, setSelectedSessionId] = useState<string>('all');
|
|
58
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
59
|
+
const [showFilters, setShowFilters] = useState(false);
|
|
60
|
+
|
|
61
|
+
const { sessions } = useSession();
|
|
62
|
+
const navigate = useNavigate();
|
|
63
|
+
const resultsRef = useRef<HTMLDivElement>(null);
|
|
64
|
+
|
|
65
|
+
// Flatten all documents from all sessions
|
|
66
|
+
const allDocuments = useMemo(() => {
|
|
67
|
+
const docs: Array<Document & { sessionId: string; sessionName: string }> = [];
|
|
68
|
+
sessions.forEach((session) => {
|
|
69
|
+
session.documents.forEach((doc) => {
|
|
70
|
+
docs.push({
|
|
71
|
+
...doc,
|
|
72
|
+
sessionId: session.id,
|
|
73
|
+
sessionName: session.name,
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
return docs;
|
|
78
|
+
}, [sessions]);
|
|
79
|
+
|
|
80
|
+
// Configure Fuse.js for fuzzy search
|
|
81
|
+
const fuse = useMemo(() => {
|
|
82
|
+
return new Fuse(allDocuments, {
|
|
83
|
+
keys: [
|
|
84
|
+
{ name: 'name', weight: 2 },
|
|
85
|
+
{ name: 'sessionName', weight: 1 },
|
|
86
|
+
{ name: 'path', weight: 0.5 },
|
|
87
|
+
],
|
|
88
|
+
threshold: 0.4,
|
|
89
|
+
includeScore: true,
|
|
90
|
+
});
|
|
91
|
+
}, [allDocuments]);
|
|
92
|
+
|
|
93
|
+
// Filter and search documents
|
|
94
|
+
const filteredDocuments = useMemo(() => {
|
|
95
|
+
let results = searchQuery
|
|
96
|
+
? fuse.search(searchQuery).map((result) => result.item)
|
|
97
|
+
: allDocuments;
|
|
98
|
+
|
|
99
|
+
// Apply status filter
|
|
100
|
+
if (statusFilter !== 'all') {
|
|
101
|
+
results = results.filter((doc) => doc.status === statusFilter);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Apply session filter
|
|
105
|
+
if (selectedSessionId !== 'all') {
|
|
106
|
+
results = results.filter((doc) => doc.sessionId === selectedSessionId);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return results;
|
|
110
|
+
}, [searchQuery, statusFilter, selectedSessionId, fuse, allDocuments]);
|
|
111
|
+
|
|
112
|
+
// Keyboard navigation
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
115
|
+
if (!filteredDocuments.length) return;
|
|
116
|
+
|
|
117
|
+
if (e.key === 'ArrowDown') {
|
|
118
|
+
e.preventDefault();
|
|
119
|
+
setSelectedIndex((prev) => (prev < filteredDocuments.length - 1 ? prev + 1 : prev));
|
|
120
|
+
} else if (e.key === 'ArrowUp') {
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : prev));
|
|
123
|
+
} else if (e.key === 'Enter' && filteredDocuments[selectedIndex]) {
|
|
124
|
+
e.preventDefault();
|
|
125
|
+
handleOpenDocument(filteredDocuments[selectedIndex]);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
130
|
+
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
131
|
+
}, [filteredDocuments, selectedIndex]);
|
|
132
|
+
|
|
133
|
+
// Reset selected index when results change
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
setSelectedIndex(0);
|
|
136
|
+
}, [filteredDocuments]);
|
|
137
|
+
|
|
138
|
+
// Auto-scroll selected item into view
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
if (resultsRef.current) {
|
|
141
|
+
const selectedElement = resultsRef.current.querySelector(`[data-index="${selectedIndex}"]`);
|
|
142
|
+
if (selectedElement) {
|
|
143
|
+
selectedElement.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}, [selectedIndex]);
|
|
147
|
+
|
|
148
|
+
const handleOpenDocument = (doc: Document & { sessionId: string; sessionName: string }) => {
|
|
149
|
+
navigate(`/session/${doc.sessionId}`);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const getStatusIcon = (status: string) => {
|
|
153
|
+
switch (status) {
|
|
154
|
+
case 'completed':
|
|
155
|
+
return <CheckCircle2 className="w-4 h-4 text-green-500" />;
|
|
156
|
+
case 'pending':
|
|
157
|
+
return <Clock className="w-4 h-4 text-yellow-500" />;
|
|
158
|
+
case 'error':
|
|
159
|
+
return <XCircle className="w-4 h-4 text-red-500" />;
|
|
160
|
+
default:
|
|
161
|
+
return <FileText className="w-4 h-4 text-muted-foreground" />;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const statusOptions = [
|
|
166
|
+
{ value: 'all' as StatusFilter, label: 'All Status', count: allDocuments.length },
|
|
167
|
+
{
|
|
168
|
+
value: 'completed' as StatusFilter,
|
|
169
|
+
label: 'Completed',
|
|
170
|
+
count: allDocuments.filter((d) => d.status === 'completed').length,
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
value: 'pending' as StatusFilter,
|
|
174
|
+
label: 'Pending',
|
|
175
|
+
count: allDocuments.filter((d) => d.status === 'pending').length,
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
value: 'error' as StatusFilter,
|
|
179
|
+
label: 'Error',
|
|
180
|
+
count: allDocuments.filter((d) => d.status === 'error').length,
|
|
181
|
+
},
|
|
182
|
+
];
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<motion.div
|
|
186
|
+
variants={containerVariants}
|
|
187
|
+
initial="hidden"
|
|
188
|
+
animate="visible"
|
|
189
|
+
className="p-6 space-y-6 max-w-[1200px] mx-auto"
|
|
190
|
+
>
|
|
191
|
+
{/* Header */}
|
|
192
|
+
<motion.div variants={itemVariants}>
|
|
193
|
+
<h1 className="text-3xl font-bold mb-2 flex items-center gap-2">
|
|
194
|
+
<SearchIcon className="w-8 h-8" />
|
|
195
|
+
Search Documents
|
|
196
|
+
</h1>
|
|
197
|
+
<p className="text-muted-foreground">
|
|
198
|
+
Find documents across all sessions with advanced filtering
|
|
199
|
+
</p>
|
|
200
|
+
</motion.div>
|
|
201
|
+
|
|
202
|
+
{/* Search Input */}
|
|
203
|
+
<motion.div variants={itemVariants} className="space-y-4">
|
|
204
|
+
<div className="relative">
|
|
205
|
+
<Input
|
|
206
|
+
type="text"
|
|
207
|
+
placeholder="Search by document name, session, or path..."
|
|
208
|
+
value={searchQuery}
|
|
209
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
210
|
+
className="w-full pl-10 pr-12 h-12 text-lg"
|
|
211
|
+
autoFocus
|
|
212
|
+
/>
|
|
213
|
+
<SearchIcon className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-muted-foreground" />
|
|
214
|
+
<Button
|
|
215
|
+
variant={showFilters ? 'default' : 'ghost'}
|
|
216
|
+
size="icon"
|
|
217
|
+
className="absolute right-2 top-1/2 -translate-y-1/2"
|
|
218
|
+
onClick={() => setShowFilters(!showFilters)}
|
|
219
|
+
>
|
|
220
|
+
<Filter className="w-4 h-4" />
|
|
221
|
+
</Button>
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
{/* Filters */}
|
|
225
|
+
{showFilters && (
|
|
226
|
+
<motion.div
|
|
227
|
+
initial={{ opacity: 0, height: 0 }}
|
|
228
|
+
animate={{ opacity: 1, height: 'auto' }}
|
|
229
|
+
exit={{ opacity: 0, height: 0 }}
|
|
230
|
+
className="grid grid-cols-1 md:grid-cols-2 gap-4 p-4 bg-muted/30 rounded-lg border border-border"
|
|
231
|
+
>
|
|
232
|
+
{/* Status Filter */}
|
|
233
|
+
<div className="space-y-2">
|
|
234
|
+
<label className="text-sm font-medium">Status</label>
|
|
235
|
+
<div className="flex gap-2 flex-wrap">
|
|
236
|
+
{statusOptions.map((option) => (
|
|
237
|
+
<Button
|
|
238
|
+
key={option.value}
|
|
239
|
+
variant={statusFilter === option.value ? 'default' : 'outline'}
|
|
240
|
+
size="sm"
|
|
241
|
+
onClick={() => setStatusFilter(option.value)}
|
|
242
|
+
className="gap-2"
|
|
243
|
+
>
|
|
244
|
+
{option.label}
|
|
245
|
+
<span className="text-xs opacity-70">({option.count})</span>
|
|
246
|
+
</Button>
|
|
247
|
+
))}
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
{/* Session Filter */}
|
|
252
|
+
<div className="space-y-2">
|
|
253
|
+
<label className="text-sm font-medium">Session</label>
|
|
254
|
+
<select
|
|
255
|
+
value={selectedSessionId}
|
|
256
|
+
onChange={(e) => setSelectedSessionId(e.target.value)}
|
|
257
|
+
className="w-full h-9 px-3 rounded-md border border-input bg-background"
|
|
258
|
+
>
|
|
259
|
+
<option value="all">All Sessions</option>
|
|
260
|
+
{sessions.map((session) => (
|
|
261
|
+
<option key={session.id} value={session.id}>
|
|
262
|
+
{session.name} ({session.documents.length})
|
|
263
|
+
</option>
|
|
264
|
+
))}
|
|
265
|
+
</select>
|
|
266
|
+
</div>
|
|
267
|
+
</motion.div>
|
|
268
|
+
)}
|
|
269
|
+
</motion.div>
|
|
270
|
+
|
|
271
|
+
{/* Results Summary */}
|
|
272
|
+
<motion.div variants={itemVariants} className="flex items-center justify-between">
|
|
273
|
+
<p className="text-sm text-foreground">
|
|
274
|
+
{filteredDocuments.length} {filteredDocuments.length === 1 ? 'result' : 'results'} found
|
|
275
|
+
{searchQuery && (
|
|
276
|
+
<span className="ml-1">
|
|
277
|
+
for "<span className="font-semibold text-foreground">{searchQuery}</span>"
|
|
278
|
+
</span>
|
|
279
|
+
)}
|
|
280
|
+
</p>
|
|
281
|
+
<p className="text-xs text-muted-foreground">
|
|
282
|
+
Use <kbd className="px-1.5 py-0.5 bg-muted rounded text-xs">↑</kbd>
|
|
283
|
+
<kbd className="px-1.5 py-0.5 bg-muted rounded text-xs ml-1">↓</kbd> to navigate,
|
|
284
|
+
<kbd className="px-1.5 py-0.5 bg-muted rounded text-xs ml-1">Enter</kbd> to open
|
|
285
|
+
</p>
|
|
286
|
+
</motion.div>
|
|
287
|
+
|
|
288
|
+
{/* Results */}
|
|
289
|
+
<motion.div variants={itemVariants} className="space-y-2" ref={resultsRef}>
|
|
290
|
+
{filteredDocuments.length === 0 ? (
|
|
291
|
+
<Card className="border-dashed">
|
|
292
|
+
<CardContent className="pt-12 pb-12 text-center">
|
|
293
|
+
<SearchIcon className="w-12 h-12 mx-auto mb-4 text-muted-foreground opacity-50" />
|
|
294
|
+
<p className="text-lg font-medium mb-1">No documents found</p>
|
|
295
|
+
<p className="text-sm text-muted-foreground">
|
|
296
|
+
Try adjusting your search query or filters
|
|
297
|
+
</p>
|
|
298
|
+
</CardContent>
|
|
299
|
+
</Card>
|
|
300
|
+
) : (
|
|
301
|
+
filteredDocuments.map((doc, index) => (
|
|
302
|
+
<motion.div
|
|
303
|
+
key={`${doc.sessionId}-${doc.id}`}
|
|
304
|
+
data-index={index}
|
|
305
|
+
variants={itemVariants}
|
|
306
|
+
onClick={() => handleOpenDocument(doc)}
|
|
307
|
+
onMouseEnter={() => setSelectedIndex(index)}
|
|
308
|
+
className={cn(
|
|
309
|
+
'p-4 rounded-lg border cursor-pointer transition-all duration-150',
|
|
310
|
+
'hover:border-primary hover:bg-accent/50',
|
|
311
|
+
selectedIndex === index
|
|
312
|
+
? 'border-primary bg-accent/50 shadow-xs'
|
|
313
|
+
: 'border-border bg-card'
|
|
314
|
+
)}
|
|
315
|
+
>
|
|
316
|
+
<div className="flex items-start justify-between gap-4">
|
|
317
|
+
<div className="flex-1 min-w-0">
|
|
318
|
+
<div className="flex items-center gap-2 mb-1">
|
|
319
|
+
{getStatusIcon(doc.status)}
|
|
320
|
+
<h3 className="font-medium truncate">{doc.name}</h3>
|
|
321
|
+
</div>
|
|
322
|
+
<div className="flex items-center gap-4 text-sm text-foreground">
|
|
323
|
+
<span className="flex items-center gap-1">
|
|
324
|
+
<FolderOpen className="w-3.5 h-3.5" />
|
|
325
|
+
{doc.sessionName}
|
|
326
|
+
</span>
|
|
327
|
+
{doc.processedAt && (
|
|
328
|
+
<span className="flex items-center gap-1">
|
|
329
|
+
<Calendar className="w-3.5 h-3.5" />
|
|
330
|
+
{new Date(doc.processedAt).toLocaleDateString()}
|
|
331
|
+
</span>
|
|
332
|
+
)}
|
|
333
|
+
</div>
|
|
334
|
+
<p className="text-xs text-muted-foreground mt-1 truncate">{doc.path}</p>
|
|
335
|
+
</div>
|
|
336
|
+
<ChevronRight
|
|
337
|
+
className={cn(
|
|
338
|
+
'w-5 h-5 text-muted-foreground transition-transform',
|
|
339
|
+
selectedIndex === index && 'text-primary translate-x-1'
|
|
340
|
+
)}
|
|
341
|
+
/>
|
|
342
|
+
</div>
|
|
343
|
+
</motion.div>
|
|
344
|
+
))
|
|
345
|
+
)}
|
|
346
|
+
</motion.div>
|
|
347
|
+
</motion.div>
|
|
348
|
+
);
|
|
349
|
+
});
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { Button } from '@/components/common/Button';
|
|
2
|
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/common/Card';
|
|
3
|
+
import { Input } from '@/components/common/Input';
|
|
4
|
+
import { SessionManager } from '@/components/sessions/SessionManager';
|
|
5
|
+
import { useSession } from '@/contexts/SessionContext';
|
|
6
|
+
import { cn } from '@/utils/cn';
|
|
7
|
+
import { motion } from 'framer-motion';
|
|
8
|
+
import { Calendar, Clock, FileText, FolderOpen, Grid, List, Plus, Trash2 } from 'lucide-react';
|
|
9
|
+
import { useState } from 'react';
|
|
10
|
+
import { useNavigate } from 'react-router-dom';
|
|
11
|
+
|
|
12
|
+
const containerVariants = {
|
|
13
|
+
hidden: { opacity: 0 },
|
|
14
|
+
visible: {
|
|
15
|
+
opacity: 1,
|
|
16
|
+
transition: {
|
|
17
|
+
staggerChildren: 0.1,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const itemVariants = {
|
|
23
|
+
hidden: { opacity: 0, y: 20 },
|
|
24
|
+
visible: {
|
|
25
|
+
opacity: 1,
|
|
26
|
+
y: 0,
|
|
27
|
+
transition: {
|
|
28
|
+
duration: 0.5,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export function Sessions() {
|
|
34
|
+
const navigate = useNavigate();
|
|
35
|
+
const { sessions, loadSession, deleteSession } = useSession();
|
|
36
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
37
|
+
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');
|
|
38
|
+
const [showSessionManager, setShowSessionManager] = useState(false);
|
|
39
|
+
const [showDeleteConfirm, setShowDeleteConfirm] = useState<string | null>(null);
|
|
40
|
+
|
|
41
|
+
const filteredSessions = sessions.filter((session) =>
|
|
42
|
+
session.name.toLowerCase().includes(searchQuery.toLowerCase())
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const handleSessionClick = (sessionId: string) => {
|
|
46
|
+
loadSession(sessionId);
|
|
47
|
+
navigate(`/session/${sessionId}`);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const handleDeleteSession = (sessionId: string, e: React.MouseEvent) => {
|
|
51
|
+
e.stopPropagation();
|
|
52
|
+
setShowDeleteConfirm(sessionId);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const confirmDelete = (sessionId: string) => {
|
|
56
|
+
deleteSession(sessionId);
|
|
57
|
+
setShowDeleteConfirm(null);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const formatDate = (date: Date) => {
|
|
61
|
+
return new Intl.DateTimeFormat('en-US', {
|
|
62
|
+
month: 'short',
|
|
63
|
+
day: 'numeric',
|
|
64
|
+
year: 'numeric',
|
|
65
|
+
}).format(date);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const handleNewSession = () => {
|
|
69
|
+
setShowSessionManager(true);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const handleSessionCreated = (sessionId: string) => {
|
|
73
|
+
navigate(`/session/${sessionId}`);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<motion.div
|
|
78
|
+
className="p-6 space-y-6"
|
|
79
|
+
variants={containerVariants}
|
|
80
|
+
initial="hidden"
|
|
81
|
+
animate="visible"
|
|
82
|
+
>
|
|
83
|
+
<motion.div
|
|
84
|
+
className="flex flex-col sm:flex-row gap-4 justify-between"
|
|
85
|
+
variants={itemVariants}
|
|
86
|
+
>
|
|
87
|
+
<Input
|
|
88
|
+
type="search"
|
|
89
|
+
placeholder="Search sessions..."
|
|
90
|
+
value={searchQuery}
|
|
91
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
92
|
+
onClear={() => setSearchQuery('')}
|
|
93
|
+
className="max-w-md"
|
|
94
|
+
/>
|
|
95
|
+
|
|
96
|
+
<div className="flex gap-2">
|
|
97
|
+
<div className="flex rounded-md border border-border">
|
|
98
|
+
<button
|
|
99
|
+
onClick={() => setViewMode('grid')}
|
|
100
|
+
className={cn(
|
|
101
|
+
'p-2 rounded-l-md transition-colors',
|
|
102
|
+
viewMode === 'grid' ? 'bg-primary text-primary-foreground' : 'hover:bg-muted'
|
|
103
|
+
)}
|
|
104
|
+
aria-label="Grid view"
|
|
105
|
+
>
|
|
106
|
+
<Grid className="w-4 h-4" />
|
|
107
|
+
</button>
|
|
108
|
+
<button
|
|
109
|
+
onClick={() => setViewMode('list')}
|
|
110
|
+
className={cn(
|
|
111
|
+
'p-2 rounded-r-md transition-colors',
|
|
112
|
+
viewMode === 'list' ? 'bg-primary text-primary-foreground' : 'hover:bg-muted'
|
|
113
|
+
)}
|
|
114
|
+
aria-label="List view"
|
|
115
|
+
>
|
|
116
|
+
<List className="w-4 h-4" />
|
|
117
|
+
</button>
|
|
118
|
+
</div>
|
|
119
|
+
<Button icon={<Plus className="w-4 h-4" />} onClick={handleNewSession}>
|
|
120
|
+
New Session
|
|
121
|
+
</Button>
|
|
122
|
+
</div>
|
|
123
|
+
</motion.div>
|
|
124
|
+
|
|
125
|
+
<motion.div
|
|
126
|
+
className={cn(
|
|
127
|
+
'grid gap-4',
|
|
128
|
+
viewMode === 'grid' ? 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3' : 'grid-cols-1'
|
|
129
|
+
)}
|
|
130
|
+
variants={containerVariants}
|
|
131
|
+
>
|
|
132
|
+
{filteredSessions.map((session) => (
|
|
133
|
+
<motion.div key={session.id} variants={itemVariants}>
|
|
134
|
+
<Card
|
|
135
|
+
interactive
|
|
136
|
+
variant="bordered"
|
|
137
|
+
className="cursor-pointer relative group"
|
|
138
|
+
onClick={() => handleSessionClick(session.id)}
|
|
139
|
+
>
|
|
140
|
+
<CardHeader>
|
|
141
|
+
<div className="flex items-start justify-between">
|
|
142
|
+
<div className="flex items-center gap-3">
|
|
143
|
+
<div className="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center">
|
|
144
|
+
<FolderOpen className="w-5 h-5 text-primary" />
|
|
145
|
+
</div>
|
|
146
|
+
<div>
|
|
147
|
+
<CardTitle className="text-xl font-semibold">{session.name}</CardTitle>
|
|
148
|
+
<span
|
|
149
|
+
className={cn(
|
|
150
|
+
'text-xs px-2 py-1 rounded-full mt-1 inline-block',
|
|
151
|
+
session.status === 'active'
|
|
152
|
+
? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400'
|
|
153
|
+
: 'bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400'
|
|
154
|
+
)}
|
|
155
|
+
>
|
|
156
|
+
{session.status}
|
|
157
|
+
</span>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
<div className="opacity-0 group-hover:opacity-100 transition-opacity">
|
|
161
|
+
<button
|
|
162
|
+
onClick={(e) => handleDeleteSession(session.id, e)}
|
|
163
|
+
className="p-1 hover:bg-muted rounded"
|
|
164
|
+
>
|
|
165
|
+
<Trash2 className="w-4 h-4 text-muted-foreground hover:text-destructive" />
|
|
166
|
+
</button>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
</CardHeader>
|
|
170
|
+
<CardContent>
|
|
171
|
+
<div className="space-y-3">
|
|
172
|
+
<div className="grid grid-cols-2 gap-2 text-base">
|
|
173
|
+
<div className="flex items-center gap-1 text-foreground">
|
|
174
|
+
<FileText className="w-3 h-3" />
|
|
175
|
+
<span>{session.documents.length} documents</span>
|
|
176
|
+
</div>
|
|
177
|
+
<div className="flex items-center gap-1 text-foreground">
|
|
178
|
+
<Clock className="w-3 h-3" />
|
|
179
|
+
<span>{session.stats.timeSaved}m saved</span>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
<div className="pt-2 border-t border-border">
|
|
184
|
+
<div className="flex items-center justify-between text-sm text-muted-foreground">
|
|
185
|
+
<div className="flex items-center gap-1">
|
|
186
|
+
<Calendar className="w-3 h-3" />
|
|
187
|
+
<span>Created {formatDate(session.createdAt)}</span>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
<div className="flex items-center justify-between text-sm text-muted-foreground mt-1">
|
|
191
|
+
<span>Last modified {formatDate(session.lastModified)}</span>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
{viewMode === 'list' && (
|
|
196
|
+
<div className="pt-2 grid grid-cols-4 gap-2 text-sm">
|
|
197
|
+
<div>
|
|
198
|
+
<p className="text-muted-foreground">Processed</p>
|
|
199
|
+
<p className="font-medium">{session.stats.documentsProcessed}</p>
|
|
200
|
+
</div>
|
|
201
|
+
<div>
|
|
202
|
+
<p className="text-muted-foreground">Links</p>
|
|
203
|
+
<p className="font-medium">{session.stats.hyperlinksChecked}</p>
|
|
204
|
+
</div>
|
|
205
|
+
<div>
|
|
206
|
+
<p className="text-muted-foreground">Feedback</p>
|
|
207
|
+
<p className="font-medium">{session.stats.feedbackImported}</p>
|
|
208
|
+
</div>
|
|
209
|
+
<div>
|
|
210
|
+
<p className="text-muted-foreground">Time</p>
|
|
211
|
+
<p className="font-medium">{session.stats.timeSaved}m</p>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
)}
|
|
215
|
+
</div>
|
|
216
|
+
</CardContent>
|
|
217
|
+
</Card>
|
|
218
|
+
|
|
219
|
+
{/* Delete Confirmation Dialog */}
|
|
220
|
+
{showDeleteConfirm === session.id && (
|
|
221
|
+
<motion.div
|
|
222
|
+
initial={{ opacity: 0 }}
|
|
223
|
+
animate={{ opacity: 1 }}
|
|
224
|
+
className="fixed inset-0 bg-background/80 backdrop-blur-xs z-50 flex items-center justify-center p-4"
|
|
225
|
+
onClick={() => setShowDeleteConfirm(null)}
|
|
226
|
+
>
|
|
227
|
+
<motion.div
|
|
228
|
+
initial={{ scale: 0.95 }}
|
|
229
|
+
animate={{ scale: 1 }}
|
|
230
|
+
className="bg-card rounded-lg shadow-xl border border-border p-6 max-w-sm"
|
|
231
|
+
onClick={(e) => e.stopPropagation()}
|
|
232
|
+
>
|
|
233
|
+
<h3 className="text-lg font-semibold mb-2">Delete Session</h3>
|
|
234
|
+
<p className="text-sm text-muted-foreground mb-4">
|
|
235
|
+
Are you sure you want to delete "{session.name}"? This action cannot be undone.
|
|
236
|
+
</p>
|
|
237
|
+
<div className="flex gap-2 justify-end">
|
|
238
|
+
<Button variant="outline" size="sm" onClick={() => setShowDeleteConfirm(null)}>
|
|
239
|
+
Cancel
|
|
240
|
+
</Button>
|
|
241
|
+
<Button
|
|
242
|
+
variant="destructive"
|
|
243
|
+
size="sm"
|
|
244
|
+
onClick={() => confirmDelete(session.id)}
|
|
245
|
+
>
|
|
246
|
+
Delete
|
|
247
|
+
</Button>
|
|
248
|
+
</div>
|
|
249
|
+
</motion.div>
|
|
250
|
+
</motion.div>
|
|
251
|
+
)}
|
|
252
|
+
</motion.div>
|
|
253
|
+
))}
|
|
254
|
+
</motion.div>
|
|
255
|
+
|
|
256
|
+
{filteredSessions.length === 0 && (
|
|
257
|
+
<motion.div variants={itemVariants} className="text-center py-12">
|
|
258
|
+
<FolderOpen className="w-12 h-12 mx-auto text-muted-foreground mb-4" />
|
|
259
|
+
<h3 className="text-lg font-medium mb-2">
|
|
260
|
+
{searchQuery ? 'No sessions found' : 'No sessions yet'}
|
|
261
|
+
</h3>
|
|
262
|
+
<p className="text-muted-foreground mb-4">
|
|
263
|
+
{searchQuery
|
|
264
|
+
? 'Try adjusting your search query'
|
|
265
|
+
: 'Create your first session to start processing documents'}
|
|
266
|
+
</p>
|
|
267
|
+
{!searchQuery && (
|
|
268
|
+
<Button onClick={handleNewSession} icon={<Plus className="w-4 h-4" />}>
|
|
269
|
+
Create First Session
|
|
270
|
+
</Button>
|
|
271
|
+
)}
|
|
272
|
+
</motion.div>
|
|
273
|
+
)}
|
|
274
|
+
|
|
275
|
+
{showSessionManager && (
|
|
276
|
+
<SessionManager
|
|
277
|
+
mode="new"
|
|
278
|
+
onClose={() => setShowSessionManager(false)}
|
|
279
|
+
onSessionCreated={handleSessionCreated}
|
|
280
|
+
onSessionLoaded={handleSessionCreated}
|
|
281
|
+
/>
|
|
282
|
+
)}
|
|
283
|
+
</motion.div>
|
|
284
|
+
);
|
|
285
|
+
}
|