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.
Files changed (271) hide show
  1. package/.eslintrc.json +43 -0
  2. package/.github/workflows/build.yml +64 -0
  3. package/.github/workflows/ci.yml +39 -0
  4. package/.vscode/extensions.json +3 -0
  5. package/Current.md +97 -0
  6. package/DocHub_Image.png +0 -0
  7. package/README.md +666 -0
  8. package/USER_GUIDE.md +1173 -0
  9. package/Updater.md +311 -0
  10. package/build/256x256.png +0 -0
  11. package/build/512x512.png +0 -0
  12. package/build/app-update.yml +4 -0
  13. package/build/create-icon.js +208 -0
  14. package/build/icon.ico +0 -0
  15. package/build/icon.png +0 -0
  16. package/build/icon_1024x1024.png +0 -0
  17. package/dist/assets/Analytics-BpsG9895.js +1 -0
  18. package/dist/assets/Card-IAZin8kp.js +1 -0
  19. package/dist/assets/CurrentSession-B-rFkHvf.js +12 -0
  20. package/dist/assets/Dashboard-C_5gMb0q.js +1 -0
  21. package/dist/assets/Documents-CqZ25axS.js +1 -0
  22. package/dist/assets/Input-l89xwXBi.js +1 -0
  23. package/dist/assets/Reporting-DqdHJY_a.js +1 -0
  24. package/dist/assets/Search-XNbu5z_3.js +1 -0
  25. package/dist/assets/SessionManager-lH9hZfzH.js +1 -0
  26. package/dist/assets/Sessions-ClZOPYNc.js +1 -0
  27. package/dist/assets/Settings-DUEHGURa.js +11 -0
  28. package/dist/assets/index-8xUe8ptc.js +24 -0
  29. package/dist/assets/index-RYyJqF7O.css +1 -0
  30. package/dist/assets/path-BkOl0AGO.js +1 -0
  31. package/dist/assets/promises-ID_B9S-h.js +1 -0
  32. package/dist/assets/urlHelpers-TvgahX0r.js +1 -0
  33. package/dist/assets/useToast-yRSO1dkm.js +1 -0
  34. package/dist/assets/vendor-charts-RkGK5ROP.js +36 -0
  35. package/dist/assets/vendor-db-l0sNRNKZ.js +1 -0
  36. package/dist/assets/vendor-react-BVZ_anCF.js +4 -0
  37. package/dist/assets/vendor-search-Dw8P0qyA.js +1 -0
  38. package/dist/assets/vendor-ui-BU7NfluV.js +53 -0
  39. package/dist/electron/PowerAutomateApiService-LfW09ZGr.js +147 -0
  40. package/dist/electron/main-CXkNtyv-.js +19789 -0
  41. package/dist/electron/main.js +5 -0
  42. package/dist/electron/preload.js +1 -0
  43. package/dist/icon.png +0 -0
  44. package/dist/index.html +27 -0
  45. package/docs/CODEBASE_ANALYSIS_REPORT.md +309 -0
  46. package/docs/DEBUG_LOGGING_GUIDE.md +244 -0
  47. package/docs/README.md +115 -0
  48. package/docs/TOC_WIRING_GUIDE.md +344 -0
  49. package/docs/analysis/Bullet_Symbol_Bug_Analysis.md +136 -0
  50. package/docs/analysis/DOCXMLATER_ANALYSIS_SUMMARY.txt +169 -0
  51. package/docs/analysis/Document_Processing_Issues_Analysis.md +704 -0
  52. package/docs/analysis/FIELD_PRESERVATION_ANALYSIS.md +1200 -0
  53. package/docs/analysis/INDENTATION_PRESERVE_ANALYSIS.md +181 -0
  54. package/docs/analysis/INDENTATION_PRESERVE_IMPLEMENTATION.md +207 -0
  55. package/docs/analysis/List_Implementation.md +206 -0
  56. package/docs/analysis/List_Implementation_Accuracy_Report.md +366 -0
  57. package/docs/analysis/PROCESSING_OPTIONS_UI_UPDATES.md +220 -0
  58. package/docs/analysis/RefactorStyles.md +852 -0
  59. package/docs/analysis/STYLE_PARAMETER_ENHANCEMENT.md +143 -0
  60. package/docs/analysis/docxmlater-comparison-todo-2025-11-13.md +636 -0
  61. package/docs/analysis/docxmlater-implementation-analysis-2025-11-13.md +340 -0
  62. package/docs/analysis/docxmlater-template_ui-integration-analysis.md +263 -0
  63. package/docs/analysis/github-issues-to-create.md +237 -0
  64. package/docs/api/API_README.md +538 -0
  65. package/docs/api/API_REFERENCE.md +751 -0
  66. package/docs/api/TYPE_DEFINITIONS.md +869 -0
  67. package/docs/architecture/FONT_EMBEDDING_GUIDE.md +318 -0
  68. package/docs/architecture/docxmlater-functions-and-structure.md +726 -0
  69. package/docs/docxmlater-readme.md +1341 -0
  70. package/docs/fixes/EXECUTION_LOG_TEST_BASE.md +573 -0
  71. package/docs/fixes/HYPERLINK_TEXT_SANITIZATION.md +253 -0
  72. package/docs/fixes/README.md +37 -0
  73. package/docs/github-issues/issue-1-body.md +125 -0
  74. package/docs/github-issues/issue-10-body.md +850 -0
  75. package/docs/github-issues/issue-2-body.md +200 -0
  76. package/docs/github-issues/issue-3-body.md +270 -0
  77. package/docs/github-issues/issue-4-body.md +169 -0
  78. package/docs/github-issues/issue-5-body.md +173 -0
  79. package/docs/github-issues/issue-6-body.md +158 -0
  80. package/docs/github-issues/issue-7-body.md +171 -0
  81. package/docs/github-issues/issue-8-body.md +407 -0
  82. package/docs/github-issues/issue-9-body.md +515 -0
  83. package/docs/github-issues/issue-tracker.md +274 -0
  84. package/docs/github-issues/predictive-analysis-2025-10-18.md +2131 -0
  85. package/docs/implementation/List_Framework_Refactor_Plan.md +336 -0
  86. package/docs/implementation/PRIMARY_TEXT_COLOR_FEATURE.md +217 -0
  87. package/docs/implementation/RELEASE_PLAN_v2.1.0.md +362 -0
  88. package/docs/implementation/RefactorStyles.md +588 -0
  89. package/docs/implementation/implement-plan.md +489 -0
  90. package/docs/implementation/missing-helpers-implementation.md +391 -0
  91. package/docs/implementation/refactor-plan.md +520 -0
  92. package/docs/implementation/session-implementation-complete.md +233 -0
  93. package/docs/implementation/session-management-plan.md +250 -0
  94. package/docs/setup-checklist.md +77 -0
  95. package/docs/versions/changelog.md +345 -0
  96. package/electron/customUpdater.ts +656 -0
  97. package/electron/main.ts +2441 -0
  98. package/electron/memoryConfig.ts +187 -0
  99. package/electron/preload.ts +394 -0
  100. package/electron/proxyConfig.ts +340 -0
  101. package/electron/services/BackupService.ts +452 -0
  102. package/electron/services/DictionaryService.ts +402 -0
  103. package/electron/services/LocalDictionaryLookupService.ts +147 -0
  104. package/electron/services/PowerAutomateApiService.ts +231 -0
  105. package/electron/services/SharePointSyncService.ts +474 -0
  106. package/electron/windowsCertStore.ts +427 -0
  107. package/electron/zscalerConfig.ts +381 -0
  108. package/eslint.config.js +92 -0
  109. package/jest.config.js +52 -0
  110. package/package.json +214 -0
  111. package/postcss.config.mjs +6 -0
  112. package/public/icon.png +0 -0
  113. package/publish-release.ps1 +5 -0
  114. package/renovate.json +30 -0
  115. package/src/App.tsx +216 -0
  116. package/src/__mocks__/p-limit.js +12 -0
  117. package/src/__mocks__/styleMock.js +1 -0
  118. package/src/components/common/BugReportButton.tsx +44 -0
  119. package/src/components/common/BugReportDialog.tsx +193 -0
  120. package/src/components/common/Button.tsx +153 -0
  121. package/src/components/common/Card.tsx +86 -0
  122. package/src/components/common/ColorPickerDialog.tsx +177 -0
  123. package/src/components/common/ConfirmDialog.tsx +96 -0
  124. package/src/components/common/DebugConsole.tsx +275 -0
  125. package/src/components/common/EmptyState.tsx +183 -0
  126. package/src/components/common/ErrorBoundary.tsx +98 -0
  127. package/src/components/common/ErrorDetailsDialog.tsx +153 -0
  128. package/src/components/common/ErrorFallback.tsx +218 -0
  129. package/src/components/common/Input.tsx +109 -0
  130. package/src/components/common/Skeleton.tsx +184 -0
  131. package/src/components/common/SplashScreen.tsx +81 -0
  132. package/src/components/common/Toast.tsx +155 -0
  133. package/src/components/common/Tooltip.tsx +79 -0
  134. package/src/components/common/UpdateNotification.tsx +320 -0
  135. package/src/components/comparison/ComparisonWindow.tsx +374 -0
  136. package/src/components/comparison/SideBySideDiff.tsx +486 -0
  137. package/src/components/comparison/index.ts +8 -0
  138. package/src/components/document/DocumentUploader.tsx +288 -0
  139. package/src/components/document/HyperlinkPreview.tsx +430 -0
  140. package/src/components/document/HyperlinkService.md +1484 -0
  141. package/src/components/document/Hyperlink_Technical_Documentation.md +496 -0
  142. package/src/components/document/InlineChangesView.tsx +707 -0
  143. package/src/components/document/ProcessingProgress.tsx +303 -0
  144. package/src/components/document/ProcessingResults.tsx +256 -0
  145. package/src/components/document/TrackedChangesDetail.tsx +530 -0
  146. package/src/components/document/TrackedChangesPanel.tsx +546 -0
  147. package/src/components/document/VirtualDocumentList.tsx +240 -0
  148. package/src/components/editor/DocumentEditor.tsx +723 -0
  149. package/src/components/editor/DocumentEditorModal.tsx +640 -0
  150. package/src/components/editor/EditorQuickActions.tsx +502 -0
  151. package/src/components/editor/EditorToolbar.tsx +312 -0
  152. package/src/components/editor/TableEditor.tsx +926 -0
  153. package/src/components/editor/index.ts +18 -0
  154. package/src/components/layout/Header.tsx +190 -0
  155. package/src/components/layout/Sidebar.tsx +313 -0
  156. package/src/components/layout/TitleBar.tsx +190 -0
  157. package/src/components/navigation/CommandPalette.tsx +233 -0
  158. package/src/components/navigation/KeyboardShortcutsModal.tsx +173 -0
  159. package/src/components/sessions/ChangeItem.tsx +408 -0
  160. package/src/components/sessions/ChangeViewer.tsx +1155 -0
  161. package/src/components/sessions/DocumentComparisonModal.tsx +314 -0
  162. package/src/components/sessions/ProcessingOptions.tsx +297 -0
  163. package/src/components/sessions/ReplacementsTab.tsx +438 -0
  164. package/src/components/sessions/RevisionHandlingOptions.tsx +87 -0
  165. package/src/components/sessions/SessionManager.tsx +188 -0
  166. package/src/components/sessions/StylesEditor.tsx +1335 -0
  167. package/src/components/sessions/TabContainer.tsx +151 -0
  168. package/src/components/sessions/VirtualSessionList.tsx +157 -0
  169. package/src/components/sessions/sessionToProcessorManager.tsx +420 -0
  170. package/src/components/settings/CertificateManager.tsx +410 -0
  171. package/src/components/settings/SegmentedControl.tsx +88 -0
  172. package/src/components/settings/SettingRow.tsx +52 -0
  173. package/src/contexts/GlobalStatsContext.tsx +396 -0
  174. package/src/contexts/SessionContext.tsx +2129 -0
  175. package/src/contexts/ThemeContext.tsx +428 -0
  176. package/src/contexts/UserSettingsContext.tsx +290 -0
  177. package/src/contexts/__tests__/GlobalStatsContext.test.tsx +390 -0
  178. package/src/global.d.ts +273 -0
  179. package/src/hooks/useDocumentQueue.tsx +210 -0
  180. package/src/hooks/useToast.tsx +55 -0
  181. package/src/main.tsx +10 -0
  182. package/src/pages/Analytics.tsx +386 -0
  183. package/src/pages/CurrentSession.tsx +1174 -0
  184. package/src/pages/Dashboard.tsx +319 -0
  185. package/src/pages/Documents.tsx +317 -0
  186. package/src/pages/Projects.tsx +250 -0
  187. package/src/pages/Reporting.tsx +386 -0
  188. package/src/pages/Search.tsx +349 -0
  189. package/src/pages/Sessions.tsx +285 -0
  190. package/src/pages/Settings.tsx +2662 -0
  191. package/src/services/HyperlinkService.ts +1085 -0
  192. package/src/services/document/DocXMLaterProcessor.ts +617 -0
  193. package/src/services/document/DocumentProcessingComparison.ts +856 -0
  194. package/src/services/document/DocumentSnapshotService.ts +575 -0
  195. package/src/services/document/WordDocumentProcessor.ts +10509 -0
  196. package/src/services/document/__tests__/DocXMLaterProcessor.hyperlinks.test.md +311 -0
  197. package/src/services/document/__tests__/WordDocumentProcessor.integration.test.ts +515 -0
  198. package/src/services/document/__tests__/WordDocumentProcessor.test.ts +812 -0
  199. package/src/services/document/blanklines/BlankLineManager.ts +658 -0
  200. package/src/services/document/blanklines/__tests__/paragraphChecks.test.ts +281 -0
  201. package/src/services/document/blanklines/helpers/blankLineInsertion.ts +87 -0
  202. package/src/services/document/blanklines/helpers/blankLineSnapshot.ts +251 -0
  203. package/src/services/document/blanklines/helpers/clearCustom.ts +121 -0
  204. package/src/services/document/blanklines/helpers/contextChecks.ts +117 -0
  205. package/src/services/document/blanklines/helpers/imageChecks.ts +51 -0
  206. package/src/services/document/blanklines/helpers/paragraphChecks.ts +236 -0
  207. package/src/services/document/blanklines/helpers/removeBlanksBetweenListItems.ts +91 -0
  208. package/src/services/document/blanklines/helpers/removeTrailingBlanks.ts +35 -0
  209. package/src/services/document/blanklines/helpers/tableGuards.ts +21 -0
  210. package/src/services/document/blanklines/index.ts +67 -0
  211. package/src/services/document/blanklines/rules/additionRules.ts +337 -0
  212. package/src/services/document/blanklines/rules/indentationRules.ts +317 -0
  213. package/src/services/document/blanklines/rules/removalRules.ts +362 -0
  214. package/src/services/document/blanklines/rules/ruleTypes.ts +92 -0
  215. package/src/services/document/blanklines/types.ts +29 -0
  216. package/src/services/document/helpers/ImageBorderCropper.ts +377 -0
  217. package/src/services/document/helpers/__tests__/whitespace.test.ts +272 -0
  218. package/src/services/document/helpers/whitespace.ts +117 -0
  219. package/src/services/document/list/ListNormalizer.ts +947 -0
  220. package/src/services/document/list/index.ts +45 -0
  221. package/src/services/document/list/list-detection.ts +275 -0
  222. package/src/services/document/list/list-types.ts +162 -0
  223. package/src/services/document/processors/HyperlinkProcessor.ts +370 -0
  224. package/src/services/document/processors/ListProcessor.ts +257 -0
  225. package/src/services/document/processors/StructureProcessor.ts +176 -0
  226. package/src/services/document/processors/StyleProcessor.ts +389 -0
  227. package/src/services/document/processors/TableProcessor.ts +2238 -0
  228. package/src/services/document/processors/__tests__/HyperlinkProcessor.test.ts +314 -0
  229. package/src/services/document/processors/__tests__/ListProcessor.test.ts +291 -0
  230. package/src/services/document/processors/__tests__/StructureProcessor.test.ts +257 -0
  231. package/src/services/document/processors/__tests__/TableProcessor.hlp-tips-bullets.test.ts +459 -0
  232. package/src/services/document/processors/__tests__/TableProcessor.test.ts +1604 -0
  233. package/src/services/document/processors/index.ts +28 -0
  234. package/src/services/document/types/docx-processing.ts +310 -0
  235. package/src/services/editor/EditorActionHandlers.ts +901 -0
  236. package/src/services/editor/index.ts +13 -0
  237. package/src/setupTests.ts +47 -0
  238. package/src/styles/global.css +782 -0
  239. package/src/types/backup.ts +132 -0
  240. package/src/types/dictionary.ts +125 -0
  241. package/src/types/document-processing.ts +331 -0
  242. package/src/types/docxmlater-augments.d.ts +142 -0
  243. package/src/types/editor.ts +280 -0
  244. package/src/types/electron.ts +340 -0
  245. package/src/types/globalStats.ts +155 -0
  246. package/src/types/hyperlink.ts +471 -0
  247. package/src/types/operations.ts +354 -0
  248. package/src/types/session.ts +427 -0
  249. package/src/types/settings.ts +112 -0
  250. package/src/utils/MemoryMonitor.ts +248 -0
  251. package/src/utils/cn.ts +6 -0
  252. package/src/utils/colorConvert.ts +306 -0
  253. package/src/utils/diffUtils.ts +347 -0
  254. package/src/utils/documentUtils.ts +202 -0
  255. package/src/utils/electronGuard.ts +62 -0
  256. package/src/utils/indexedDB.ts +915 -0
  257. package/src/utils/logger.ts +717 -0
  258. package/src/utils/pathSecurity.ts +232 -0
  259. package/src/utils/pathValidator.ts +236 -0
  260. package/src/utils/processingTimeEstimator.ts +153 -0
  261. package/src/utils/safeJsonParse.ts +62 -0
  262. package/src/utils/textSanitizer.ts +162 -0
  263. package/src/utils/urlHelpers.ts +304 -0
  264. package/src/utils/urlPatterns.ts +198 -0
  265. package/src/utils/urlSanitizer.ts +152 -0
  266. package/src/vite-env.d.ts +11 -0
  267. package/tsconfig.electron.json +19 -0
  268. package/tsconfig.json +36 -0
  269. package/tsconfig.node.json +12 -0
  270. package/typedoc.json +45 -0
  271. package/vite.config.ts +152 -0
@@ -0,0 +1,319 @@
1
+ import { Button } from '@/components/common/Button';
2
+ import {
3
+ Card,
4
+ CardContent,
5
+ CardDescription,
6
+ CardHeader,
7
+ CardTitle,
8
+ } from '@/components/common/Card';
9
+ import { SessionManager } from '@/components/sessions/SessionManager';
10
+ import { useGlobalStats } from '@/contexts/GlobalStatsContext';
11
+ import { useSession } from '@/contexts/SessionContext';
12
+ import { cn } from '@/utils/cn';
13
+ import { motion } from 'framer-motion';
14
+ import {
15
+ ArrowUpRight,
16
+ Calendar,
17
+ Clock,
18
+ FileCheck,
19
+ FileText,
20
+ FolderOpen,
21
+ Link,
22
+ MessageSquare,
23
+ Minus,
24
+ Plus,
25
+ TrendingDown,
26
+ TrendingUp,
27
+ } from 'lucide-react';
28
+ import { useState } from 'react';
29
+ import { useNavigate } from 'react-router-dom';
30
+
31
+ const containerVariants = {
32
+ hidden: { opacity: 0 },
33
+ visible: {
34
+ opacity: 1,
35
+ transition: {
36
+ staggerChildren: 0.1,
37
+ },
38
+ },
39
+ };
40
+
41
+ const itemVariants = {
42
+ hidden: { opacity: 0, y: 20 },
43
+ visible: {
44
+ opacity: 1,
45
+ y: 0,
46
+ transition: {
47
+ duration: 0.5,
48
+ },
49
+ },
50
+ };
51
+
52
+ export function Dashboard() {
53
+ const { recentSessions, loadSession } = useSession();
54
+ const { stats: globalStats, getTodayStats, getTodayChange } = useGlobalStats();
55
+ const navigate = useNavigate();
56
+ const [showSessionManager, setShowSessionManager] = useState(false);
57
+ const [sessionManagerMode, setSessionManagerMode] = useState<'new' | 'load'>('new');
58
+
59
+ // Get today's stats and changes from yesterday
60
+ const todayStats = getTodayStats();
61
+ const todayChange = getTodayChange();
62
+
63
+ const stats = [
64
+ {
65
+ title: 'Documents Processed',
66
+ value: globalStats.allTime.documentsProcessed.toString(),
67
+ todayValue: todayStats.documentsProcessed,
68
+ change: todayChange.documentsProcessed || 0,
69
+ icon: FileCheck,
70
+ gradient: 'from-green-400 to-emerald-600',
71
+ bgGradient: 'from-green-500/20 to-emerald-500/10',
72
+ },
73
+ {
74
+ title: 'Hyperlinks Checked',
75
+ value: globalStats.allTime.hyperlinksChecked.toString(),
76
+ todayValue: todayStats.hyperlinksChecked,
77
+ change: todayChange.hyperlinksChecked || 0,
78
+ icon: Link,
79
+ gradient: 'from-blue-400 to-indigo-600',
80
+ bgGradient: 'from-blue-500/20 to-indigo-500/10',
81
+ },
82
+ {
83
+ title: 'Feedback Imported',
84
+ value: globalStats.allTime.feedbackImported.toString(),
85
+ todayValue: todayStats.feedbackImported,
86
+ change: todayChange.feedbackImported || 0,
87
+ icon: MessageSquare,
88
+ gradient: 'from-purple-400 to-pink-600',
89
+ bgGradient: 'from-purple-500/20 to-pink-500/10',
90
+ },
91
+ {
92
+ title: 'Time Saved',
93
+ value: `${globalStats.allTime.timeSaved}m`,
94
+ todayValue: todayStats.timeSaved,
95
+ change: todayChange.timeSaved || 0,
96
+ icon: Clock,
97
+ gradient: 'from-orange-400 to-red-600',
98
+ bgGradient: 'from-orange-500/20 to-red-500/10',
99
+ },
100
+ ];
101
+
102
+ const handleNewSession = () => {
103
+ setSessionManagerMode('new');
104
+ setShowSessionManager(true);
105
+ };
106
+
107
+ const handleLoadSession = () => {
108
+ setSessionManagerMode('load');
109
+ setShowSessionManager(true);
110
+ };
111
+
112
+ const handleSessionCreated = (sessionId: string) => {
113
+ navigate(`/session/${sessionId}`);
114
+ };
115
+
116
+ const handleRecentSessionClick = (sessionId: string) => {
117
+ loadSession(sessionId);
118
+ navigate(`/session/${sessionId}`);
119
+ };
120
+
121
+ const formatDate = (date: Date) => {
122
+ const now = new Date();
123
+ const diff = now.getTime() - date.getTime();
124
+ const hours = Math.floor(diff / (1000 * 60 * 60));
125
+
126
+ if (hours < 1) {
127
+ const minutes = Math.floor(diff / (1000 * 60));
128
+ return `${minutes} minutes ago`;
129
+ } else if (hours < 24) {
130
+ return `${hours} hours ago`;
131
+ } else {
132
+ const days = Math.floor(hours / 24);
133
+ return `${days} day${days > 1 ? 's' : ''} ago`;
134
+ }
135
+ };
136
+
137
+ return (
138
+ <motion.div
139
+ className="p-6 space-y-6"
140
+ variants={containerVariants}
141
+ initial="hidden"
142
+ animate="visible"
143
+ >
144
+ <motion.div className="flex justify-end" variants={itemVariants}>
145
+ <div className="flex gap-2">
146
+ <Button
147
+ onClick={handleNewSession}
148
+ variant="default"
149
+ size="sm"
150
+ icon={<Plus className="w-4 h-4" />}
151
+ >
152
+ New Session
153
+ </Button>
154
+ <Button
155
+ onClick={handleLoadSession}
156
+ variant="outline"
157
+ size="sm"
158
+ icon={<FolderOpen className="w-4 h-4" />}
159
+ >
160
+ Load Session
161
+ </Button>
162
+ </div>
163
+ </motion.div>
164
+
165
+ <motion.div
166
+ className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"
167
+ variants={itemVariants}
168
+ >
169
+ {stats.map((stat) => {
170
+ const Icon = stat.icon;
171
+
172
+ return (
173
+ <motion.div key={stat.title} whileHover={{ y: -4 }} transition={{ duration: 0.2 }}>
174
+ <Card className="relative overflow-hidden group cursor-pointer border-0 shadow-lg hover:shadow-2xl transition-all">
175
+ <div
176
+ className={cn(
177
+ 'absolute inset-0 bg-linear-to-br opacity-5 group-hover:opacity-10 transition-opacity',
178
+ stat.bgGradient
179
+ )}
180
+ />
181
+ <CardContent className="p-6 relative">
182
+ <div className="flex items-center justify-between mb-4">
183
+ <div
184
+ className={cn('p-3 rounded-xl bg-linear-to-br', stat.gradient, 'shadow-lg')}
185
+ >
186
+ <Icon className="w-6 h-6 text-white" />
187
+ </div>
188
+ <motion.button
189
+ whileHover={{ scale: 1.1 }}
190
+ whileTap={{ scale: 0.95 }}
191
+ className="p-2 rounded-lg hover:bg-muted transition-colors"
192
+ >
193
+ <ArrowUpRight className="w-4 h-4 text-muted-foreground" />
194
+ </motion.button>
195
+ </div>
196
+
197
+ <div className="space-y-1">
198
+ <p className="text-xs font-medium text-foreground uppercase tracking-wide">
199
+ {stat.title}
200
+ </p>
201
+ <p className="text-3xl font-bold tracking-tight">{stat.value}</p>
202
+ <div className="flex items-center gap-2 pt-2">
203
+ {stat.todayValue > 0 ? (
204
+ <>
205
+ <div className="flex items-center gap-1">
206
+ {stat.change > 0 ? (
207
+ <div className="flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400">
208
+ <TrendingUp className="w-3 h-3" />
209
+ <span>+{stat.change}</span>
210
+ </div>
211
+ ) : stat.change < 0 ? (
212
+ <div className="flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400">
213
+ <TrendingDown className="w-3 h-3" />
214
+ <span>{stat.change}</span>
215
+ </div>
216
+ ) : (
217
+ <div className="flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium bg-muted text-muted-foreground">
218
+ <Minus className="w-3 h-3" />
219
+ <span>No change</span>
220
+ </div>
221
+ )}
222
+ </div>
223
+ <span className="text-xs text-muted-foreground">vs yesterday</span>
224
+ </>
225
+ ) : (
226
+ <span className="text-xs text-muted-foreground">No activity today</span>
227
+ )}
228
+ </div>
229
+ </div>
230
+ </CardContent>
231
+ </Card>
232
+ </motion.div>
233
+ );
234
+ })}
235
+ </motion.div>
236
+
237
+ <motion.div variants={itemVariants}>
238
+ <Card>
239
+ <CardHeader>
240
+ <CardTitle>Recent Sessions</CardTitle>
241
+ <CardDescription>Your recently accessed document processing sessions</CardDescription>
242
+ </CardHeader>
243
+ <CardContent>
244
+ {recentSessions.length === 0 ? (
245
+ <div className="text-center py-8">
246
+ <FileText className="w-12 h-12 mx-auto text-muted-foreground mb-3" />
247
+ <p className="text-muted-foreground">No sessions yet</p>
248
+ <p className="text-sm text-muted-foreground mt-1">
249
+ Create a new session to start processing documents
250
+ </p>
251
+ <Button
252
+ onClick={handleNewSession}
253
+ variant="outline"
254
+ size="sm"
255
+ className="mt-4"
256
+ icon={<Plus className="w-4 h-4" />}
257
+ >
258
+ Create First Session
259
+ </Button>
260
+ </div>
261
+ ) : (
262
+ <div className="space-y-3">
263
+ {recentSessions.map((session) => (
264
+ <motion.div
265
+ key={session.id}
266
+ className="flex items-center justify-between p-4 rounded-lg border border-border hover:bg-muted/50 transition-all cursor-pointer group"
267
+ whileHover={{ x: 4 }}
268
+ onClick={() => handleRecentSessionClick(session.id)}
269
+ >
270
+ <div className="flex items-center gap-4">
271
+ <div className="p-2 rounded-lg bg-primary/10">
272
+ <FileText className="w-5 h-5 text-primary" />
273
+ </div>
274
+ <div>
275
+ <p className="text-lg font-medium">{session.name}</p>
276
+ <div className="flex items-center gap-4 text-sm text-muted-foreground mt-1">
277
+ <span className="flex items-center gap-1">
278
+ <FileCheck className="w-3 h-3" />
279
+ {session.documents.length} documents
280
+ </span>
281
+ <span className="flex items-center gap-1">
282
+ <Calendar className="w-3 h-3" />
283
+ {formatDate(session.lastModified)}
284
+ </span>
285
+ </div>
286
+ </div>
287
+ </div>
288
+ <div className="flex items-center gap-2">
289
+ <span
290
+ className={cn(
291
+ 'text-xs px-2 py-1 rounded-full',
292
+ session.status === 'active'
293
+ ? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400'
294
+ : 'bg-muted text-muted-foreground'
295
+ )}
296
+ >
297
+ {session.status}
298
+ </span>
299
+ <ArrowUpRight className="w-4 h-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" />
300
+ </div>
301
+ </motion.div>
302
+ ))}
303
+ </div>
304
+ )}
305
+ </CardContent>
306
+ </Card>
307
+ </motion.div>
308
+
309
+ {showSessionManager && (
310
+ <SessionManager
311
+ mode={sessionManagerMode}
312
+ onClose={() => setShowSessionManager(false)}
313
+ onSessionCreated={handleSessionCreated}
314
+ onSessionLoaded={handleSessionCreated}
315
+ />
316
+ )}
317
+ </motion.div>
318
+ );
319
+ }
@@ -0,0 +1,317 @@
1
+ import { useState, useMemo } from 'react';
2
+ import { useSession } from '@/contexts/SessionContext';
3
+ import { motion } from 'framer-motion';
4
+ import {
5
+ FileText,
6
+ FolderOpen,
7
+ CheckCircle,
8
+ AlertCircle,
9
+ Clock,
10
+ Search,
11
+ Filter,
12
+ } from 'lucide-react';
13
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/common/Card';
14
+ import { Input } from '@/components/common/Input';
15
+ import { Button } from '@/components/common/Button';
16
+ import { cn } from '@/utils/cn';
17
+ import logger from '@/utils/logger';
18
+ import type { Document } from '@/types/session';
19
+
20
+ interface DocumentWithSession extends Document {
21
+ sessionName: string;
22
+ sessionId: string;
23
+ }
24
+
25
+ export function Documents() {
26
+ const { sessions } = useSession();
27
+ const [searchQuery, setSearchQuery] = useState('');
28
+ const [statusFilter, setStatusFilter] = useState<'all' | 'completed' | 'pending' | 'error'>(
29
+ 'all'
30
+ );
31
+
32
+ // Collect all documents from all sessions
33
+ const allDocuments = useMemo(() => {
34
+ const docs: DocumentWithSession[] = [];
35
+ sessions.forEach((session) => {
36
+ session.documents.forEach((doc) => {
37
+ docs.push({
38
+ ...doc,
39
+ sessionName: session.name,
40
+ sessionId: session.id,
41
+ });
42
+ });
43
+ });
44
+ return docs;
45
+ }, [sessions]);
46
+
47
+ // Filter documents
48
+ const filteredDocuments = useMemo(() => {
49
+ return allDocuments.filter((doc) => {
50
+ // Filter by search query
51
+ const matchesSearch =
52
+ searchQuery === '' ||
53
+ doc.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
54
+ doc.sessionName.toLowerCase().includes(searchQuery.toLowerCase());
55
+
56
+ // Filter by status
57
+ const matchesStatus = statusFilter === 'all' || doc.status === statusFilter;
58
+
59
+ return matchesSearch && matchesStatus;
60
+ });
61
+ }, [allDocuments, searchQuery, statusFilter]);
62
+
63
+ const handleOpenLocation = async (path?: string) => {
64
+ if (!path) {
65
+ logger.warn('No path available for document');
66
+ return;
67
+ }
68
+
69
+ try {
70
+ await window.electronAPI.showInFolder(path);
71
+ } catch (err) {
72
+ logger.error('Failed to open file location:', err);
73
+ }
74
+ };
75
+
76
+ const handleOpenDocument = async (path?: string) => {
77
+ if (!path) {
78
+ logger.warn('No path available for document');
79
+ return;
80
+ }
81
+
82
+ try {
83
+ await window.electronAPI.openDocument(path);
84
+ logger.info('Document opened successfully');
85
+ } catch (err) {
86
+ logger.error('Failed to open document:', err);
87
+ }
88
+ };
89
+
90
+ const getStatusIcon = (status: Document['status']) => {
91
+ switch (status) {
92
+ case 'completed':
93
+ return <CheckCircle className="w-4 h-4 text-green-500" />;
94
+ case 'error':
95
+ return <AlertCircle className="w-4 h-4 text-red-500" />;
96
+ case 'pending':
97
+ return <Clock className="w-4 h-4 text-muted-foreground" />;
98
+ default:
99
+ return <FileText className="w-4 h-4 text-muted-foreground" />;
100
+ }
101
+ };
102
+
103
+ const getStatusBadge = (status: Document['status']) => {
104
+ const styles = {
105
+ completed: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400',
106
+ error: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400',
107
+ pending: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400',
108
+ processing: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400',
109
+ };
110
+
111
+ return (
112
+ <span className={cn('px-2 py-1 text-xs rounded-full font-medium', styles[status])}>
113
+ {status.charAt(0).toUpperCase() + status.slice(1)}
114
+ </span>
115
+ );
116
+ };
117
+
118
+ const formatDate = (date?: Date) => {
119
+ if (!date) return 'N/A';
120
+ return new Date(date).toLocaleString();
121
+ };
122
+
123
+ return (
124
+ <div className="p-6 max-w-7xl mx-auto space-y-6">
125
+ {/* Header */}
126
+ <div>
127
+ <h1 className="text-3xl font-bold mb-2">Documents</h1>
128
+ <p className="text-muted-foreground">
129
+ View and manage all processed documents across all sessions
130
+ </p>
131
+ </div>
132
+
133
+ {/* Stats Cards */}
134
+ <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
135
+ <Card>
136
+ <CardContent className="p-4">
137
+ <div className="flex items-center gap-3">
138
+ <FileText className="w-8 h-8 text-blue-500" />
139
+ <div>
140
+ <p className="text-sm text-foreground">Total</p>
141
+ <p className="text-2xl font-bold">{allDocuments.length}</p>
142
+ </div>
143
+ </div>
144
+ </CardContent>
145
+ </Card>
146
+
147
+ <Card>
148
+ <CardContent className="p-4">
149
+ <div className="flex items-center gap-3">
150
+ <CheckCircle className="w-8 h-8 text-green-500" />
151
+ <div>
152
+ <p className="text-sm text-foreground">Completed</p>
153
+ <p className="text-2xl font-bold">
154
+ {allDocuments.filter((d) => d.status === 'completed').length}
155
+ </p>
156
+ </div>
157
+ </div>
158
+ </CardContent>
159
+ </Card>
160
+
161
+ <Card>
162
+ <CardContent className="p-4">
163
+ <div className="flex items-center gap-3">
164
+ <Clock className="w-8 h-8 text-yellow-500" />
165
+ <div>
166
+ <p className="text-sm text-foreground">Pending</p>
167
+ <p className="text-2xl font-bold">
168
+ {allDocuments.filter((d) => d.status === 'pending').length}
169
+ </p>
170
+ </div>
171
+ </div>
172
+ </CardContent>
173
+ </Card>
174
+
175
+ <Card>
176
+ <CardContent className="p-4">
177
+ <div className="flex items-center gap-3">
178
+ <AlertCircle className="w-8 h-8 text-red-500" />
179
+ <div>
180
+ <p className="text-sm text-foreground">Errors</p>
181
+ <p className="text-2xl font-bold">
182
+ {allDocuments.filter((d) => d.status === 'error').length}
183
+ </p>
184
+ </div>
185
+ </div>
186
+ </CardContent>
187
+ </Card>
188
+ </div>
189
+
190
+ {/* Filters */}
191
+ <Card>
192
+ <CardHeader>
193
+ <CardTitle>Filters</CardTitle>
194
+ </CardHeader>
195
+ <CardContent className="space-y-4">
196
+ <div className="flex flex-col sm:flex-row gap-4">
197
+ <div className="flex-1">
198
+ <Input
199
+ type="search"
200
+ placeholder="Search documents..."
201
+ value={searchQuery}
202
+ onChange={(e) => setSearchQuery(e.target.value)}
203
+ leftIcon={<Search className="w-4 h-4" />}
204
+ />
205
+ </div>
206
+ <div className="flex gap-2">
207
+ <Button
208
+ variant={statusFilter === 'all' ? 'default' : 'outline'}
209
+ size="sm"
210
+ onClick={() => setStatusFilter('all')}
211
+ >
212
+ All
213
+ </Button>
214
+ <Button
215
+ variant={statusFilter === 'completed' ? 'default' : 'outline'}
216
+ size="sm"
217
+ onClick={() => setStatusFilter('completed')}
218
+ >
219
+ Completed
220
+ </Button>
221
+ <Button
222
+ variant={statusFilter === 'pending' ? 'default' : 'outline'}
223
+ size="sm"
224
+ onClick={() => setStatusFilter('pending')}
225
+ >
226
+ Pending
227
+ </Button>
228
+ <Button
229
+ variant={statusFilter === 'error' ? 'default' : 'outline'}
230
+ size="sm"
231
+ onClick={() => setStatusFilter('error')}
232
+ >
233
+ Errors
234
+ </Button>
235
+ </div>
236
+ </div>
237
+ </CardContent>
238
+ </Card>
239
+
240
+ {/* Documents List */}
241
+ <Card>
242
+ <CardHeader>
243
+ <CardTitle>
244
+ {filteredDocuments.length} Document{filteredDocuments.length !== 1 ? 's' : ''}
245
+ </CardTitle>
246
+ </CardHeader>
247
+ <CardContent>
248
+ {filteredDocuments.length === 0 ? (
249
+ <div className="text-center py-12">
250
+ <FileText className="w-16 h-16 mx-auto text-muted-foreground mb-4" />
251
+ <h3 className="text-lg font-medium mb-2">No documents found</h3>
252
+ <p className="text-muted-foreground">
253
+ {searchQuery || statusFilter !== 'all'
254
+ ? 'Try adjusting your filters'
255
+ : 'Start by creating a session and adding documents'}
256
+ </p>
257
+ </div>
258
+ ) : (
259
+ <div className="space-y-2">
260
+ {filteredDocuments.map((doc) => (
261
+ <motion.div
262
+ key={`${doc.sessionId}-${doc.id}`}
263
+ initial={{ opacity: 0, y: 20 }}
264
+ animate={{ opacity: 1, y: 0 }}
265
+ className="flex items-center justify-between p-4 rounded-lg border border-border hover:bg-muted/50 transition-all group"
266
+ >
267
+ <div className="flex items-center gap-4 flex-1 min-w-0">
268
+ {getStatusIcon(doc.status)}
269
+ <div className="flex-1 min-w-0">
270
+ <p className="font-medium truncate">{doc.name}</p>
271
+ <div className="flex items-center gap-2 text-sm text-muted-foreground">
272
+ <span className="truncate">{doc.sessionName}</span>
273
+ {doc.processedAt && (
274
+ <>
275
+ <span>•</span>
276
+ <span>{formatDate(doc.processedAt)}</span>
277
+ </>
278
+ )}
279
+ </div>
280
+ </div>
281
+ </div>
282
+
283
+ <div className="flex items-center gap-3">
284
+ {getStatusBadge(doc.status)}
285
+ {doc.status === 'completed' && doc.path && (
286
+ <Button
287
+ variant="ghost"
288
+ size="xs"
289
+ icon={<FileText className="w-4 h-4" />}
290
+ onClick={() => handleOpenDocument(doc.path)}
291
+ title="Open document in Word"
292
+ className="text-green-600 hover:text-green-700 hover:bg-green-50 dark:text-green-400 dark:hover:bg-green-950"
293
+ >
294
+ Open Document
295
+ </Button>
296
+ )}
297
+ {doc.path && (
298
+ <Button
299
+ variant="ghost"
300
+ size="xs"
301
+ icon={<FolderOpen className="w-4 h-4" />}
302
+ onClick={() => handleOpenLocation(doc.path)}
303
+ title="Open file location"
304
+ >
305
+ Open Location
306
+ </Button>
307
+ )}
308
+ </div>
309
+ </motion.div>
310
+ ))}
311
+ </div>
312
+ )}
313
+ </CardContent>
314
+ </Card>
315
+ </div>
316
+ );
317
+ }