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,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
|
+
}
|