@donartcha/openlag 0.1.1 → 0.1.3
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/dist/assets/{arc-BJpiUvSh.js → arc-CQVyn5E3.js} +1 -1
- package/dist/assets/{architectureDiagram-3BPJPVTR-Dc7Q7uiQ.js → architectureDiagram-3BPJPVTR-BK7dvXUP.js} +1 -1
- package/dist/assets/{blockDiagram-GPEHLZMM-dYx8zJHa.js → blockDiagram-GPEHLZMM-CZACUlID.js} +1 -1
- package/dist/assets/{c4Diagram-AAUBKEIU-DEW1mepT.js → c4Diagram-AAUBKEIU-BaIdnoVu.js} +1 -1
- package/dist/assets/channel-cdJUve2-.js +1 -0
- package/dist/assets/{chunk-2J33WTMH-DI5V_htM.js → chunk-2J33WTMH-I6mjZe22.js} +1 -1
- package/dist/assets/{chunk-4BX2VUAB-Dpc1Oo7a.js → chunk-4BX2VUAB-Bni2aQg6.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-BVCvy2y7.js → chunk-55IACEB6-BVvD3auX.js} +1 -1
- package/dist/assets/{chunk-727SXJPM-CiQJMcWK.js → chunk-727SXJPM-BvRYlYfC.js} +1 -1
- package/dist/assets/{chunk-AQP2D5EJ-CfIJf1-T.js → chunk-AQP2D5EJ-DODUUYy0.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-CSSwZdAo.js → chunk-FMBD7UC4-CSnOrzk-.js} +1 -1
- package/dist/assets/{chunk-ND2GUHAM-CpsO12XK.js → chunk-ND2GUHAM-Cwj-8YlS.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-CWuoRpFf.js → chunk-QZHKN3VN-DvvnV7FS.js} +1 -1
- package/dist/assets/classDiagram-4FO5ZUOK-cojzztnL.js +1 -0
- package/dist/assets/classDiagram-v2-Q7XG4LA2-cojzztnL.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-COfB4T0K.js → cose-bilkent-S5V4N54A-CpzWWErA.js} +1 -1
- package/dist/assets/{dagre-BM42HDAG-C1H0EKAG.js → dagre-BM42HDAG-BoDP4qSL.js} +1 -1
- package/dist/assets/{diagram-2AECGRRQ-D6sEKSEZ.js → diagram-2AECGRRQ-DHIQUqF0.js} +1 -1
- package/dist/assets/{diagram-5GNKFQAL-Brd_bOCT.js → diagram-5GNKFQAL-xlx_pjV_.js} +1 -1
- package/dist/assets/{diagram-KO2AKTUF-Dt0MCanV.js → diagram-KO2AKTUF-B8MO1HgW.js} +1 -1
- package/dist/assets/{diagram-LMA3HP47-DQrGF35P.js → diagram-LMA3HP47-CRSJknuZ.js} +1 -1
- package/dist/assets/{diagram-OG6HWLK6-Dm7seF-q.js → diagram-OG6HWLK6-CeTvy4fY.js} +1 -1
- package/dist/assets/{erDiagram-TEJ5UH35-U6Gl6mUU.js → erDiagram-TEJ5UH35-DFtC0ScP.js} +1 -1
- package/dist/assets/{flowDiagram-I6XJVG4X-C7K7oK1X.js → flowDiagram-I6XJVG4X-DMRa3J_m.js} +1 -1
- package/dist/assets/{ganttDiagram-6RSMTGT7-CNvz6W3E.js → ganttDiagram-6RSMTGT7-Bxuykf6p.js} +1 -1
- package/dist/assets/{gitGraphDiagram-PVQCEYII-CyhbUvLq.js → gitGraphDiagram-PVQCEYII-CXTm84QI.js} +1 -1
- package/dist/assets/index-D02phRy3.css +1 -0
- package/dist/assets/index-Dvw-BZIp.js +734 -0
- package/dist/assets/{infoDiagram-5YYISTIA-DyHSE0hy.js → infoDiagram-5YYISTIA-D2tvLdtC.js} +1 -1
- package/dist/assets/{ishikawaDiagram-YF4QCWOH-fjlmrHx3.js → ishikawaDiagram-YF4QCWOH-D0jVP7EP.js} +1 -1
- package/dist/assets/{journeyDiagram-JHISSGLW-DOoASPaf.js → journeyDiagram-JHISSGLW-Ca5D6tBE.js} +1 -1
- package/dist/assets/{kanban-definition-UN3LZRKU-BEiKB1N_.js → kanban-definition-UN3LZRKU-BQ6UUXz2.js} +1 -1
- package/dist/assets/{linear-CPWtktbJ.js → linear-ARjH3_jK.js} +1 -1
- package/dist/assets/{mindmap-definition-RKZ34NQL-DliBjc9v.js → mindmap-definition-RKZ34NQL-CRRCJeZm.js} +1 -1
- package/dist/assets/{pieDiagram-4H26LBE5-CDu9kWU3.js → pieDiagram-4H26LBE5-BwtLTkTI.js} +1 -1
- package/dist/assets/{quadrantDiagram-W4KKPZXB-CEp1tT5r.js → quadrantDiagram-W4KKPZXB-BZ6VcAAZ.js} +1 -1
- package/dist/assets/{requirementDiagram-4Y6WPE33-BD2uboSV.js → requirementDiagram-4Y6WPE33-CmuAE86r.js} +1 -1
- package/dist/assets/{sankeyDiagram-5OEKKPKP-ZNgAHRIH.js → sankeyDiagram-5OEKKPKP-DRMutNq4.js} +1 -1
- package/dist/assets/{sequenceDiagram-3UESZ5HK-BrBpCFjG.js → sequenceDiagram-3UESZ5HK-DXssdhHI.js} +1 -1
- package/dist/assets/{stateDiagram-AJRCARHV-CDZcNxGe.js → stateDiagram-AJRCARHV-jyjJA7Vx.js} +1 -1
- package/dist/assets/stateDiagram-v2-BHNVJYJU-CmxCW1S9.js +1 -0
- package/dist/assets/{timeline-definition-PNZ67QCA-BszoRXfI.js → timeline-definition-PNZ67QCA-CMZ7Ow2L.js} +1 -1
- package/dist/assets/{vennDiagram-CIIHVFJN-CqnUhalx.js → vennDiagram-CIIHVFJN-GcmWlOLV.js} +1 -1
- package/dist/assets/{wardley-L42UT6IY-Cw8Z6Ke3.js → wardley-L42UT6IY-DbIT5rBc.js} +1 -1
- package/dist/assets/{wardleyDiagram-YWT4CUSO-TUrrIhGl.js → wardleyDiagram-YWT4CUSO-gYWXw-3o.js} +1 -1
- package/dist/assets/{xychartDiagram-2RQKCTM6-DWvpHqB8.js → xychartDiagram-2RQKCTM6-BVhFPdW8.js} +1 -1
- package/dist/cli/openlag.js +37 -4
- package/dist/index.html +3 -3
- package/index.html +1 -1
- package/package.json +2 -2
- package/scripts/cli/generate.ts +14 -2
- package/scripts/cli/init.ts +27 -4
- package/scripts/cli/openlag.ts +3 -1
- package/src/App.tsx +3 -2
- package/src/components/DocumentationView.tsx +92 -440
- package/src/components/GraphView.tsx +26 -3
- package/src/store.ts +6 -2
- package/dist/assets/channel-CfEECmqT.js +0 -1
- package/dist/assets/classDiagram-4FO5ZUOK-CqWhRzlO.js +0 -1
- package/dist/assets/classDiagram-v2-Q7XG4LA2-CqWhRzlO.js +0 -1
- package/dist/assets/index-By2WPc1i.js +0 -729
- package/dist/assets/index-CBSt31g-.css +0 -1
- package/dist/assets/stateDiagram-v2-BHNVJYJU-MAgBD3Ln.js +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import React, { useMemo, useState, useEffect
|
|
1
|
+
import React, { useMemo, useState, useEffect } from 'react';
|
|
2
2
|
import { useStore } from '../store';
|
|
3
|
-
import { Artifact
|
|
4
|
-
import { Layers, FileText,
|
|
3
|
+
import { Artifact } from '../types';
|
|
4
|
+
import { Layers, FileText, FileCode2, ShieldCheck, ChevronRight, Search, GitPullRequest, Repeat, Box, Rocket, Activity, Wrench, Trash2, AlertCircle, Printer, Milestone, Bookmark, BookOpen } from 'lucide-react';
|
|
5
5
|
import { MarkdownRenderer } from './MarkdownRenderer';
|
|
6
6
|
|
|
7
7
|
const OwnershipBadge = ({ artifact }: { artifact: Artifact }) => {
|
|
@@ -15,36 +15,21 @@ const OwnershipBadge = ({ artifact }: { artifact: Artifact }) => {
|
|
|
15
15
|
);
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
interface GroupedArtifacts {
|
|
19
|
-
REQUIREMENT: Artifact[];
|
|
20
|
-
USE_CASE: Artifact[];
|
|
21
|
-
DESIGN: Artifact[];
|
|
22
|
-
COMPONENT: Artifact[];
|
|
23
|
-
CODE_ENTITY: Artifact[];
|
|
24
|
-
TEST: Artifact[];
|
|
25
|
-
DOCUMENTATION: Artifact[];
|
|
26
|
-
INCIDENT: Artifact[];
|
|
27
|
-
INFRASTRUCTURE: Artifact[];
|
|
28
|
-
DEPLOYMENT: Artifact[];
|
|
29
|
-
MONITORING: Artifact[];
|
|
30
|
-
MAINTENANCE: Artifact[];
|
|
31
|
-
VERSION: Artifact[];
|
|
32
|
-
SYSTEM_VERSION: Artifact[];
|
|
33
|
-
}
|
|
34
|
-
|
|
35
18
|
const PHASES = [
|
|
19
|
+
{ id: 'strategy', title: 'Strategy & Planning', icon: Bookmark, types: ['PROJECT', 'EPIC', 'FEATURE', 'BUSINESS_RULE', 'DECISION', 'GLOSSARY_TERM', 'RISK', 'PROCESS'] },
|
|
36
20
|
{ id: 'req', title: 'Requirements / Analysis', icon: FileText, types: ['REQUIREMENT', 'USE_CASE'] },
|
|
37
|
-
{ id: 'design', title: '
|
|
38
|
-
{ id: 'dev', title: 'Development', icon: FileCode2, types: ['CODE_ENTITY'] },
|
|
21
|
+
{ id: 'design', title: 'Architecture & Design', icon: Layers, types: ['DESIGN', 'COMPONENT', 'API', 'DATABASE_ENTITY'] },
|
|
22
|
+
{ id: 'dev', title: 'Development', icon: FileCode2, types: ['CODE_ENTITY', 'LIBRARY', 'ENVIRONMENT'] },
|
|
39
23
|
{ id: 'review', title: 'Code Review', icon: GitPullRequest, types: ['CODE_ENTITY'] },
|
|
40
|
-
{ id: 'ci', title: 'Continuous Integration', icon: Repeat, types: ['INFRASTRUCTURE'] },
|
|
41
|
-
{ id: 'verif', title: 'Testing', icon: ShieldCheck, types: ['TEST'] },
|
|
24
|
+
{ id: 'ci', title: 'Continuous Integration', icon: Repeat, types: ['INFRASTRUCTURE', 'PIPELINE'] },
|
|
25
|
+
{ id: 'verif', title: 'Testing / QA', icon: ShieldCheck, types: ['TEST', 'TEST_CASE', 'CHECK', 'BUG'] },
|
|
42
26
|
{ id: 'build', title: 'Build / Packaging', icon: Box, types: ['INFRASTRUCTURE'] },
|
|
43
27
|
{ id: 'deploy', title: 'Deployment', icon: Rocket, types: ['DEPLOYMENT'] },
|
|
44
|
-
{ id: 'monitor', title: 'Monitoring', icon: Activity, types: ['MONITORING', 'INCIDENT'] },
|
|
28
|
+
{ id: 'monitor', title: 'Monitoring & Feedback', icon: Activity, types: ['MONITORING', 'INCIDENT'] },
|
|
29
|
+
{ id: 'docs', title: 'Documentation', icon: BookOpen, types: ['DOCUMENTATION'] },
|
|
45
30
|
{ id: 'maint', title: 'Maintenance / Refactoring', icon: Wrench, types: ['MAINTENANCE'] },
|
|
46
31
|
{ id: 'retire', title: 'Retirement / Replacement', icon: Trash2, types: ['MAINTENANCE'] },
|
|
47
|
-
{ id: 'versions', title: 'Releases & Versions', icon: Milestone, types: ['VERSION', 'SYSTEM_VERSION'] },
|
|
32
|
+
{ id: 'versions', title: 'Releases & Versions', icon: Milestone, types: ['VERSION', 'SYSTEM_VERSION', 'CHANGE'] },
|
|
48
33
|
];
|
|
49
34
|
|
|
50
35
|
export const DocumentationView: React.FC = () => {
|
|
@@ -117,18 +102,13 @@ export const DocumentationView: React.FC = () => {
|
|
|
117
102
|
}, [selectedArtifactId, graph, settings.docsFocusDepth]);
|
|
118
103
|
|
|
119
104
|
const grouped = useMemo(() => {
|
|
120
|
-
const groups:
|
|
121
|
-
REQUIREMENT: [], USE_CASE: [], DESIGN: [], COMPONENT: [],
|
|
122
|
-
CODE_ENTITY: [], TEST: [], DOCUMENTATION: [], INCIDENT: [],
|
|
123
|
-
INFRASTRUCTURE: [], DEPLOYMENT: [], MONITORING: [], MAINTENANCE: [],
|
|
124
|
-
VERSION: [], SYSTEM_VERSION: []
|
|
125
|
-
};
|
|
105
|
+
const groups: Record<string, Artifact[]> = {};
|
|
126
106
|
if (graph && graph.artifacts) {
|
|
127
107
|
graph.artifacts.forEach(a => {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
groups[type].push(a);
|
|
108
|
+
if (!groups[a.type]) {
|
|
109
|
+
groups[a.type] = [];
|
|
131
110
|
}
|
|
111
|
+
groups[a.type].push(a);
|
|
132
112
|
});
|
|
133
113
|
}
|
|
134
114
|
return groups;
|
|
@@ -162,28 +142,17 @@ export const DocumentationView: React.FC = () => {
|
|
|
162
142
|
|
|
163
143
|
const applyFilters = (list: Artifact[] = []) => filterByGraph(filterByLayerAndOwnership(filterBySearch(list)));
|
|
164
144
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
CODE_ENTITY: applyFilters(grouped.CODE_ENTITY),
|
|
171
|
-
TEST: applyFilters(grouped.TEST),
|
|
172
|
-
DOCUMENTATION: applyFilters(grouped.DOCUMENTATION),
|
|
173
|
-
INCIDENT: applyFilters(grouped.INCIDENT),
|
|
174
|
-
INFRASTRUCTURE: applyFilters(grouped.INFRASTRUCTURE),
|
|
175
|
-
DEPLOYMENT: applyFilters(grouped.DEPLOYMENT),
|
|
176
|
-
MONITORING: applyFilters(grouped.MONITORING),
|
|
177
|
-
MAINTENANCE: applyFilters(grouped.MAINTENANCE),
|
|
178
|
-
VERSION: applyFilters(grouped.VERSION),
|
|
179
|
-
SYSTEM_VERSION: applyFilters(grouped.SYSTEM_VERSION),
|
|
180
|
-
};
|
|
145
|
+
const result: Record<string, Artifact[]> = {};
|
|
146
|
+
Object.keys(grouped).forEach(key => {
|
|
147
|
+
result[key] = applyFilters(grouped[key]);
|
|
148
|
+
});
|
|
149
|
+
return result;
|
|
181
150
|
}, [grouped, searchQuery, selectedArtifactId, reachableArtifactIds, isImpactFocusMode, filterLayer, filterOwner, filterTeam]);
|
|
182
151
|
|
|
183
152
|
const phasesData = useMemo(() => {
|
|
184
153
|
return PHASES.map(phase => {
|
|
185
154
|
let filteredArtifactsInPhase = phase.types.flatMap(type =>
|
|
186
|
-
(filteredGroups[type
|
|
155
|
+
(filteredGroups[type] || [])
|
|
187
156
|
);
|
|
188
157
|
|
|
189
158
|
if (phase.id === 'dev') {
|
|
@@ -191,20 +160,19 @@ export const DocumentationView: React.FC = () => {
|
|
|
191
160
|
} else if (phase.id === 'review') {
|
|
192
161
|
filteredArtifactsInPhase = filteredArtifactsInPhase.filter(a => a.subType === 'Review');
|
|
193
162
|
} else if (phase.id === 'ci') {
|
|
194
|
-
filteredArtifactsInPhase = filteredArtifactsInPhase.filter(a => a.
|
|
163
|
+
filteredArtifactsInPhase = filteredArtifactsInPhase.filter(a => a.type === 'PIPELINE' || (a.type === 'INFRASTRUCTURE' && a.subType?.includes('CI')));
|
|
195
164
|
} else if (phase.id === 'build') {
|
|
196
|
-
filteredArtifactsInPhase = filteredArtifactsInPhase.filter(a => a.subType === 'Build' || a.subType === 'Package');
|
|
165
|
+
filteredArtifactsInPhase = filteredArtifactsInPhase.filter(a => a.type === 'INFRASTRUCTURE' && (a.subType === 'Build' || a.subType === 'Package'));
|
|
197
166
|
} else if (phase.id === 'maint') {
|
|
198
|
-
filteredArtifactsInPhase = filteredArtifactsInPhase.filter(a => a.subType !== 'Retirement');
|
|
167
|
+
filteredArtifactsInPhase = filteredArtifactsInPhase.filter(a => a.type === 'MAINTENANCE' && a.subType !== 'Retirement');
|
|
199
168
|
} else if (phase.id === 'retire') {
|
|
200
|
-
filteredArtifactsInPhase = filteredArtifactsInPhase.filter(a => a.subType === 'Retirement');
|
|
169
|
+
filteredArtifactsInPhase = filteredArtifactsInPhase.filter(a => a.type === 'MAINTENANCE' && a.subType === 'Retirement');
|
|
201
170
|
}
|
|
202
171
|
|
|
203
172
|
const subTypes = Array.from(new Set(filteredArtifactsInPhase.map(a => a.subType).filter(Boolean))) as string[];
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const compSubTypes = Array.from(new Set((filteredGroups.COMPONENT || []).map(a => a.subType).filter(Boolean))) as string[];
|
|
173
|
+
|
|
174
|
+
if (phase.id === 'design' && (filteredGroups['COMPONENT'] || []).length > 0) {
|
|
175
|
+
const compSubTypes = Array.from(new Set((filteredGroups['COMPONENT'] || []).map(a => a.subType).filter(Boolean))) as string[];
|
|
208
176
|
for (const s of compSubTypes) {
|
|
209
177
|
if (!subTypes.includes(s)) subTypes.push(s);
|
|
210
178
|
}
|
|
@@ -235,18 +203,9 @@ export const DocumentationView: React.FC = () => {
|
|
|
235
203
|
|
|
236
204
|
const currentVersion = versions.find(v => v.id === currentVersionId);
|
|
237
205
|
|
|
238
|
-
const getRelations = (artifactId: string, type: 'to' | 'from') => {
|
|
239
|
-
return graph.relations.filter(r => type === 'to' ? r.to === artifactId : r.from === artifactId)
|
|
240
|
-
.map(r => {
|
|
241
|
-
const relatedId = type === 'to' ? r.from : r.to;
|
|
242
|
-
const relatedArt = graph.artifacts.find(a => a.id === relatedId);
|
|
243
|
-
return { relation: r, artifact: relatedArt };
|
|
244
|
-
}).filter(r => r.artifact !== undefined);
|
|
245
|
-
};
|
|
246
|
-
|
|
247
206
|
const handlePhaseClick = (phaseId: string) => {
|
|
248
207
|
if (selectedPhase === phaseId && !selectedSubType) {
|
|
249
|
-
setSelectedPhase(null);
|
|
208
|
+
setSelectedPhase(null);
|
|
250
209
|
} else {
|
|
251
210
|
setSelectedPhase(phaseId);
|
|
252
211
|
setSelectedSubType(null);
|
|
@@ -263,19 +222,6 @@ export const DocumentationView: React.FC = () => {
|
|
|
263
222
|
}
|
|
264
223
|
};
|
|
265
224
|
|
|
266
|
-
const hasReqs = (filteredGroups.REQUIREMENT?.length || 0) + (filteredGroups.USE_CASE?.length || 0) > 0;
|
|
267
|
-
const hasDesign = (filteredGroups.DESIGN?.length || 0) + (filteredGroups.COMPONENT?.length || 0) > 0;
|
|
268
|
-
const hasDev = (filteredGroups.CODE_ENTITY || []).filter(c => c.subType !== 'Review').length > 0;
|
|
269
|
-
const hasReview = (filteredGroups.CODE_ENTITY || []).filter(c => c.subType === 'Review').length > 0;
|
|
270
|
-
const hasCI = (filteredGroups.INFRASTRUCTURE || []).filter(i => i.subType?.includes('CI') || i.subType === 'Pipeline').length > 0;
|
|
271
|
-
const hasTest = (filteredGroups.TEST?.length || 0) > 0;
|
|
272
|
-
const hasBuild = (filteredGroups.INFRASTRUCTURE || []).filter(i => i.subType === 'Build' || i.subType === 'Package').length > 0;
|
|
273
|
-
const hasDeploy = (filteredGroups.DEPLOYMENT?.length || 0) > 0;
|
|
274
|
-
const hasMonit = (filteredGroups.MONITORING?.length || 0) + (filteredGroups.INCIDENT?.length || 0) > 0;
|
|
275
|
-
const hasMaint = (filteredGroups.MAINTENANCE || []).filter(m => m.subType !== 'Retirement').length > 0;
|
|
276
|
-
const hasRet = (filteredGroups.MAINTENANCE || []).filter(m => m.subType === 'Retirement').length > 0;
|
|
277
|
-
const hasVersions = (filteredGroups.VERSION?.length || 0) + (filteredGroups.SYSTEM_VERSION?.length || 0) > 0;
|
|
278
|
-
|
|
279
225
|
const shouldShow = (hasItems: boolean) => !(isImpactFocusMode && selectedArtifactId && !hasItems);
|
|
280
226
|
|
|
281
227
|
return (
|
|
@@ -535,371 +481,77 @@ export const DocumentationView: React.FC = () => {
|
|
|
535
481
|
</div>
|
|
536
482
|
|
|
537
483
|
<div className="flex-1 overflow-y-auto px-8 lg:px-16 pb-12 custom-scrollbar">
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
.filter(
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
<span className="text-[10px] bg-red-500/10 text-red-400 px-2 py-1 border border-red-500/20 uppercase tracking-widest flex items-center gap-1">
|
|
557
|
-
<AlertCircle size={10} />
|
|
558
|
-
Traceability Gap
|
|
559
|
-
</span>
|
|
560
|
-
)}
|
|
561
|
-
{req.title}
|
|
562
|
-
</h3>
|
|
563
|
-
<OwnershipBadge artifact={req} />
|
|
564
|
-
<MarkdownRenderer content={req.description} />
|
|
565
|
-
</div>
|
|
566
|
-
))}
|
|
567
|
-
</section>
|
|
568
|
-
)}
|
|
569
|
-
|
|
570
|
-
{/* 2. Technical Design */}
|
|
571
|
-
{(!selectedPhase || selectedPhase === 'design') && shouldShow(hasDesign) && (
|
|
572
|
-
<section className="mb-16">
|
|
573
|
-
<h2 className="text-[10px] uppercase tracking-widest text-white/40 mb-6 bg-white/5 p-3 border-l-2 border-white/20">2. Technical Design</h2>
|
|
574
|
-
{(filteredGroups.DESIGN?.length === 0 && (filteredGroups.COMPONENT?.length || 0) === 0) && <p className="italic text-white/40 text-xs text-center p-12 bg-white/5 border border-dashed border-white/10 rounded">No architectural design for this phase.</p>}
|
|
575
|
-
{(filteredGroups.DESIGN || [])
|
|
576
|
-
.filter(des => !selectedSubType || des.subType === selectedSubType)
|
|
577
|
-
.map(des => (
|
|
578
|
-
<div key={des.id} id={des.id} className="mb-6 bg-[#0c0c0c] border border-white/10 p-5 pl-6 border-l-[3px] border-l-purple-500/50 hover:bg-white-[0.02] transition-colors">
|
|
579
|
-
<h3 className="font-serif text-lg text-white mb-4 flex items-center gap-3">
|
|
580
|
-
<button
|
|
581
|
-
onClick={() => setSelectedArtifact(des.id)}
|
|
582
|
-
className={`text-[10px] font-mono bg-black px-2 py-1 border border-white/5 uppercase tracking-widest hover:border-purple-500/50 transition-colors ${selectedArtifactId === des.id ? 'text-purple-400 border-purple-500/50' : 'text-white/40'}`}
|
|
583
|
-
>
|
|
584
|
-
{des.id}
|
|
585
|
-
</button>
|
|
586
|
-
{orphanArtifactIds.has(des.id) && (
|
|
587
|
-
<span className="text-[8px] bg-red-500/10 text-red-400 px-1.5 py-0.5 border border-red-500/20 uppercase font-mono tracking-tighter flex items-center gap-1">
|
|
588
|
-
<AlertCircle size={10} />
|
|
589
|
-
Orphan
|
|
590
|
-
</span>
|
|
591
|
-
)}
|
|
592
|
-
{des.title}
|
|
593
|
-
</h3>
|
|
594
|
-
<OwnershipBadge artifact={des} />
|
|
595
|
-
<MarkdownRenderer content={des.description} />
|
|
596
|
-
</div>
|
|
597
|
-
))}
|
|
598
|
-
</section>
|
|
599
|
-
)}
|
|
600
|
-
|
|
601
|
-
{/* 3. Development */}
|
|
602
|
-
{(!selectedPhase || selectedPhase === 'dev') && shouldShow(hasDev) && (
|
|
603
|
-
<section className="mb-16">
|
|
604
|
-
<h2 className="text-[10px] uppercase tracking-widest text-white/40 mb-6 bg-white/5 p-3 border-l-2 border-white/20">3. Development</h2>
|
|
605
|
-
<div className="overflow-x-auto rounded border border-white/10">
|
|
606
|
-
<table className="min-w-full border-collapse text-xs">
|
|
607
|
-
<thead className="bg-[#0c0c0c] border-b border-white/10 text-white/40 uppercase tracking-[0.2em] text-[8px] font-bold">
|
|
608
|
-
<tr>
|
|
609
|
-
<th className="p-4 text-left">Code Entity</th>
|
|
610
|
-
<th className="p-4 text-left border-l border-white/5">Mapping</th>
|
|
611
|
-
<th className="p-4 text-left border-l border-white/5 w-1/2">Logic Summary</th>
|
|
612
|
-
</tr>
|
|
613
|
-
</thead>
|
|
614
|
-
<tbody className="divide-y divide-white/5">
|
|
615
|
-
{(filteredGroups.CODE_ENTITY || [])
|
|
616
|
-
.filter(code => code.subType !== 'Review')
|
|
617
|
-
.filter(code => !selectedSubType || code.subType === selectedSubType)
|
|
618
|
-
.map(code => (
|
|
619
|
-
<tr key={code.id} id={code.id} className="bg-transparent hover:bg-white/[0.02] transition-colors group">
|
|
620
|
-
<td className="p-4 font-mono">
|
|
621
|
-
<button
|
|
622
|
-
onClick={() => setSelectedArtifact(code.id)}
|
|
623
|
-
className={`text-left hover:text-emerald-300 transition-colors ${selectedArtifactId === code.id ? 'text-emerald-400' : 'text-emerald-500/70'}`}
|
|
624
|
-
>
|
|
625
|
-
{code.title}
|
|
626
|
-
</button>
|
|
627
|
-
<OwnershipBadge artifact={code} />
|
|
628
|
-
{orphanArtifactIds.has(code.id) && (
|
|
629
|
-
<div className="flex items-center gap-1 text-[8px] text-red-400 mt-0.5 font-bold uppercase tracking-tighter">
|
|
630
|
-
<AlertCircle size={8} />
|
|
631
|
-
Unlinked Entity
|
|
632
|
-
</div>
|
|
633
|
-
)}
|
|
634
|
-
<div className="text-[8px] text-white/20 mt-1 uppercase tracking-tighter">{code.id}</div>
|
|
635
|
-
</td>
|
|
636
|
-
<td className="p-4 border-l border-white/5 text-amber-400/80">{code.subType || '-'}</td>
|
|
637
|
-
<td className="p-4 border-l border-white/5 text-white/60"><MarkdownRenderer content={code.description} /></td>
|
|
638
|
-
</tr>
|
|
639
|
-
))}
|
|
640
|
-
{((filteredGroups.CODE_ENTITY || []).filter(c => c.subType !== 'Review').length === 0) && (
|
|
641
|
-
<tr>
|
|
642
|
-
<td colSpan={3} className="p-12 text-center text-white/20 italic">No code entities documented for this version.</td>
|
|
643
|
-
</tr>
|
|
644
|
-
)}
|
|
645
|
-
</tbody>
|
|
646
|
-
</table>
|
|
647
|
-
</div>
|
|
648
|
-
</section>
|
|
649
|
-
)}
|
|
650
|
-
|
|
651
|
-
{/* 4. Code Review */}
|
|
652
|
-
{(!selectedPhase || selectedPhase === 'review') && shouldShow(hasReview) && (
|
|
653
|
-
<section className="mb-16">
|
|
654
|
-
<h2 className="text-[10px] uppercase tracking-widest text-white/40 mb-6 bg-white/5 p-3 border-l-2 border-white/20">4. Code Review</h2>
|
|
655
|
-
{((filteredGroups.CODE_ENTITY || []).filter(c => c.subType === 'Review').length === 0) && <p className="italic text-white/40 text-xs text-center p-8 bg-white/5 border border-dashed border-white/10 rounded">No active reviews for this version.</p>}
|
|
656
|
-
<div className="grid grid-cols-1 gap-4">
|
|
657
|
-
{(filteredGroups.CODE_ENTITY || []).filter(c => c.subType === 'Review').map(rev => (
|
|
658
|
-
<div key={rev.id} id={rev.id} className="bg-[#0c0c0c] border border-white/10 p-5 pl-6 border-l-[3px] border-l-emerald-500/50">
|
|
659
|
-
<h3 className="font-serif text-lg text-white mb-2 flex items-center gap-3">
|
|
660
|
-
<button
|
|
661
|
-
onClick={() => setSelectedArtifact(rev.id)}
|
|
662
|
-
className={`text-[10px] font-mono bg-black px-2 py-1 border border-white/5 uppercase tracking-widest hover:border-emerald-500/50 transition-colors ${selectedArtifactId === rev.id ? 'text-emerald-400 border-emerald-500/50' : 'text-white/40'}`}
|
|
663
|
-
>
|
|
664
|
-
{rev.id}
|
|
665
|
-
</button>
|
|
666
|
-
{rev.title}
|
|
667
|
-
</h3>
|
|
668
|
-
<MarkdownRenderer content={rev.description} />
|
|
669
|
-
</div>
|
|
670
|
-
))}
|
|
671
|
-
</div>
|
|
672
|
-
</section>
|
|
673
|
-
)}
|
|
674
|
-
|
|
675
|
-
{/* 5. Continuous Integration */}
|
|
676
|
-
{(!selectedPhase || selectedPhase === 'ci') && shouldShow(hasCI) && (
|
|
677
|
-
<section className="mb-16">
|
|
678
|
-
<h2 className="text-[10px] uppercase tracking-widest text-white/40 mb-6 bg-white/5 p-3 border-l-2 border-white/20">5. Continuous Integration (CI)</h2>
|
|
679
|
-
{((filteredGroups.INFRASTRUCTURE || []).filter(i => i.subType?.includes('CI') || i.subType === 'Pipeline').length === 0) && <p className="italic text-white/40 text-xs p-12 bg-white/5 border border-dashed border-white/10 rounded text-center">No CI pipelines defined.</p>}
|
|
680
|
-
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
681
|
-
{(filteredGroups.INFRASTRUCTURE || []).filter(i => i.subType?.includes('CI') || i.subType === 'Pipeline').map(ci => (
|
|
682
|
-
<div key={ci.id} id={ci.id} className="bg-[#0c0c0c] border border-white/10 p-5 hover:bg-white/[0.03] transition-all">
|
|
683
|
-
<h4 className="text-white font-bold mb-2 flex items-center gap-2">
|
|
684
|
-
<Repeat size={14} className="text-emerald-400" />
|
|
685
|
-
{ci.title}
|
|
686
|
-
</h4>
|
|
687
|
-
<MarkdownRenderer content={ci.description} />
|
|
688
|
-
</div>
|
|
689
|
-
))}
|
|
690
|
-
</div>
|
|
691
|
-
</section>
|
|
692
|
-
)}
|
|
693
|
-
|
|
694
|
-
{/* 6. Testing */}
|
|
695
|
-
{(!selectedPhase || selectedPhase === 'verif') && shouldShow(hasTest) && (
|
|
696
|
-
<section className="mb-16">
|
|
697
|
-
<h2 className="text-[10px] uppercase tracking-widest text-white/40 mb-6 bg-white/5 p-3 border-l-2 border-white/20">6. Verification & Quality</h2>
|
|
698
|
-
<div className="space-y-4">
|
|
699
|
-
{(filteredGroups.TEST || []).map(test => (
|
|
700
|
-
<div key={test.id} id={test.id} className="bg-[#0c0c0c] border border-white/10 p-5 border-l-[3px] border-l-rose-500/50">
|
|
701
|
-
<h3 className="font-serif text-[#e0e0e0] mb-2 flex items-center gap-3">
|
|
702
|
-
<button
|
|
703
|
-
onClick={() => setSelectedArtifact(test.id)}
|
|
704
|
-
className={`text-[10px] font-mono bg-black px-2 py-1 border border-white/5 uppercase tracking-widest hover:border-rose-500/50 transition-colors ${selectedArtifactId === test.id ? 'text-rose-400 border-rose-500/50' : 'text-white/40'}`}
|
|
705
|
-
>
|
|
706
|
-
{test.id}
|
|
707
|
-
</button>
|
|
708
|
-
{orphanArtifactIds.has(test.id) && (
|
|
709
|
-
<span className="text-[10px] bg-red-500/10 text-red-400 px-2 py-1 border border-red-500/20 uppercase tracking-widest flex items-center gap-1">
|
|
710
|
-
<AlertCircle size={10} />
|
|
711
|
-
Gap
|
|
712
|
-
</span>
|
|
713
|
-
)}
|
|
714
|
-
{test.title}
|
|
715
|
-
</h3>
|
|
716
|
-
<OwnershipBadge artifact={test} />
|
|
717
|
-
<MarkdownRenderer content={test.description} />
|
|
718
|
-
</div>
|
|
719
|
-
))}
|
|
720
|
-
{(filteredGroups.TEST || []).length === 0 && <p className="italic text-white/40 text-xs">No verification artifacts recorded.</p>}
|
|
721
|
-
</div>
|
|
722
|
-
</section>
|
|
723
|
-
)}
|
|
484
|
+
{phasesData.map((phase, idx) => {
|
|
485
|
+
if (selectedPhase && selectedPhase !== phase.id) return null;
|
|
486
|
+
|
|
487
|
+
let phaseArtifacts = phase.types.flatMap(type => filteredGroups[type] || []);
|
|
488
|
+
|
|
489
|
+
if (phase.id === 'dev') {
|
|
490
|
+
phaseArtifacts = phaseArtifacts.filter(a => a.subType !== 'Review');
|
|
491
|
+
} else if (phase.id === 'review') {
|
|
492
|
+
phaseArtifacts = phaseArtifacts.filter(a => a.subType === 'Review');
|
|
493
|
+
} else if (phase.id === 'ci') {
|
|
494
|
+
phaseArtifacts = phaseArtifacts.filter(a => a.type === 'PIPELINE' || (a.type === 'INFRASTRUCTURE' && a.subType?.includes('CI')));
|
|
495
|
+
} else if (phase.id === 'build') {
|
|
496
|
+
phaseArtifacts = phaseArtifacts.filter(a => a.type === 'INFRASTRUCTURE' && (a.subType === 'Build' || a.subType === 'Package'));
|
|
497
|
+
} else if (phase.id === 'maint') {
|
|
498
|
+
phaseArtifacts = phaseArtifacts.filter(a => a.type === 'MAINTENANCE' && a.subType !== 'Retirement');
|
|
499
|
+
} else if (phase.id === 'retire') {
|
|
500
|
+
phaseArtifacts = phaseArtifacts.filter(a => a.type === 'MAINTENANCE' && a.subType === 'Retirement');
|
|
501
|
+
}
|
|
724
502
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
<h4 className="text-white font-bold mb-2 flex items-center gap-2">
|
|
733
|
-
<Box size={14} className="text-amber-400" />
|
|
734
|
-
{b.title}
|
|
735
|
-
</h4>
|
|
736
|
-
<OwnershipBadge artifact={b} />
|
|
737
|
-
<MarkdownRenderer content={b.description} />
|
|
738
|
-
</div>
|
|
739
|
-
))}
|
|
740
|
-
{((filteredGroups.INFRASTRUCTURE || []).filter(i => i.subType === 'Build' || i.subType === 'Package').length === 0) && <p className="italic text-white/40 text-xs col-span-2">Build configurations are handled externally.</p>}
|
|
741
|
-
</div>
|
|
742
|
-
</section>
|
|
743
|
-
)}
|
|
503
|
+
if (selectedSubType) {
|
|
504
|
+
if (selectedSubType === 'Component') {
|
|
505
|
+
phaseArtifacts = phaseArtifacts.filter(a => a.type === 'COMPONENT' && !a.subType);
|
|
506
|
+
} else {
|
|
507
|
+
phaseArtifacts = phaseArtifacts.filter(a => a.subType === selectedSubType);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
744
510
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
<section className="mb-16">
|
|
748
|
-
<h2 className="text-[10px] uppercase tracking-widest text-white/40 mb-6 bg-white/5 p-3 border-l-2 border-white/20">8. Deployment</h2>
|
|
749
|
-
<div className="space-y-6">
|
|
750
|
-
{(filteredGroups.DEPLOYMENT || []).map(d => (
|
|
751
|
-
<div key={d.id} id={d.id} className="bg-[#0c0c0c] border border-white/10 p-6 rounded-sm border-l-[3px] border-l-sky-500/50">
|
|
752
|
-
<h3 className="text-xl font-serif italic text-white mb-4 flex items-center gap-3">
|
|
753
|
-
<Rocket size={20} className="text-sky-400" />
|
|
754
|
-
<button
|
|
755
|
-
onClick={() => setSelectedArtifact(d.id)}
|
|
756
|
-
className={`text-[10px] font-mono bg-black px-2 py-1 border border-white/5 uppercase tracking-widest hover:border-sky-500/50 transition-colors ${selectedArtifactId === d.id ? 'text-sky-400 border-sky-500/50' : 'text-white/40'}`}
|
|
757
|
-
>
|
|
758
|
-
{d.id}
|
|
759
|
-
</button>
|
|
760
|
-
{orphanArtifactIds.has(d.id) && (
|
|
761
|
-
<span className="text-[10px] bg-red-500/10 text-red-400 px-2 py-1 border border-red-500/20 uppercase tracking-widest flex items-center gap-1">
|
|
762
|
-
<AlertCircle size={10} />
|
|
763
|
-
Unlinked
|
|
764
|
-
</span>
|
|
765
|
-
)}
|
|
766
|
-
{d.title}
|
|
767
|
-
</h3>
|
|
768
|
-
<OwnershipBadge artifact={d} />
|
|
769
|
-
<MarkdownRenderer content={d.description} />
|
|
770
|
-
</div>
|
|
771
|
-
))}
|
|
772
|
-
{(filteredGroups.DEPLOYMENT || []).length === 0 && <p className="italic text-white/40 text-xs">No deployment records found.</p>}
|
|
773
|
-
</div>
|
|
774
|
-
</section>
|
|
775
|
-
)}
|
|
511
|
+
if (!shouldShow(phaseArtifacts.length > 0)) return null;
|
|
512
|
+
if (phaseArtifacts.length === 0 && selectedPhase !== phase.id && !selectedSubType) return null;
|
|
776
513
|
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
{
|
|
794
|
-
|
|
795
|
-
|
|
514
|
+
return (
|
|
515
|
+
<section key={phase.id} className="mb-16">
|
|
516
|
+
<h2 className="text-[10px] uppercase tracking-widest text-white/40 mb-6 bg-white/5 p-3 border-l-2 border-white/20">
|
|
517
|
+
{idx + 1}. {phase.title}
|
|
518
|
+
</h2>
|
|
519
|
+
{phaseArtifacts.length === 0 && (
|
|
520
|
+
<p className="italic text-white/40 text-xs p-8 bg-white/5 border border-dashed border-white/10 rounded text-center">
|
|
521
|
+
No active artifacts for this phase.
|
|
522
|
+
</p>
|
|
523
|
+
)}
|
|
524
|
+
<div className="space-y-4">
|
|
525
|
+
{phaseArtifacts.map((artifact) => (
|
|
526
|
+
<div key={artifact.id} id={artifact.id} className="mb-6 bg-[#0c0c0c] border border-white/10 p-5 pl-6 border-l-[3px] border-l-emerald-500/50 hover:bg-white/[0.02] transition-colors relative group">
|
|
527
|
+
<h3 className="font-serif text-lg text-white flex items-center gap-4 flex-wrap mb-4">
|
|
528
|
+
<button
|
|
529
|
+
onClick={() => setSelectedArtifact(artifact.id)}
|
|
530
|
+
className={`text-[10px] font-mono bg-black px-2 py-1 border border-white/5 uppercase tracking-widest transition-colors hover:border-emerald-500/50 ${selectedArtifactId === artifact.id ? 'text-emerald-400 border-emerald-500/50' : 'text-white/40'}`}
|
|
531
|
+
>
|
|
532
|
+
{artifact.id}
|
|
533
|
+
</button>
|
|
534
|
+
{artifact.subType && <span className="text-[10px] font-sans bg-white/5 text-white/60 px-2 py-1 border border-white/10 uppercase tracking-widest">{artifact.subType}</span>}
|
|
535
|
+
{orphanArtifactIds.has(artifact.id) && (
|
|
796
536
|
<span className="text-[10px] bg-red-500/10 text-red-400 px-2 py-1 border border-red-500/20 uppercase tracking-widest flex items-center gap-1">
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
</span>
|
|
800
|
-
)}
|
|
801
|
-
{m.title}
|
|
802
|
-
</h3>
|
|
803
|
-
<OwnershipBadge artifact={m} />
|
|
804
|
-
<MarkdownRenderer content={m.description} />
|
|
805
|
-
</div>
|
|
806
|
-
))}
|
|
807
|
-
{(filteredGroups.INCIDENT || []).length > 0 && (
|
|
808
|
-
<div className="mt-8">
|
|
809
|
-
<h4 className="text-[10px] uppercase tracking-widest text-red-500/60 font-bold mb-4">Historical Incidents (Feedback Loop)</h4>
|
|
810
|
-
<div className="space-y-3">
|
|
811
|
-
{filteredGroups.INCIDENT.map(inc => (
|
|
812
|
-
<div key={inc.id} id={inc.id} className="bg-red-500/5 border border-red-500/10 p-4 rounded text-xs">
|
|
813
|
-
<span className="font-bold text-red-400 mr-2">[{inc.id}]</span>
|
|
814
|
-
<span className="text-white/80">{inc.title}</span>
|
|
815
|
-
</div>
|
|
816
|
-
))}
|
|
817
|
-
</div>
|
|
818
|
-
</div>
|
|
819
|
-
)}
|
|
820
|
-
</div>
|
|
821
|
-
</section>
|
|
822
|
-
)}
|
|
823
|
-
|
|
824
|
-
{/* 10. Maintenance */}
|
|
825
|
-
{(!selectedPhase || selectedPhase === 'maint') && shouldShow(hasMaint) && (
|
|
826
|
-
<section className="mb-16">
|
|
827
|
-
<h2 className="text-[10px] uppercase tracking-widest text-white/40 mb-6 bg-white/5 p-3 border-l-2 border-white/20">10. Maintenance / Refactoring</h2>
|
|
828
|
-
<div className="space-y-4">
|
|
829
|
-
{(filteredGroups.MAINTENANCE || []).filter(m => m.subType !== 'Retirement').map(m => (
|
|
830
|
-
<div key={m.id} id={m.id} className="bg-[#0c0c0c] border border-white/10 p-5 border-l-[3px] border-l-indigo-500/50">
|
|
831
|
-
<h3 className="text-white font-bold mb-2 flex items-center gap-2">
|
|
832
|
-
<Wrench size={14} className="text-indigo-400" />
|
|
833
|
-
{m.title}
|
|
834
|
-
</h3>
|
|
835
|
-
<OwnershipBadge artifact={m} />
|
|
836
|
-
<MarkdownRenderer content={m.description} />
|
|
837
|
-
</div>
|
|
838
|
-
))}
|
|
839
|
-
{((filteredGroups.MAINTENANCE || []).filter(m => m.subType !== 'Retirement').length === 0) && <p className="italic text-white/40 text-xs">System is in active health. No maintenance tasks scheduled.</p>}
|
|
840
|
-
</div>
|
|
841
|
-
</section>
|
|
842
|
-
)}
|
|
843
|
-
|
|
844
|
-
{/* 11. Retirement */}
|
|
845
|
-
{(!selectedPhase || selectedPhase === 'retire') && shouldShow(hasRet) && (
|
|
846
|
-
<section className="mb-16">
|
|
847
|
-
<h2 className="text-[10px] uppercase tracking-widest text-white/40 mb-6 bg-white/5 p-3 border-l-2 border-white/20">11. Retirement / Replacement</h2>
|
|
848
|
-
<div className="space-y-4">
|
|
849
|
-
{(filteredGroups.MAINTENANCE || []).filter(m => m.subType === 'Retirement').map(m => (
|
|
850
|
-
<div key={m.id} id={m.id} className="bg-[#1a0a0a] border border-red-900/30 p-5 border-l-[3px] border-l-red-900">
|
|
851
|
-
<h3 className="text-white/60 font-bold mb-2 flex items-center gap-2">
|
|
852
|
-
<Trash2 size={14} className="text-red-900" />
|
|
853
|
-
{m.title}
|
|
854
|
-
</h3>
|
|
855
|
-
<OwnershipBadge artifact={m} />
|
|
856
|
-
<MarkdownRenderer content={m.description} />
|
|
857
|
-
</div>
|
|
858
|
-
))}
|
|
859
|
-
{((filteredGroups.MAINTENANCE || []).filter(m => m.subType === 'Retirement').length === 0) && <p className="italic text-white/40 text-xs">No retired components for this version.</p>}
|
|
860
|
-
</div>
|
|
861
|
-
</section>
|
|
862
|
-
)}
|
|
863
|
-
|
|
864
|
-
{/* 12. Releases & Versions */}
|
|
865
|
-
{(!selectedPhase || selectedPhase === 'versions') && (
|
|
866
|
-
<section className="mb-16">
|
|
867
|
-
<h2 className="text-[10px] uppercase tracking-widest text-white/40 mb-6 bg-white/5 p-3 border-l-2 border-white/20">12. Releases & Versions</h2>
|
|
868
|
-
<div className="space-y-4">
|
|
869
|
-
{[...(grouped.VERSION || []), ...(grouped.SYSTEM_VERSION || [])].map(v => (
|
|
870
|
-
<div key={v.id} id={v.id} className="bg-[#0c0c0c] border border-white/10 p-5 border-l-[3px] border-l-emerald-500/50">
|
|
871
|
-
<h3 className="font-serif text-lg text-white mb-2 flex items-center gap-3">
|
|
872
|
-
<Milestone size={16} className="text-emerald-400" />
|
|
873
|
-
<button
|
|
874
|
-
onClick={() => setSelectedArtifact(v.id)}
|
|
875
|
-
className={`text-[10px] font-mono bg-black px-2 py-1 border border-white/5 uppercase tracking-widest hover:border-emerald-500/50 transition-colors ${selectedArtifactId === v.id ? 'text-emerald-400 border-emerald-500/50' : 'text-white/40'}`}
|
|
876
|
-
>
|
|
877
|
-
{v.id}
|
|
878
|
-
</button>
|
|
879
|
-
<span>{v.title}</span>
|
|
880
|
-
{v.type === 'SYSTEM_VERSION' && (
|
|
881
|
-
<span className="ml-auto text-xs font-mono text-emerald-400 border border-emerald-500/30 bg-emerald-500/10 px-2 py-1 rounded">
|
|
882
|
-
{v.component} v{v.version}
|
|
537
|
+
<AlertCircle size={10} />
|
|
538
|
+
Traceability Gap
|
|
883
539
|
</span>
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
<MarkdownRenderer content={v.description} />
|
|
540
|
+
)}
|
|
541
|
+
<span>{artifact.title}</span>
|
|
542
|
+
{artifact.version && <span className="ml-auto text-xs font-mono text-emerald-400 border border-emerald-500/30 bg-emerald-500/10 px-2 py-1 rounded">v{artifact.version}</span>}
|
|
543
|
+
</h3>
|
|
544
|
+
<OwnershipBadge artifact={artifact} />
|
|
545
|
+
<MarkdownRenderer content={artifact.description} />
|
|
546
|
+
</div>
|
|
547
|
+
))}
|
|
893
548
|
</div>
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
)}
|
|
899
|
-
</div>
|
|
549
|
+
</section>
|
|
550
|
+
);
|
|
551
|
+
})}
|
|
552
|
+
</div>
|
|
900
553
|
</div>
|
|
901
554
|
</div>
|
|
902
555
|
</div>
|
|
903
556
|
);
|
|
904
557
|
};
|
|
905
|
-
|