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,530 @@
|
|
|
1
|
+
import type { DocumentChange } from '@/types/session';
|
|
2
|
+
import { cn } from '@/utils/cn';
|
|
3
|
+
import { motion } from 'framer-motion';
|
|
4
|
+
import {
|
|
5
|
+
AlertTriangle,
|
|
6
|
+
CheckCircle,
|
|
7
|
+
ChevronDown,
|
|
8
|
+
ChevronRight,
|
|
9
|
+
FileText,
|
|
10
|
+
Hash,
|
|
11
|
+
Link,
|
|
12
|
+
List,
|
|
13
|
+
XCircle,
|
|
14
|
+
} from 'lucide-react';
|
|
15
|
+
import { useState } from 'react';
|
|
16
|
+
|
|
17
|
+
interface TrackedChangesDetailProps {
|
|
18
|
+
changes: DocumentChange[];
|
|
19
|
+
className?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface GroupedChange {
|
|
23
|
+
category: string;
|
|
24
|
+
title: string;
|
|
25
|
+
icon: typeof Link;
|
|
26
|
+
iconColor: string;
|
|
27
|
+
items: DocumentChange[];
|
|
28
|
+
summary?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function TrackedChangesDetail({ changes, className }: TrackedChangesDetailProps) {
|
|
32
|
+
const [expandedCategories, setExpandedCategories] = useState<Set<string>>(new Set());
|
|
33
|
+
|
|
34
|
+
const toggleCategory = (category: string) => {
|
|
35
|
+
setExpandedCategories((prev) => {
|
|
36
|
+
const next = new Set(prev);
|
|
37
|
+
if (next.has(category)) {
|
|
38
|
+
next.delete(category);
|
|
39
|
+
} else {
|
|
40
|
+
next.add(category);
|
|
41
|
+
}
|
|
42
|
+
return next;
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Group changes by category with intelligent aggregation
|
|
47
|
+
const groupedChanges = groupChangesByCategory(changes);
|
|
48
|
+
|
|
49
|
+
if (groupedChanges.length === 0) {
|
|
50
|
+
return (
|
|
51
|
+
<div className={cn('text-center py-4 text-sm text-muted-foreground', className)}>
|
|
52
|
+
No tracked changes to display
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div className={cn('space-y-3', className)}>
|
|
59
|
+
<div className="flex items-center justify-between mb-2">
|
|
60
|
+
<h4 className="font-semibold text-sm">Tracked Changes ({changes.length})</h4>
|
|
61
|
+
<button
|
|
62
|
+
onClick={() => {
|
|
63
|
+
if (expandedCategories.size === groupedChanges.length) {
|
|
64
|
+
setExpandedCategories(new Set());
|
|
65
|
+
} else {
|
|
66
|
+
setExpandedCategories(new Set(groupedChanges.map((g) => g.category)));
|
|
67
|
+
}
|
|
68
|
+
}}
|
|
69
|
+
className="text-xs text-muted-foreground hover:text-foreground"
|
|
70
|
+
>
|
|
71
|
+
{expandedCategories.size === groupedChanges.length ? 'Collapse All' : 'Expand All'}
|
|
72
|
+
</button>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div className="space-y-2">
|
|
76
|
+
{groupedChanges.map((group) => (
|
|
77
|
+
<ChangeGroup
|
|
78
|
+
key={group.category}
|
|
79
|
+
group={group}
|
|
80
|
+
isExpanded={expandedCategories.has(group.category)}
|
|
81
|
+
onToggle={() => toggleCategory(group.category)}
|
|
82
|
+
/>
|
|
83
|
+
))}
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function ChangeGroup({
|
|
90
|
+
group,
|
|
91
|
+
isExpanded,
|
|
92
|
+
onToggle,
|
|
93
|
+
}: {
|
|
94
|
+
group: GroupedChange;
|
|
95
|
+
isExpanded: boolean;
|
|
96
|
+
onToggle: () => void;
|
|
97
|
+
}) {
|
|
98
|
+
const Icon = group.icon;
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<motion.div
|
|
102
|
+
initial={{ opacity: 0, y: 10 }}
|
|
103
|
+
animate={{ opacity: 1, y: 0 }}
|
|
104
|
+
className="border border-border rounded-lg overflow-hidden bg-muted/20"
|
|
105
|
+
>
|
|
106
|
+
<button
|
|
107
|
+
onClick={onToggle}
|
|
108
|
+
className="w-full flex items-center gap-3 p-3 hover:bg-muted/40 transition-colors text-left"
|
|
109
|
+
>
|
|
110
|
+
<Icon className={cn('w-5 h-5 shrink-0', group.iconColor)} />
|
|
111
|
+
<div className="flex-1 min-w-0">
|
|
112
|
+
<div className="flex items-center gap-2">
|
|
113
|
+
<span className="font-medium text-sm">{group.title}</span>
|
|
114
|
+
<span className="text-xs text-muted-foreground">({group.items.length})</span>
|
|
115
|
+
</div>
|
|
116
|
+
{group.summary && (
|
|
117
|
+
<p className="text-xs text-muted-foreground mt-0.5 truncate">{group.summary}</p>
|
|
118
|
+
)}
|
|
119
|
+
</div>
|
|
120
|
+
{isExpanded ? (
|
|
121
|
+
<ChevronDown className="w-4 h-4 text-muted-foreground shrink-0" />
|
|
122
|
+
) : (
|
|
123
|
+
<ChevronRight className="w-4 h-4 text-muted-foreground shrink-0" />
|
|
124
|
+
)}
|
|
125
|
+
</button>
|
|
126
|
+
|
|
127
|
+
{isExpanded && (
|
|
128
|
+
<motion.div
|
|
129
|
+
initial={{ height: 0, opacity: 0 }}
|
|
130
|
+
animate={{ height: 'auto', opacity: 1 }}
|
|
131
|
+
exit={{ height: 0, opacity: 0 }}
|
|
132
|
+
className="border-t border-border"
|
|
133
|
+
>
|
|
134
|
+
{group.category === 'hyperlink_update' ? (
|
|
135
|
+
<HyperlinkUpdatesView items={group.items} />
|
|
136
|
+
) : group.category === 'hyperlink_failed' ? (
|
|
137
|
+
<HyperlinkFailedView items={group.items} />
|
|
138
|
+
) : group.category === 'list_fix' ? (
|
|
139
|
+
<ListFixesView items={group.items} />
|
|
140
|
+
) : group.category === 'blank_lines' ? (
|
|
141
|
+
<BlankLinesView items={group.items} />
|
|
142
|
+
) : (
|
|
143
|
+
<DefaultChangesView items={group.items} />
|
|
144
|
+
)}
|
|
145
|
+
</motion.div>
|
|
146
|
+
)}
|
|
147
|
+
</motion.div>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function HyperlinkUpdatesView({ items }: { items: DocumentChange[] }) {
|
|
152
|
+
// Group by Header 2
|
|
153
|
+
const byHeader2 = groupByHeader2(items);
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<div className="p-3 space-y-4 max-h-96 overflow-y-auto">
|
|
157
|
+
{byHeader2.map((section, idx) => (
|
|
158
|
+
<div key={idx} className="space-y-2">
|
|
159
|
+
{section.header && (
|
|
160
|
+
<div className="flex items-center gap-2 text-xs font-medium text-muted-foreground">
|
|
161
|
+
<FileText className="w-3 h-3" />
|
|
162
|
+
<span>Under "{section.header}"</span>
|
|
163
|
+
</div>
|
|
164
|
+
)}
|
|
165
|
+
<div className="space-y-3 pl-5">
|
|
166
|
+
{section.items.map((change, itemIdx) => (
|
|
167
|
+
<div key={itemIdx} className="space-y-1.5 text-xs border-l-2 border-primary/30 pl-3">
|
|
168
|
+
{/* Content ID - Always shown prominently at top for identification */}
|
|
169
|
+
{change.contentId && (
|
|
170
|
+
<div className="flex items-center gap-2">
|
|
171
|
+
<span className="text-muted-foreground text-[10px]">Content ID:</span>
|
|
172
|
+
<code className="bg-primary/10 text-primary px-1.5 py-0.5 rounded text-[10px] font-medium">
|
|
173
|
+
{change.contentId}
|
|
174
|
+
</code>
|
|
175
|
+
</div>
|
|
176
|
+
)}
|
|
177
|
+
|
|
178
|
+
{/* What Changed section */}
|
|
179
|
+
<div className="space-y-1">
|
|
180
|
+
<span className="text-[10px] font-medium text-muted-foreground uppercase tracking-wide">
|
|
181
|
+
What Changed:
|
|
182
|
+
</span>
|
|
183
|
+
|
|
184
|
+
{/* URL change (if URL changed) */}
|
|
185
|
+
{change.urlBefore && change.urlAfter && (
|
|
186
|
+
<div className="space-y-0.5 ml-2">
|
|
187
|
+
<span className="text-[10px] text-muted-foreground">URL:</span>
|
|
188
|
+
<div className="flex items-start gap-2">
|
|
189
|
+
<span className="text-muted-foreground min-w-[45px]">Before:</span>
|
|
190
|
+
<code className="bg-red-500/10 text-red-600 dark:text-red-400 px-1.5 py-0.5 rounded break-all flex-1 line-through">
|
|
191
|
+
{change.urlBefore}
|
|
192
|
+
</code>
|
|
193
|
+
</div>
|
|
194
|
+
<div className="flex items-start gap-2">
|
|
195
|
+
<span className="text-muted-foreground min-w-[45px]">After:</span>
|
|
196
|
+
<code className="bg-green-500/10 text-green-600 dark:text-green-400 px-1.5 py-0.5 rounded break-all flex-1">
|
|
197
|
+
{change.urlAfter}
|
|
198
|
+
</code>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
)}
|
|
202
|
+
|
|
203
|
+
{/* Text to Display change (if text changed) */}
|
|
204
|
+
{change.before && change.after && (
|
|
205
|
+
<div className="space-y-0.5 ml-2">
|
|
206
|
+
<span className="text-[10px] text-muted-foreground">Text to Display:</span>
|
|
207
|
+
<div className="flex items-start gap-2">
|
|
208
|
+
<span className="text-muted-foreground min-w-[45px]">Before:</span>
|
|
209
|
+
<code className="bg-red-500/10 text-red-600 dark:text-red-400 px-1.5 py-0.5 rounded break-all flex-1 line-through">
|
|
210
|
+
{change.before}
|
|
211
|
+
</code>
|
|
212
|
+
</div>
|
|
213
|
+
<div className="flex items-start gap-2">
|
|
214
|
+
<span className="text-muted-foreground min-w-[45px]">After:</span>
|
|
215
|
+
<code className="bg-green-500/10 text-green-600 dark:text-green-400 px-1.5 py-0.5 rounded break-all flex-1">
|
|
216
|
+
{change.after}
|
|
217
|
+
</code>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
)}
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
))}
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
))}
|
|
227
|
+
</div>
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function HyperlinkFailedView({ items }: { items: DocumentChange[] }) {
|
|
232
|
+
const byHeader2 = groupByHeader2(items);
|
|
233
|
+
|
|
234
|
+
return (
|
|
235
|
+
<div className="p-3 space-y-4 max-h-96 overflow-y-auto">
|
|
236
|
+
{byHeader2.map((section, idx) => (
|
|
237
|
+
<div key={idx} className="space-y-2">
|
|
238
|
+
{section.header && (
|
|
239
|
+
<div className="flex items-center gap-2 text-xs font-medium text-muted-foreground">
|
|
240
|
+
<FileText className="w-3 h-3" />
|
|
241
|
+
<span>Under "{section.header}"</span>
|
|
242
|
+
</div>
|
|
243
|
+
)}
|
|
244
|
+
<div className="space-y-1.5 pl-5">
|
|
245
|
+
{section.items.map((change, itemIdx) => (
|
|
246
|
+
<div key={itemIdx} className="flex items-start gap-2 text-xs">
|
|
247
|
+
{change.hyperlinkStatus === 'expired' ? (
|
|
248
|
+
<AlertTriangle className="w-4 h-4 text-orange-500 shrink-0 mt-0.5" />
|
|
249
|
+
) : (
|
|
250
|
+
<XCircle className="w-4 h-4 text-red-500 shrink-0 mt-0.5" />
|
|
251
|
+
)}
|
|
252
|
+
<div className="flex-1">
|
|
253
|
+
<span className="font-medium">{change.description}</span>
|
|
254
|
+
{change.contentId && (
|
|
255
|
+
<span className="ml-2 text-muted-foreground">({change.contentId})</span>
|
|
256
|
+
)}
|
|
257
|
+
<span
|
|
258
|
+
className={cn(
|
|
259
|
+
'ml-2 text-[10px] px-1.5 py-0.5 rounded',
|
|
260
|
+
change.hyperlinkStatus === 'expired'
|
|
261
|
+
? 'bg-orange-500/10 text-orange-600'
|
|
262
|
+
: 'bg-red-500/10 text-red-600'
|
|
263
|
+
)}
|
|
264
|
+
>
|
|
265
|
+
{change.hyperlinkStatus === 'expired' ? 'Expired' : 'Not Found'}
|
|
266
|
+
</span>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
))}
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
))}
|
|
273
|
+
</div>
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function ListFixesView({ items }: { items: DocumentChange[] }) {
|
|
278
|
+
const byHeader2 = groupByHeader2(items);
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<div className="p-3 space-y-4 max-h-96 overflow-y-auto">
|
|
282
|
+
{byHeader2.map((section, idx) => (
|
|
283
|
+
<div key={idx} className="space-y-2">
|
|
284
|
+
{section.header && (
|
|
285
|
+
<div className="flex items-center gap-2 text-xs font-medium text-muted-foreground">
|
|
286
|
+
<FileText className="w-3 h-3" />
|
|
287
|
+
<span>Under "{section.header}"</span>
|
|
288
|
+
</div>
|
|
289
|
+
)}
|
|
290
|
+
<div className="space-y-1 pl-5">
|
|
291
|
+
{section.items.map((change, itemIdx) => (
|
|
292
|
+
<div key={itemIdx} className="flex items-start gap-2 text-xs">
|
|
293
|
+
<CheckCircle className="w-4 h-4 text-green-500 shrink-0 mt-0.5" />
|
|
294
|
+
<span>{change.description}</span>
|
|
295
|
+
{change.count !== undefined && change.count > 0 && (
|
|
296
|
+
<span className="text-muted-foreground">({change.count} items)</span>
|
|
297
|
+
)}
|
|
298
|
+
</div>
|
|
299
|
+
))}
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
))}
|
|
303
|
+
</div>
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function BlankLinesView({ items }: { items: DocumentChange[] }) {
|
|
308
|
+
// Aggregate blank line changes
|
|
309
|
+
const removed = items.filter((c) => c.description.toLowerCase().includes('removed'));
|
|
310
|
+
const added = items.filter((c) => c.description.toLowerCase().includes('added'));
|
|
311
|
+
|
|
312
|
+
const totalRemoved = removed.reduce((sum, c) => sum + (c.count || 1), 0);
|
|
313
|
+
const totalAdded = added.reduce((sum, c) => sum + (c.count || 1), 0);
|
|
314
|
+
|
|
315
|
+
return (
|
|
316
|
+
<div className="p-3 space-y-2 text-xs">
|
|
317
|
+
{totalRemoved > 0 && (
|
|
318
|
+
<div className="flex items-center gap-2">
|
|
319
|
+
<XCircle className="w-4 h-4 text-red-500" />
|
|
320
|
+
<span>Removed {totalRemoved} blank line{totalRemoved !== 1 ? 's' : ''}</span>
|
|
321
|
+
</div>
|
|
322
|
+
)}
|
|
323
|
+
{totalAdded > 0 && (
|
|
324
|
+
<div className="flex items-center gap-2">
|
|
325
|
+
<CheckCircle className="w-4 h-4 text-green-500" />
|
|
326
|
+
<span>
|
|
327
|
+
Added {totalAdded} standardized line{totalAdded !== 1 ? 's' : ''} for structure
|
|
328
|
+
</span>
|
|
329
|
+
</div>
|
|
330
|
+
)}
|
|
331
|
+
</div>
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function DefaultChangesView({ items }: { items: DocumentChange[] }) {
|
|
336
|
+
return (
|
|
337
|
+
<div className="p-3 space-y-1.5 max-h-96 overflow-y-auto">
|
|
338
|
+
{items.map((change, idx) => (
|
|
339
|
+
<div key={idx} className="flex items-start gap-2 text-xs">
|
|
340
|
+
<CheckCircle className="w-4 h-4 text-green-500 shrink-0 mt-0.5" />
|
|
341
|
+
<div className="flex-1">
|
|
342
|
+
<p className="font-medium">{change.description}</p>
|
|
343
|
+
{change.nearestHeader2 && (
|
|
344
|
+
<p className="text-muted-foreground text-[10px] mt-0.5">
|
|
345
|
+
Under "{change.nearestHeader2}"
|
|
346
|
+
</p>
|
|
347
|
+
)}
|
|
348
|
+
{change.before && change.after && (
|
|
349
|
+
<div className="mt-1 space-y-0.5">
|
|
350
|
+
<div className="flex items-start gap-2">
|
|
351
|
+
<span className="text-muted-foreground min-w-[45px]">Before:</span>
|
|
352
|
+
<code className="bg-muted px-1.5 py-0.5 rounded break-all flex-1">
|
|
353
|
+
{change.before}
|
|
354
|
+
</code>
|
|
355
|
+
</div>
|
|
356
|
+
<div className="flex items-start gap-2">
|
|
357
|
+
<span className="text-muted-foreground min-w-[45px]">After:</span>
|
|
358
|
+
<code className="bg-primary/10 text-primary px-1.5 py-0.5 rounded break-all flex-1">
|
|
359
|
+
{change.after}
|
|
360
|
+
</code>
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
)}
|
|
364
|
+
{change.count !== undefined && change.count > 0 && (
|
|
365
|
+
<p className="text-muted-foreground text-[10px] mt-0.5">
|
|
366
|
+
{change.count} occurrence{change.count !== 1 ? 's' : ''}
|
|
367
|
+
</p>
|
|
368
|
+
)}
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
))}
|
|
372
|
+
</div>
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Helper: Group changes by category
|
|
377
|
+
function groupChangesByCategory(changes: DocumentChange[]): GroupedChange[] {
|
|
378
|
+
const groups: GroupedChange[] = [];
|
|
379
|
+
|
|
380
|
+
// Separate changes by category
|
|
381
|
+
const hyperlinksUpdated = changes.filter(
|
|
382
|
+
(c) => c.category === 'hyperlink_update' && c.hyperlinkStatus === 'updated'
|
|
383
|
+
);
|
|
384
|
+
const hyperlinksFailed = changes.filter(
|
|
385
|
+
(c) =>
|
|
386
|
+
c.category === 'hyperlink_failed' ||
|
|
387
|
+
c.hyperlinkStatus === 'expired' ||
|
|
388
|
+
c.hyperlinkStatus === 'not_found'
|
|
389
|
+
);
|
|
390
|
+
const listFixes = changes.filter((c) => c.category === 'list_fix');
|
|
391
|
+
const blankLines = changes.filter((c) => c.category === 'blank_lines');
|
|
392
|
+
const styleChanges = changes.filter((c) => c.category === 'style_application');
|
|
393
|
+
const structureChanges = changes.filter((c) => c.category === 'structure');
|
|
394
|
+
const otherChanges = changes.filter(
|
|
395
|
+
(c) =>
|
|
396
|
+
!c.category ||
|
|
397
|
+
(c.category === 'other' &&
|
|
398
|
+
!hyperlinksUpdated.includes(c) &&
|
|
399
|
+
!hyperlinksFailed.includes(c) &&
|
|
400
|
+
!listFixes.includes(c) &&
|
|
401
|
+
!blankLines.includes(c) &&
|
|
402
|
+
!styleChanges.includes(c) &&
|
|
403
|
+
!structureChanges.includes(c))
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
// Hyperlink updates
|
|
407
|
+
if (hyperlinksUpdated.length > 0) {
|
|
408
|
+
groups.push({
|
|
409
|
+
category: 'hyperlink_update',
|
|
410
|
+
title: 'Updated Hyperlink URLs / Content IDs',
|
|
411
|
+
icon: Link,
|
|
412
|
+
iconColor: 'text-blue-500',
|
|
413
|
+
items: hyperlinksUpdated,
|
|
414
|
+
summary: `${hyperlinksUpdated.length} hyperlink${hyperlinksUpdated.length !== 1 ? 's' : ''} updated`,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Failed/Expired hyperlinks
|
|
419
|
+
if (hyperlinksFailed.length > 0) {
|
|
420
|
+
const expiredCount = hyperlinksFailed.filter((c) => c.hyperlinkStatus === 'expired').length;
|
|
421
|
+
const notFoundCount = hyperlinksFailed.filter(
|
|
422
|
+
(c) => c.hyperlinkStatus === 'not_found'
|
|
423
|
+
).length;
|
|
424
|
+
|
|
425
|
+
let summary = '';
|
|
426
|
+
if (expiredCount > 0 && notFoundCount > 0) {
|
|
427
|
+
summary = `${expiredCount} expired, ${notFoundCount} not found`;
|
|
428
|
+
} else if (expiredCount > 0) {
|
|
429
|
+
summary = `${expiredCount} expired`;
|
|
430
|
+
} else {
|
|
431
|
+
summary = `${notFoundCount} not found`;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
groups.push({
|
|
435
|
+
category: 'hyperlink_failed',
|
|
436
|
+
title: 'Hyperlink Issues Found',
|
|
437
|
+
icon: AlertTriangle,
|
|
438
|
+
iconColor: 'text-orange-500',
|
|
439
|
+
items: hyperlinksFailed,
|
|
440
|
+
summary,
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// List formatting fixes
|
|
445
|
+
if (listFixes.length > 0) {
|
|
446
|
+
const totalListsFixed = listFixes.reduce((sum, c) => sum + (c.count || 1), 0);
|
|
447
|
+
groups.push({
|
|
448
|
+
category: 'list_fix',
|
|
449
|
+
title: 'List Formatting',
|
|
450
|
+
icon: List,
|
|
451
|
+
iconColor: 'text-purple-500',
|
|
452
|
+
items: listFixes,
|
|
453
|
+
summary: `Fixed ${totalListsFixed} list${totalListsFixed !== 1 ? 's' : ''}`,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Blank lines (condensed)
|
|
458
|
+
if (blankLines.length > 0) {
|
|
459
|
+
groups.push({
|
|
460
|
+
category: 'blank_lines',
|
|
461
|
+
title: 'Structure Changes',
|
|
462
|
+
icon: FileText,
|
|
463
|
+
iconColor: 'text-gray-500',
|
|
464
|
+
items: blankLines,
|
|
465
|
+
summary: 'Blank line adjustments',
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Style applications
|
|
470
|
+
if (styleChanges.length > 0) {
|
|
471
|
+
groups.push({
|
|
472
|
+
category: 'style_application',
|
|
473
|
+
title: 'Style Applications',
|
|
474
|
+
icon: Hash,
|
|
475
|
+
iconColor: 'text-indigo-500',
|
|
476
|
+
items: styleChanges,
|
|
477
|
+
summary: `${styleChanges.length} style${styleChanges.length !== 1 ? 's' : ''} applied`,
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Other structure changes
|
|
482
|
+
if (structureChanges.length > 0) {
|
|
483
|
+
groups.push({
|
|
484
|
+
category: 'structure',
|
|
485
|
+
title: 'Other Structure Changes',
|
|
486
|
+
icon: FileText,
|
|
487
|
+
iconColor: 'text-teal-500',
|
|
488
|
+
items: structureChanges,
|
|
489
|
+
summary: `${structureChanges.length} change${structureChanges.length !== 1 ? 's' : ''}`,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Other changes
|
|
494
|
+
if (otherChanges.length > 0) {
|
|
495
|
+
groups.push({
|
|
496
|
+
category: 'other',
|
|
497
|
+
title: 'Other Changes',
|
|
498
|
+
icon: FileText,
|
|
499
|
+
iconColor: 'text-gray-500',
|
|
500
|
+
items: otherChanges,
|
|
501
|
+
summary: `${otherChanges.length} miscellaneous change${otherChanges.length !== 1 ? 's' : ''}`,
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return groups;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Helper: Group items by nearest Header 2
|
|
509
|
+
function groupByHeader2(
|
|
510
|
+
items: DocumentChange[]
|
|
511
|
+
): Array<{ header: string | null; items: DocumentChange[] }> {
|
|
512
|
+
const grouped = new Map<string | null, DocumentChange[]>();
|
|
513
|
+
|
|
514
|
+
for (const item of items) {
|
|
515
|
+
const header = item.nearestHeader2 || null;
|
|
516
|
+
if (!grouped.has(header)) {
|
|
517
|
+
grouped.set(header, []);
|
|
518
|
+
}
|
|
519
|
+
grouped.get(header)!.push(item);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Convert to array, sort by header (nulls last)
|
|
523
|
+
return Array.from(grouped.entries())
|
|
524
|
+
.sort(([a], [b]) => {
|
|
525
|
+
if (a === null) return 1;
|
|
526
|
+
if (b === null) return -1;
|
|
527
|
+
return a.localeCompare(b);
|
|
528
|
+
})
|
|
529
|
+
.map(([header, items]) => ({ header, items }));
|
|
530
|
+
}
|