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,193 @@
1
+ import { useUserSettings } from '@/contexts/UserSettingsContext';
2
+ import logger from '@/utils/logger';
3
+ import { AnimatePresence, motion } from 'framer-motion';
4
+ import { Bug, Send, X } from 'lucide-react';
5
+ import { useState } from 'react';
6
+ import { Button } from './Button';
7
+
8
+ interface BugReportDialogProps {
9
+ isOpen: boolean;
10
+ onClose: () => void;
11
+ }
12
+
13
+ export function BugReportDialog({ isOpen, onClose }: BugReportDialogProps) {
14
+ const { settings } = useUserSettings();
15
+ const [title, setTitle] = useState('');
16
+ const [description, setDescription] = useState('');
17
+ const [isSubmitting, setIsSubmitting] = useState(false);
18
+
19
+ const handleSubmit = async () => {
20
+ if (!title.trim() || !description.trim()) return;
21
+
22
+ setIsSubmitting(true);
23
+
24
+ const bugReport = {
25
+ Type: 'Bug Report',
26
+ Email: settings.profile.email,
27
+ Title: title,
28
+ Description: description,
29
+ };
30
+
31
+ const apiUrl = settings.apiConnections.bugReportUrl;
32
+
33
+ // Check if using default URL - if so, fallback to mailto
34
+ if (apiUrl === 'https://www.example.com' || !apiUrl) {
35
+ const subject = encodeURIComponent(`Bug Report: ${title}`);
36
+ const body = encodeURIComponent(`
37
+ Bug Report
38
+ ----------
39
+ Email: ${settings.profile.email}
40
+ Title: ${title}
41
+
42
+ Description of Issue:
43
+ ${description}
44
+
45
+ Submitted: ${new Date().toLocaleString()}
46
+ `);
47
+
48
+ window.location.href = `mailto:support@example.com?subject=${subject}&body=${body}`;
49
+
50
+ // Clear fields and close dialog
51
+ setTitle('');
52
+ setDescription('');
53
+ setIsSubmitting(false);
54
+ onClose();
55
+
56
+ // Show success notification
57
+ alert('Your bug report has been sent to the Documentation Hub Admin');
58
+ return;
59
+ }
60
+
61
+ // Use API if configured
62
+ try {
63
+ const response = await fetch(apiUrl, {
64
+ method: 'POST',
65
+ headers: {
66
+ 'Content-Type': 'application/json',
67
+ },
68
+ body: JSON.stringify(bugReport),
69
+ });
70
+
71
+ if (response.ok) {
72
+ // Clear fields and close dialog on success
73
+ setTitle('');
74
+ setDescription('');
75
+ onClose();
76
+
77
+ // Show success notification
78
+ alert('Your bug report has been sent to the Documentation Hub Admin');
79
+ } else {
80
+ alert('Failed to submit bug report. Please try again.');
81
+ }
82
+ } catch (error) {
83
+ logger.error('Error submitting bug report:', error);
84
+ alert('Failed to submit bug report. Please check your API configuration.');
85
+ } finally {
86
+ setIsSubmitting(false);
87
+ }
88
+ };
89
+
90
+ const handleCancel = () => {
91
+ setTitle('');
92
+ setDescription('');
93
+ onClose();
94
+ };
95
+
96
+ return (
97
+ <AnimatePresence>
98
+ {isOpen && (
99
+ <>
100
+ {/* Backdrop */}
101
+ <motion.div
102
+ initial={{ opacity: 0 }}
103
+ animate={{ opacity: 1 }}
104
+ exit={{ opacity: 0 }}
105
+ className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50"
106
+ onClick={handleCancel}
107
+ />
108
+
109
+ {/* Dialog */}
110
+ <motion.div
111
+ initial={{ opacity: 0, scale: 0.95, y: 20 }}
112
+ animate={{ opacity: 1, scale: 1, y: 0 }}
113
+ exit={{ opacity: 0, scale: 0.95, y: 20 }}
114
+ transition={{ type: 'spring', damping: 20, stiffness: 300 }}
115
+ className="fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-lg z-50"
116
+ >
117
+ <div className="bg-background border border-border rounded-lg shadow-xl overflow-hidden">
118
+ {/* Header */}
119
+ <div className="flex items-center justify-between p-4 border-b border-border bg-muted/30">
120
+ <div className="flex items-center gap-2">
121
+ <div className="w-8 h-8 rounded-lg bg-linear-to-br from-red-500 to-orange-600 flex items-center justify-center">
122
+ <Bug className="w-4 h-4 text-white" />
123
+ </div>
124
+ <h2 className="text-lg font-semibold">Report a Bug</h2>
125
+ </div>
126
+ <button
127
+ onClick={handleCancel}
128
+ className="p-1 rounded-md hover:bg-muted transition-colors"
129
+ aria-label="Close dialog"
130
+ >
131
+ <X className="w-5 h-5" />
132
+ </button>
133
+ </div>
134
+
135
+ {/* Content */}
136
+ <div className="p-6 space-y-4">
137
+ <div>
138
+ <label htmlFor="bug-title" className="block text-sm font-medium mb-2">
139
+ Title
140
+ </label>
141
+ <input
142
+ id="bug-title"
143
+ type="text"
144
+ value={title}
145
+ onChange={(e) => setTitle(e.target.value)}
146
+ placeholder="Brief description of the issue"
147
+ className="w-full px-3 py-2 rounded-md border border-input bg-background focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
148
+ autoFocus
149
+ />
150
+ </div>
151
+
152
+ <div>
153
+ <label htmlFor="bug-description" className="block text-sm font-medium mb-2">
154
+ Description of Issue
155
+ </label>
156
+ <textarea
157
+ id="bug-description"
158
+ value={description}
159
+ onChange={(e) => setDescription(e.target.value)}
160
+ placeholder="Please provide details about the bug, steps to reproduce, and any error messages..."
161
+ rows={6}
162
+ className="w-full px-3 py-2 rounded-md border border-input bg-background resize-none focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
163
+ />
164
+ </div>
165
+
166
+ <div className="text-xs text-muted-foreground bg-muted/30 p-3 rounded-md">
167
+ <p>
168
+ <strong>Note:</strong> Your email ({settings.profile.email}) will be included
169
+ with this report so we can follow up if needed.
170
+ </p>
171
+ </div>
172
+ </div>
173
+
174
+ {/* Footer */}
175
+ <div className="flex items-center justify-end gap-2 p-4 border-t border-border bg-muted/30">
176
+ <Button variant="outline" onClick={handleCancel}>
177
+ Cancel
178
+ </Button>
179
+ <Button
180
+ onClick={handleSubmit}
181
+ disabled={!title.trim() || !description.trim() || isSubmitting}
182
+ icon={<Send className="w-4 h-4" />}
183
+ >
184
+ {isSubmitting ? 'Sending...' : 'Send Report'}
185
+ </Button>
186
+ </div>
187
+ </div>
188
+ </motion.div>
189
+ </>
190
+ )}
191
+ </AnimatePresence>
192
+ );
193
+ }
@@ -0,0 +1,153 @@
1
+ import { cn } from '@/utils/cn';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+ import { motion } from 'framer-motion';
4
+ import { forwardRef, useEffect, useState } from 'react';
5
+
6
+ const buttonVariants = cva(
7
+ 'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
12
+ destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
13
+ outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
14
+ secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
15
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
16
+ link: 'text-primary underline-offset-4 hover:underline',
17
+ gradient:
18
+ 'bg-linear-to-r from-blue-500 to-purple-600 text-white hover:from-blue-600 hover:to-purple-700',
19
+ },
20
+ size: {
21
+ default: 'h-10 px-4 py-2',
22
+ sm: 'h-9 rounded-md px-3',
23
+ lg: 'h-11 rounded-md px-8',
24
+ icon: 'h-10 w-10',
25
+ xs: 'h-7 px-2 text-xs rounded',
26
+ },
27
+ },
28
+ defaultVariants: {
29
+ variant: 'default',
30
+ size: 'default',
31
+ },
32
+ }
33
+ );
34
+
35
+ export interface ButtonProps
36
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
37
+ VariantProps<typeof buttonVariants> {
38
+ asChild?: boolean;
39
+ loading?: boolean;
40
+ icon?: React.ReactNode;
41
+ showSuccess?: boolean;
42
+ onSuccess?: () => void;
43
+ }
44
+
45
+ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
46
+ (
47
+ {
48
+ className,
49
+ variant,
50
+ size,
51
+ loading,
52
+ icon,
53
+ children,
54
+ disabled,
55
+ showSuccess,
56
+ onSuccess,
57
+ ...props
58
+ },
59
+ ref
60
+ ) => {
61
+ const { asChild, ...buttonProps } = props as any;
62
+ const [isSuccess, setIsSuccess] = useState(false);
63
+
64
+ useEffect(() => {
65
+ if (showSuccess) {
66
+ setIsSuccess(true);
67
+ const timer = setTimeout(() => {
68
+ setIsSuccess(false);
69
+ onSuccess?.();
70
+ }, 2000);
71
+ return () => clearTimeout(timer);
72
+ } else {
73
+ setIsSuccess(false);
74
+ }
75
+ }, [showSuccess, onSuccess]);
76
+
77
+ return (
78
+ <motion.button
79
+ className={cn(
80
+ buttonVariants({ variant, size, className }),
81
+ isSuccess && 'bg-green-500 hover:bg-green-500 text-green-50'
82
+ )}
83
+ ref={ref}
84
+ disabled={disabled || loading || isSuccess}
85
+ whileHover={{ scale: disabled || loading || isSuccess ? 1 : 1.02 }}
86
+ whileTap={{ scale: disabled || loading || isSuccess ? 1 : 0.98 }}
87
+ animate={isSuccess ? { scale: [1, 1.05, 1] } : {}}
88
+ transition={{ duration: 0.2 }}
89
+ {...buttonProps}
90
+ >
91
+ {isSuccess ? (
92
+ <motion.div
93
+ initial={{ scale: 0 }}
94
+ animate={{ scale: 1 }}
95
+ className="flex items-center gap-2"
96
+ >
97
+ <svg
98
+ className="w-5 h-5"
99
+ fill="none"
100
+ stroke="currentColor"
101
+ viewBox="0 0 24 24"
102
+ xmlns="http://www.w3.org/2000/svg"
103
+ >
104
+ <motion.path
105
+ strokeLinecap="round"
106
+ strokeLinejoin="round"
107
+ strokeWidth={2}
108
+ d="M5 13l4 4L19 7"
109
+ initial={{ pathLength: 0 }}
110
+ animate={{ pathLength: 1 }}
111
+ transition={{ duration: 0.3 }}
112
+ />
113
+ </svg>
114
+ Saved!
115
+ </motion.div>
116
+ ) : loading ? (
117
+ <>
118
+ <svg
119
+ className="mr-2 h-4 w-4 animate-spin"
120
+ xmlns="http://www.w3.org/2000/svg"
121
+ fill="none"
122
+ viewBox="0 0 24 24"
123
+ >
124
+ <circle
125
+ className="opacity-25"
126
+ cx="12"
127
+ cy="12"
128
+ r="10"
129
+ stroke="currentColor"
130
+ strokeWidth="4"
131
+ />
132
+ <path
133
+ className="opacity-75"
134
+ fill="currentColor"
135
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
136
+ />
137
+ </svg>
138
+ Loading...
139
+ </>
140
+ ) : (
141
+ <>
142
+ {icon && <span className="mr-2">{icon}</span>}
143
+ {children}
144
+ </>
145
+ )}
146
+ </motion.button>
147
+ );
148
+ }
149
+ );
150
+
151
+ Button.displayName = 'Button';
152
+
153
+ export { Button, buttonVariants };
@@ -0,0 +1,86 @@
1
+ import { forwardRef } from 'react';
2
+ import { cn } from '@/utils/cn';
3
+ import { motion, HTMLMotionProps } from 'framer-motion';
4
+
5
+ export interface CardProps extends HTMLMotionProps<'div'> {
6
+ variant?: 'default' | 'bordered' | 'ghost' | 'glass';
7
+ interactive?: boolean;
8
+ }
9
+
10
+ const Card = forwardRef<HTMLDivElement, CardProps>(
11
+ ({ className, variant = 'default', interactive = false, children, ...props }, ref) => {
12
+ const variants = {
13
+ default: 'bg-card text-card-foreground shadow-xs',
14
+ bordered: 'border border-border bg-card text-card-foreground',
15
+ ghost: 'bg-transparent',
16
+ glass: 'glass border border-white/10 shadow-lg',
17
+ };
18
+
19
+ return (
20
+ <motion.div
21
+ ref={ref}
22
+ className={cn(
23
+ 'rounded-lg',
24
+ variants[variant],
25
+ interactive && 'cursor-pointer transition-shadow duration-200 hover:shadow-md',
26
+ className
27
+ )}
28
+ whileHover={interactive ? { scale: 1.02 } : undefined}
29
+ whileTap={interactive ? { scale: 0.98 } : undefined}
30
+ transition={{ duration: 0.2 }}
31
+ {...props}
32
+ >
33
+ {children}
34
+ </motion.div>
35
+ );
36
+ }
37
+ );
38
+
39
+ Card.displayName = 'Card';
40
+
41
+ const CardHeader = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
42
+ ({ className, ...props }, ref) => (
43
+ <div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />
44
+ )
45
+ );
46
+
47
+ CardHeader.displayName = 'CardHeader';
48
+
49
+ const CardTitle = forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
50
+ ({ className, ...props }, ref) => (
51
+ <h3
52
+ ref={ref}
53
+ className={cn('text-2xl font-semibold leading-none tracking-tight', className)}
54
+ {...props}
55
+ />
56
+ )
57
+ );
58
+
59
+ CardTitle.displayName = 'CardTitle';
60
+
61
+ const CardDescription = forwardRef<
62
+ HTMLParagraphElement,
63
+ React.HTMLAttributes<HTMLParagraphElement>
64
+ >(({ className, ...props }, ref) => (
65
+ <p ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />
66
+ ));
67
+
68
+ CardDescription.displayName = 'CardDescription';
69
+
70
+ const CardContent = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
71
+ ({ className, ...props }, ref) => (
72
+ <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
73
+ )
74
+ );
75
+
76
+ CardContent.displayName = 'CardContent';
77
+
78
+ const CardFooter = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
79
+ ({ className, ...props }, ref) => (
80
+ <div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props} />
81
+ )
82
+ );
83
+
84
+ CardFooter.displayName = 'CardFooter';
85
+
86
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
@@ -0,0 +1,177 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { motion, AnimatePresence } from 'framer-motion';
3
+ import { X, Check, Palette, Pipette } from 'lucide-react';
4
+ import { Button } from './Button';
5
+ import { cn } from '@/utils/cn';
6
+
7
+ interface ColorPickerDialogProps {
8
+ isOpen: boolean;
9
+ onClose: () => void;
10
+ color: string;
11
+ onColorChange: (color: string) => void;
12
+ title?: string;
13
+ }
14
+
15
+ export function ColorPickerDialog({
16
+ isOpen,
17
+ onClose,
18
+ color,
19
+ onColorChange,
20
+ title = 'Pick a Color',
21
+ }: ColorPickerDialogProps) {
22
+ const [tempColor, setTempColor] = useState(color);
23
+
24
+ useEffect(() => {
25
+ if (isOpen) {
26
+ setTempColor(color);
27
+ }
28
+ }, [isOpen, color]);
29
+
30
+ const handleConfirm = () => {
31
+ onColorChange(tempColor);
32
+ onClose();
33
+ };
34
+
35
+ const handleCancel = () => {
36
+ setTempColor(color); // Reset to original
37
+ onClose();
38
+ };
39
+
40
+ return (
41
+ <AnimatePresence>
42
+ {isOpen && (
43
+ <>
44
+ <motion.div
45
+ initial={{ opacity: 0 }}
46
+ animate={{ opacity: 1 }}
47
+ exit={{ opacity: 0 }}
48
+ className="fixed inset-0 bg-black/50 backdrop-blur-xs z-50"
49
+ onClick={handleCancel}
50
+ />
51
+ <motion.div
52
+ initial={{ opacity: 0, scale: 0.95 }}
53
+ animate={{ opacity: 1, scale: 1 }}
54
+ exit={{ opacity: 0, scale: 0.95 }}
55
+ transition={{ duration: 0.2 }}
56
+ className="fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 w-80 bg-background border border-border rounded-xl shadow-2xl"
57
+ >
58
+ <div className="flex items-center justify-between p-4 border-b border-border">
59
+ <h3 className="font-semibold">{title}</h3>
60
+ <button
61
+ onClick={handleCancel}
62
+ className="p-1 rounded-md hover:bg-muted transition-colors"
63
+ >
64
+ <X className="w-4 h-4" />
65
+ </button>
66
+ </div>
67
+
68
+ <div className="p-6 space-y-4">
69
+ <div className="space-y-3">
70
+ <div className="w-full h-32 rounded-lg border border-border relative overflow-hidden group cursor-pointer">
71
+ <div
72
+ className="absolute inset-0 transition-all"
73
+ style={{ backgroundColor: tempColor }}
74
+ />
75
+ <div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity bg-black/20">
76
+ <div className="bg-white/90 px-3 py-1.5 rounded-full flex items-center gap-2 text-sm font-medium">
77
+ <Pipette className="w-4 h-4" />
78
+ Click to pick color
79
+ </div>
80
+ </div>
81
+ <input
82
+ type="color"
83
+ value={tempColor}
84
+ onChange={(e) => setTempColor(e.target.value)}
85
+ className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
86
+ />
87
+ </div>
88
+
89
+ <div className="flex gap-2">
90
+ <div className="relative flex-1">
91
+ <Palette className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
92
+ <input
93
+ type="text"
94
+ value={tempColor}
95
+ onChange={(e) => {
96
+ const value = e.target.value;
97
+ if (/^#[0-9A-Fa-f]{0,6}$/.test(value)) {
98
+ setTempColor(value);
99
+ }
100
+ }}
101
+ className="w-full pl-10 pr-3 py-2 text-sm border border-border rounded-md bg-background font-mono focus:border-primary focus:ring-1 focus:ring-primary/20 transition-all"
102
+ placeholder="#000000"
103
+ maxLength={7}
104
+ />
105
+ </div>
106
+ <div
107
+ className="w-10 h-10 rounded-md border-2 border-border shadow-inner"
108
+ style={{ backgroundColor: tempColor }}
109
+ />
110
+ </div>
111
+
112
+ <div>
113
+ <p className="text-xs text-muted-foreground mb-2">Quick Colors</p>
114
+ <div className="grid grid-cols-8 gap-2">
115
+ {[
116
+ '#ef4444',
117
+ '#f97316',
118
+ '#f59e0b',
119
+ '#eab308',
120
+ '#84cc16',
121
+ '#22c55e',
122
+ '#10b981',
123
+ '#14b8a6',
124
+ '#06b6d4',
125
+ '#0ea5e9',
126
+ '#3b82f6',
127
+ '#6366f1',
128
+ '#8b5cf6',
129
+ '#a855f7',
130
+ '#d946ef',
131
+ '#ec4899',
132
+ '#f43f5e',
133
+ '#64748b',
134
+ '#475569',
135
+ '#334155',
136
+ '#1e293b',
137
+ '#0f172a',
138
+ '#ffffff',
139
+ '#000000',
140
+ ].map((presetColor) => (
141
+ <button
142
+ key={presetColor}
143
+ onClick={() => setTempColor(presetColor)}
144
+ className={cn(
145
+ 'w-8 h-8 rounded border-2 transition-all',
146
+ tempColor === presetColor
147
+ ? 'border-primary scale-110'
148
+ : 'border-border hover:border-muted-foreground hover:scale-105'
149
+ )}
150
+ style={{ backgroundColor: presetColor }}
151
+ aria-label={`Select ${presetColor}`}
152
+ />
153
+ ))}
154
+ </div>
155
+ </div>
156
+ </div>
157
+ </div>
158
+
159
+ <div className="flex gap-2 p-4 border-t border-border">
160
+ <Button variant="outline" className="flex-1" onClick={handleCancel}>
161
+ Cancel
162
+ </Button>
163
+ <Button
164
+ variant="default"
165
+ className="flex-1"
166
+ onClick={handleConfirm}
167
+ icon={<Check className="w-4 h-4" />}
168
+ >
169
+ OK
170
+ </Button>
171
+ </div>
172
+ </motion.div>
173
+ </>
174
+ )}
175
+ </AnimatePresence>
176
+ );
177
+ }