@donartcha/openlag 0.1.2 → 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.
Files changed (62) hide show
  1. package/dist/assets/{arc-BJpiUvSh.js → arc-CQVyn5E3.js} +1 -1
  2. package/dist/assets/{architectureDiagram-3BPJPVTR-Dc7Q7uiQ.js → architectureDiagram-3BPJPVTR-BK7dvXUP.js} +1 -1
  3. package/dist/assets/{blockDiagram-GPEHLZMM-dYx8zJHa.js → blockDiagram-GPEHLZMM-CZACUlID.js} +1 -1
  4. package/dist/assets/{c4Diagram-AAUBKEIU-DEW1mepT.js → c4Diagram-AAUBKEIU-BaIdnoVu.js} +1 -1
  5. package/dist/assets/channel-cdJUve2-.js +1 -0
  6. package/dist/assets/{chunk-2J33WTMH-DI5V_htM.js → chunk-2J33WTMH-I6mjZe22.js} +1 -1
  7. package/dist/assets/{chunk-4BX2VUAB-Dpc1Oo7a.js → chunk-4BX2VUAB-Bni2aQg6.js} +1 -1
  8. package/dist/assets/{chunk-55IACEB6-BVCvy2y7.js → chunk-55IACEB6-BVvD3auX.js} +1 -1
  9. package/dist/assets/{chunk-727SXJPM-CiQJMcWK.js → chunk-727SXJPM-BvRYlYfC.js} +1 -1
  10. package/dist/assets/{chunk-AQP2D5EJ-CfIJf1-T.js → chunk-AQP2D5EJ-DODUUYy0.js} +1 -1
  11. package/dist/assets/{chunk-FMBD7UC4-CSSwZdAo.js → chunk-FMBD7UC4-CSnOrzk-.js} +1 -1
  12. package/dist/assets/{chunk-ND2GUHAM-CpsO12XK.js → chunk-ND2GUHAM-Cwj-8YlS.js} +1 -1
  13. package/dist/assets/{chunk-QZHKN3VN-CWuoRpFf.js → chunk-QZHKN3VN-DvvnV7FS.js} +1 -1
  14. package/dist/assets/classDiagram-4FO5ZUOK-cojzztnL.js +1 -0
  15. package/dist/assets/classDiagram-v2-Q7XG4LA2-cojzztnL.js +1 -0
  16. package/dist/assets/{cose-bilkent-S5V4N54A-COfB4T0K.js → cose-bilkent-S5V4N54A-CpzWWErA.js} +1 -1
  17. package/dist/assets/{dagre-BM42HDAG-C1H0EKAG.js → dagre-BM42HDAG-BoDP4qSL.js} +1 -1
  18. package/dist/assets/{diagram-2AECGRRQ-D6sEKSEZ.js → diagram-2AECGRRQ-DHIQUqF0.js} +1 -1
  19. package/dist/assets/{diagram-5GNKFQAL-Brd_bOCT.js → diagram-5GNKFQAL-xlx_pjV_.js} +1 -1
  20. package/dist/assets/{diagram-KO2AKTUF-Dt0MCanV.js → diagram-KO2AKTUF-B8MO1HgW.js} +1 -1
  21. package/dist/assets/{diagram-LMA3HP47-DQrGF35P.js → diagram-LMA3HP47-CRSJknuZ.js} +1 -1
  22. package/dist/assets/{diagram-OG6HWLK6-Dm7seF-q.js → diagram-OG6HWLK6-CeTvy4fY.js} +1 -1
  23. package/dist/assets/{erDiagram-TEJ5UH35-U6Gl6mUU.js → erDiagram-TEJ5UH35-DFtC0ScP.js} +1 -1
  24. package/dist/assets/{flowDiagram-I6XJVG4X-C7K7oK1X.js → flowDiagram-I6XJVG4X-DMRa3J_m.js} +1 -1
  25. package/dist/assets/{ganttDiagram-6RSMTGT7-CNvz6W3E.js → ganttDiagram-6RSMTGT7-Bxuykf6p.js} +1 -1
  26. package/dist/assets/{gitGraphDiagram-PVQCEYII-CyhbUvLq.js → gitGraphDiagram-PVQCEYII-CXTm84QI.js} +1 -1
  27. package/dist/assets/index-D02phRy3.css +1 -0
  28. package/dist/assets/index-Dvw-BZIp.js +734 -0
  29. package/dist/assets/{infoDiagram-5YYISTIA-DyHSE0hy.js → infoDiagram-5YYISTIA-D2tvLdtC.js} +1 -1
  30. package/dist/assets/{ishikawaDiagram-YF4QCWOH-fjlmrHx3.js → ishikawaDiagram-YF4QCWOH-D0jVP7EP.js} +1 -1
  31. package/dist/assets/{journeyDiagram-JHISSGLW-DOoASPaf.js → journeyDiagram-JHISSGLW-Ca5D6tBE.js} +1 -1
  32. package/dist/assets/{kanban-definition-UN3LZRKU-BEiKB1N_.js → kanban-definition-UN3LZRKU-BQ6UUXz2.js} +1 -1
  33. package/dist/assets/{linear-CPWtktbJ.js → linear-ARjH3_jK.js} +1 -1
  34. package/dist/assets/{mindmap-definition-RKZ34NQL-DliBjc9v.js → mindmap-definition-RKZ34NQL-CRRCJeZm.js} +1 -1
  35. package/dist/assets/{pieDiagram-4H26LBE5-CDu9kWU3.js → pieDiagram-4H26LBE5-BwtLTkTI.js} +1 -1
  36. package/dist/assets/{quadrantDiagram-W4KKPZXB-CEp1tT5r.js → quadrantDiagram-W4KKPZXB-BZ6VcAAZ.js} +1 -1
  37. package/dist/assets/{requirementDiagram-4Y6WPE33-BD2uboSV.js → requirementDiagram-4Y6WPE33-CmuAE86r.js} +1 -1
  38. package/dist/assets/{sankeyDiagram-5OEKKPKP-ZNgAHRIH.js → sankeyDiagram-5OEKKPKP-DRMutNq4.js} +1 -1
  39. package/dist/assets/{sequenceDiagram-3UESZ5HK-BrBpCFjG.js → sequenceDiagram-3UESZ5HK-DXssdhHI.js} +1 -1
  40. package/dist/assets/{stateDiagram-AJRCARHV-CDZcNxGe.js → stateDiagram-AJRCARHV-jyjJA7Vx.js} +1 -1
  41. package/dist/assets/stateDiagram-v2-BHNVJYJU-CmxCW1S9.js +1 -0
  42. package/dist/assets/{timeline-definition-PNZ67QCA-BszoRXfI.js → timeline-definition-PNZ67QCA-CMZ7Ow2L.js} +1 -1
  43. package/dist/assets/{vennDiagram-CIIHVFJN-CqnUhalx.js → vennDiagram-CIIHVFJN-GcmWlOLV.js} +1 -1
  44. package/dist/assets/{wardley-L42UT6IY-Cw8Z6Ke3.js → wardley-L42UT6IY-DbIT5rBc.js} +1 -1
  45. package/dist/assets/{wardleyDiagram-YWT4CUSO-TUrrIhGl.js → wardleyDiagram-YWT4CUSO-gYWXw-3o.js} +1 -1
  46. package/dist/assets/{xychartDiagram-2RQKCTM6-DWvpHqB8.js → xychartDiagram-2RQKCTM6-BVhFPdW8.js} +1 -1
  47. package/dist/cli/openlag.js +34 -3
  48. package/dist/index.html +3 -3
  49. package/index.html +1 -1
  50. package/package.json +2 -2
  51. package/scripts/cli/generate.ts +14 -2
  52. package/scripts/cli/init.ts +27 -4
  53. package/src/App.tsx +3 -2
  54. package/src/components/DocumentationView.tsx +92 -440
  55. package/src/components/GraphView.tsx +26 -3
  56. package/src/store.ts +6 -2
  57. package/dist/assets/channel-CfEECmqT.js +0 -1
  58. package/dist/assets/classDiagram-4FO5ZUOK-CqWhRzlO.js +0 -1
  59. package/dist/assets/classDiagram-v2-Q7XG4LA2-CqWhRzlO.js +0 -1
  60. package/dist/assets/index-By2WPc1i.js +0 -729
  61. package/dist/assets/index-CBSt31g-.css +0 -1
  62. package/dist/assets/stateDiagram-v2-BHNVJYJU-MAgBD3Ln.js +0 -1
@@ -1,7 +1,7 @@
1
- import React, { useMemo, useState, useEffect, useRef } from 'react';
1
+ import React, { useMemo, useState, useEffect } from 'react';
2
2
  import { useStore } from '../store';
3
- import { Artifact, ArtifactType } from '../types';
4
- import { Layers, FileText, Server, FileCode2, ShieldCheck, Stethoscope, ChevronRight, Search, GitPullRequest, Repeat, Box, Rocket, Activity, Wrench, Trash2, AlertCircle, Printer, Download, Milestone } from 'lucide-react';
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: 'Technical Design', icon: Layers, types: ['DESIGN', 'COMPONENT'] },
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: GroupedArtifacts = {
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
- const type = a.type as keyof GroupedArtifacts;
129
- if (groups[type]) {
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
- return {
166
- REQUIREMENT: applyFilters(grouped.REQUIREMENT),
167
- USE_CASE: applyFilters(grouped.USE_CASE),
168
- DESIGN: applyFilters(grouped.DESIGN),
169
- COMPONENT: applyFilters(grouped.COMPONENT),
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 as keyof typeof filteredGroups] || [])
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.subType?.includes('CI') || a.subType === 'Pipeline');
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
- // We will also synthesize "COMPONENT" as a pseudo subtype for Design since we grouped DESIGN and COMPONENT together in phase 2,
205
- // but only if there are COMPONENT artifacts and they don't already have subTypes.
206
- if (phase.id === 'design' && (filteredGroups.COMPONENT || []).length > 0) {
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); // toggle off
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
- {/* 1. Requirements */}
539
- {(!selectedPhase || selectedPhase === 'req') && shouldShow(hasReqs) && (
540
- <section className="mb-16">
541
- <h2 className="text-[10px] uppercase tracking-widest text-white/40 mb-6 bg-white/5 p-3 border-l-2 border-white/20">1. Requirements / Analysis</h2>
542
- {(filteredGroups.REQUIREMENT?.length === 0 && filteredGroups.USE_CASE?.length === 0) && <p className="italic text-white/40 text-xs">No requirements found in this version.</p>}
543
- {[...(filteredGroups.REQUIREMENT || []), ...(filteredGroups.USE_CASE || [])]
544
- .filter(req => !selectedSubType || req.subType === selectedSubType)
545
- .map(req => (
546
- <div key={req.id} id={req.id} className="mb-6 bg-[#0c0c0c] border border-white/10 p-5 pl-6 border-l-[3px] border-l-blue-500/50 hover:bg-white-[0.02] transition-colors relative group">
547
- <h3 className="font-serif text-lg text-white flex items-center gap-4 flex-wrap mb-4">
548
- <button
549
- onClick={() => setSelectedArtifact(req.id)}
550
- className={`text-[10px] font-mono bg-black px-2 py-1 border border-white/5 uppercase tracking-widest hover:border-blue-500/50 transition-colors ${selectedArtifactId === req.id ? 'text-blue-400 border-blue-500/50' : 'text-white/40'}`}
551
- >
552
- {req.id}
553
- </button>
554
- {req.subType && <span className="text-[10px] font-sans bg-blue-500/10 text-blue-300 px-2 py-1 border border-blue-500/20 uppercase tracking-widest">{req.subType}</span>}
555
- {orphanArtifactIds.has(req.id) && (
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
- {/* 7. Build */}
726
- {(!selectedPhase || selectedPhase === 'build') && shouldShow(hasBuild) && (
727
- <section className="mb-16">
728
- <h2 className="text-[10px] uppercase tracking-widest text-white/40 mb-6 bg-white/5 p-3 border-l-2 border-white/20">7. Build & Packaging</h2>
729
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
730
- {(filteredGroups.INFRASTRUCTURE || []).filter(i => i.subType === 'Build' || i.subType === 'Package').map(b => (
731
- <div key={b.id} id={b.id} className="bg-[#0c0c0c] border border-white/10 p-5 border-l-[3px] border-l-amber-500/50">
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
- {/* 8. Deployment */}
746
- {(!selectedPhase || selectedPhase === 'deploy') && shouldShow(hasDeploy) && (
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
- {/* 9. Monitoring */}
778
- {(!selectedPhase || selectedPhase === 'monitor') && shouldShow(hasMonit) && (
779
- <section className="mb-16">
780
- <h2 className="text-[10px] uppercase tracking-widest text-white/40 mb-6 bg-white/5 p-3 border-l-2 border-white/20">9. Monitoring & Observability</h2>
781
- <div className="grid grid-cols-1 gap-6">
782
- {(filteredGroups.MONITORING || []).map(m => (
783
- <div key={m.id} id={m.id} className="bg-black/40 border border-white/10 p-6 rounded relative overflow-hidden">
784
- <div className="absolute top-0 right-0 p-4 opacity-5">
785
- <Activity size={80} />
786
- </div>
787
- <h3 className="text-white font-bold mb-3 flex items-center gap-2">
788
- <Activity size={16} className="text-red-400" />
789
- <button
790
- onClick={() => setSelectedArtifact(m.id)}
791
- className={`text-[10px] font-mono bg-black/40 px-2 py-1 border border-white/5 uppercase tracking-widest hover:border-red-400/50 transition-colors ${selectedArtifactId === m.id ? 'text-red-400 border-red-500/50' : 'text-white/40'}`}
792
- >
793
- {m.id}
794
- </button>
795
- {orphanArtifactIds.has(m.id) && (
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
- <AlertCircle size={10} />
798
- Unlinked
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
- {v.type === 'VERSION' && (
886
- <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">
887
- v{v.version}
888
- </span>
889
- )}
890
- </h3>
891
- <OwnershipBadge artifact={v} />
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
- {([...(grouped.VERSION || []), ...(grouped.SYSTEM_VERSION || [])].length === 0) && <p className="italic text-white/40 text-xs">No version artifacts.</p>}
896
- </div>
897
- </section>
898
- )}
899
- </div>
549
+ </section>
550
+ );
551
+ })}
552
+ </div>
900
553
  </div>
901
554
  </div>
902
555
  </div>
903
556
  );
904
557
  };
905
-