@donartcha/openlag 0.1.4 → 0.1.5
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-5hssXHF1.js → arc-Cg8uAUBB.js} +1 -1
- package/dist/assets/{architectureDiagram-3BPJPVTR-CoCQ7I8T.js → architectureDiagram-3BPJPVTR-BS-jlSix.js} +1 -1
- package/dist/assets/{blockDiagram-GPEHLZMM-Ce3SBFaK.js → blockDiagram-GPEHLZMM-piJhj2Kl.js} +1 -1
- package/dist/assets/{c4Diagram-AAUBKEIU-Cxiak8OI.js → c4Diagram-AAUBKEIU-CUkupHTt.js} +1 -1
- package/dist/assets/channel-Dg6EjY0N.js +1 -0
- package/dist/assets/{chunk-2J33WTMH-CI2-2ImK.js → chunk-2J33WTMH-DK1RvYbJ.js} +1 -1
- package/dist/assets/{chunk-4BX2VUAB-CK7HADOu.js → chunk-4BX2VUAB-DkbrtaN7.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-DhMUe2Z8.js → chunk-55IACEB6-DeeqmVSw.js} +1 -1
- package/dist/assets/{chunk-727SXJPM-Ceirad8r.js → chunk-727SXJPM-BuaIQz3A.js} +1 -1
- package/dist/assets/{chunk-AQP2D5EJ-5f7TUd6D.js → chunk-AQP2D5EJ-BXTLYgxd.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-DGLw7-i0.js → chunk-FMBD7UC4-hrXWkcdb.js} +1 -1
- package/dist/assets/{chunk-ND2GUHAM-bdCVW2vW.js → chunk-ND2GUHAM-D6pYSJIp.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-Cvl6Nxaw.js → chunk-QZHKN3VN-DC-A3Aq8.js} +1 -1
- package/dist/assets/classDiagram-4FO5ZUOK-C_u0dh5t.js +1 -0
- package/dist/assets/classDiagram-v2-Q7XG4LA2-C_u0dh5t.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-D5hPTlJh.js → cose-bilkent-S5V4N54A-DsxxO59f.js} +1 -1
- package/dist/assets/{dagre-BM42HDAG-EaLHJBg9.js → dagre-BM42HDAG-BfiDIYHL.js} +1 -1
- package/dist/assets/{diagram-2AECGRRQ-B03rLzzC.js → diagram-2AECGRRQ-DtT3XImB.js} +1 -1
- package/dist/assets/{diagram-5GNKFQAL-BMGFDeYz.js → diagram-5GNKFQAL-DJFNnWg9.js} +1 -1
- package/dist/assets/{diagram-KO2AKTUF-C67gpPIs.js → diagram-KO2AKTUF-nhyTgQNk.js} +1 -1
- package/dist/assets/{diagram-LMA3HP47-BsR8n_gX.js → diagram-LMA3HP47-Dlx64Gbc.js} +1 -1
- package/dist/assets/{diagram-OG6HWLK6-B1zv47yy.js → diagram-OG6HWLK6-BtZhjKp4.js} +1 -1
- package/dist/assets/{erDiagram-TEJ5UH35-xXOEdzQg.js → erDiagram-TEJ5UH35-DCjMay5k.js} +1 -1
- package/dist/assets/{flowDiagram-I6XJVG4X-DcjP3-zz.js → flowDiagram-I6XJVG4X-VUmu-Ulq.js} +1 -1
- package/dist/assets/{ganttDiagram-6RSMTGT7-D9QN8XP5.js → ganttDiagram-6RSMTGT7-DubywubS.js} +1 -1
- package/dist/assets/{gitGraphDiagram-PVQCEYII-Cb8pk6QE.js → gitGraphDiagram-PVQCEYII-PcO-oJqF.js} +1 -1
- package/dist/assets/index-BhnK9sdD.css +1 -0
- package/dist/assets/{index-wflyqKCW.js → index-BxBZmqgf.js} +132 -135
- package/dist/assets/{infoDiagram-5YYISTIA-CudTuB2i.js → infoDiagram-5YYISTIA-BZuhNKak.js} +1 -1
- package/dist/assets/{ishikawaDiagram-YF4QCWOH-CYrMjwuu.js → ishikawaDiagram-YF4QCWOH-BXwOxb-L.js} +1 -1
- package/dist/assets/{journeyDiagram-JHISSGLW-DOODfUXV.js → journeyDiagram-JHISSGLW-C9YvQpLx.js} +1 -1
- package/dist/assets/{kanban-definition-UN3LZRKU-CQz0IKkV.js → kanban-definition-UN3LZRKU-BL-bN75y.js} +1 -1
- package/dist/assets/{linear-0fFjcdIi.js → linear-ro7DcLah.js} +1 -1
- package/dist/assets/{mindmap-definition-RKZ34NQL-BnFimeAu.js → mindmap-definition-RKZ34NQL-TyafSJyD.js} +1 -1
- package/dist/assets/{pieDiagram-4H26LBE5-CMoHxt1c.js → pieDiagram-4H26LBE5-CGF8I-Jg.js} +1 -1
- package/dist/assets/{quadrantDiagram-W4KKPZXB-C5mSbaI8.js → quadrantDiagram-W4KKPZXB-gPuTV6yC.js} +1 -1
- package/dist/assets/{requirementDiagram-4Y6WPE33-CeADpKMZ.js → requirementDiagram-4Y6WPE33-DMNEMII6.js} +1 -1
- package/dist/assets/{sankeyDiagram-5OEKKPKP-ELntQlI8.js → sankeyDiagram-5OEKKPKP-CcLp1J1q.js} +1 -1
- package/dist/assets/{sequenceDiagram-3UESZ5HK-5mkiPlow.js → sequenceDiagram-3UESZ5HK-CRQIa-m4.js} +1 -1
- package/dist/assets/{stateDiagram-AJRCARHV-DbNfJB9W.js → stateDiagram-AJRCARHV-BCDHz4Yx.js} +1 -1
- package/dist/assets/stateDiagram-v2-BHNVJYJU-Cyj4KCTb.js +1 -0
- package/dist/assets/{timeline-definition-PNZ67QCA-C8k4W5sA.js → timeline-definition-PNZ67QCA-BZBzVnMe.js} +1 -1
- package/dist/assets/{vennDiagram-CIIHVFJN-eO-TQun1.js → vennDiagram-CIIHVFJN-BLb-Z_mb.js} +1 -1
- package/dist/assets/{wardley-L42UT6IY-BuAaO8uL.js → wardley-L42UT6IY-DqVhxf9g.js} +1 -1
- package/dist/assets/{wardleyDiagram-YWT4CUSO-CTutletf.js → wardleyDiagram-YWT4CUSO-cmFDR9p-.js} +1 -1
- package/dist/assets/{xychartDiagram-2RQKCTM6-wrRC0RBX.js → xychartDiagram-2RQKCTM6-DmAwTFQ_.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/components/DocumentationView.tsx +83 -82
- package/src/components/GraphView.tsx +23 -11
- package/src/components/GuideView.tsx +5 -3
- package/src/components/ImpactView.tsx +10 -6
- package/src/components/OrphansView.tsx +8 -5
- package/src/utils/artifactUtils.ts +93 -0
- package/dist/assets/channel-BGwATj28.js +0 -1
- package/dist/assets/classDiagram-4FO5ZUOK-xQe3lOVv.js +0 -1
- package/dist/assets/classDiagram-v2-Q7XG4LA2-xQe3lOVv.js +0 -1
- package/dist/assets/index-D02phRy3.css +0 -1
- package/dist/assets/stateDiagram-v2-BHNVJYJU-hHz3nsFQ.js +0 -1
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
import React, { useMemo, useState, useEffect } from 'react';
|
|
2
2
|
import { useStore } from '../store';
|
|
3
3
|
import { Artifact } from '../types';
|
|
4
|
+
import { getArtifactLayer, getArtifactOwner, getArtifactTeam } from '../utils/artifactUtils';
|
|
4
5
|
import { Layers, FileText, FileCode2, ShieldCheck, ChevronRight, Search, GitPullRequest, Repeat, Box, Rocket, Activity, Wrench, Trash2, AlertCircle, Printer, Milestone, Bookmark, BookOpen } from 'lucide-react';
|
|
5
6
|
import { MarkdownRenderer } from './MarkdownRenderer';
|
|
6
7
|
|
|
7
8
|
const OwnershipBadge = ({ artifact }: { artifact: Artifact }) => {
|
|
8
|
-
|
|
9
|
+
const { fullGraph } = useStore();
|
|
10
|
+
const computedLayer = getArtifactLayer(artifact);
|
|
11
|
+
const computedOwner = getArtifactOwner(artifact, fullGraph);
|
|
12
|
+
const computedTeam = getArtifactTeam(artifact, fullGraph);
|
|
13
|
+
|
|
14
|
+
if (!computedLayer && !computedOwner && !computedTeam) return null;
|
|
9
15
|
return (
|
|
10
16
|
<div className="flex gap-2 mb-3 mt-1 pointer-events-none">
|
|
11
|
-
{
|
|
12
|
-
{
|
|
13
|
-
{
|
|
17
|
+
{computedLayer && <span className="text-[9px] text-purple-400 border border-purple-400/20 bg-purple-400/5 px-1.5 py-0.5 rounded-sm font-mono tracking-tighter">LAYER: {computedLayer}</span>}
|
|
18
|
+
{computedOwner && <span className="text-[9px] text-blue-400 border border-blue-400/20 bg-blue-400/5 px-1.5 py-0.5 rounded-sm font-mono tracking-tighter">OWNER: {computedOwner}</span>}
|
|
19
|
+
{computedTeam && <span className="text-[9px] text-emerald-400 border border-emerald-400/20 bg-emerald-400/5 px-1.5 py-0.5 rounded-sm font-mono tracking-tighter">TEAM: {computedTeam}</span>}
|
|
14
20
|
</div>
|
|
15
21
|
);
|
|
16
22
|
};
|
|
@@ -20,15 +26,12 @@ const PHASES = [
|
|
|
20
26
|
{ id: 'req', title: 'Requirements / Analysis', icon: FileText, types: ['REQUIREMENT', 'USE_CASE'] },
|
|
21
27
|
{ id: 'design', title: 'Architecture & Design', icon: Layers, types: ['DESIGN', 'COMPONENT', 'API', 'DATABASE_ENTITY'] },
|
|
22
28
|
{ id: 'dev', title: 'Development', icon: FileCode2, types: ['CODE_ENTITY', 'LIBRARY', 'ENVIRONMENT'] },
|
|
23
|
-
{ id: '
|
|
24
|
-
{ id: '
|
|
25
|
-
{ id: 'verif', title: 'Testing / QA', icon: ShieldCheck, types: ['TEST', 'TEST_CASE', 'CHECK', 'BUG'] },
|
|
26
|
-
{ id: 'build', title: 'Build / Packaging', icon: Box, types: ['INFRASTRUCTURE'] },
|
|
29
|
+
{ id: 'ci', title: 'Continuous Integration / Build', icon: Repeat, types: ['PIPELINE', 'INFRASTRUCTURE'] },
|
|
30
|
+
{ id: 'verif', title: 'Testing / QA', icon: ShieldCheck, types: ['TEST_CASE', 'CHECK', 'BUG'] },
|
|
27
31
|
{ id: 'deploy', title: 'Deployment', icon: Rocket, types: ['DEPLOYMENT'] },
|
|
28
32
|
{ id: 'monitor', title: 'Monitoring & Feedback', icon: Activity, types: ['MONITORING', 'INCIDENT'] },
|
|
29
33
|
{ id: 'docs', title: 'Documentation', icon: BookOpen, types: ['DOCUMENTATION'] },
|
|
30
34
|
{ id: 'maint', title: 'Maintenance / Refactoring', icon: Wrench, types: ['MAINTENANCE'] },
|
|
31
|
-
{ id: 'retire', title: 'Retirement / Replacement', icon: Trash2, types: ['MAINTENANCE'] },
|
|
32
35
|
{ id: 'versions', title: 'Releases & Versions', icon: Milestone, types: ['VERSION', 'SYSTEM_VERSION', 'CHANGE'] },
|
|
33
36
|
];
|
|
34
37
|
|
|
@@ -53,9 +56,9 @@ export const DocumentationView: React.FC = () => {
|
|
|
53
56
|
|
|
54
57
|
const filterOptions = useMemo(() => {
|
|
55
58
|
if (!graph || !graph.artifacts) return { layers: [], owners: [], teams: [] };
|
|
56
|
-
const layers = Array.from(new Set(graph.artifacts.map(a => a
|
|
57
|
-
const owners = Array.from(new Set(graph.artifacts.map(a => a
|
|
58
|
-
const teams = Array.from(new Set(graph.artifacts.map(a => a
|
|
59
|
+
const layers = Array.from(new Set(graph.artifacts.map(a => getArtifactLayer(a)).filter(Boolean))) as string[];
|
|
60
|
+
const owners = Array.from(new Set(graph.artifacts.map(a => getArtifactOwner(a, graph)).filter(Boolean))) as string[];
|
|
61
|
+
const teams = Array.from(new Set(graph.artifacts.map(a => getArtifactTeam(a, graph)).filter(Boolean))) as string[];
|
|
59
62
|
return { layers, owners, teams };
|
|
60
63
|
}, [graph]);
|
|
61
64
|
|
|
@@ -133,9 +136,12 @@ export const DocumentationView: React.FC = () => {
|
|
|
133
136
|
|
|
134
137
|
const filterByLayerAndOwnership = (list: Artifact[] = []) => {
|
|
135
138
|
return list.filter(a => {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
+
const computedLayer = getArtifactLayer(a);
|
|
140
|
+
const computedOwner = getArtifactOwner(a, graph);
|
|
141
|
+
const computedTeam = getArtifactTeam(a, graph);
|
|
142
|
+
if (filterLayer !== 'ALL' && computedLayer !== filterLayer) return false;
|
|
143
|
+
if (filterOwner !== 'ALL' && computedOwner !== filterOwner) return false;
|
|
144
|
+
if (filterTeam !== 'ALL' && computedTeam !== filterTeam) return false;
|
|
139
145
|
return true;
|
|
140
146
|
});
|
|
141
147
|
};
|
|
@@ -155,31 +161,8 @@ export const DocumentationView: React.FC = () => {
|
|
|
155
161
|
(filteredGroups[type] || [])
|
|
156
162
|
);
|
|
157
163
|
|
|
158
|
-
if (phase.id === 'dev') {
|
|
159
|
-
filteredArtifactsInPhase = filteredArtifactsInPhase.filter(a => a.subType !== 'Review');
|
|
160
|
-
} else if (phase.id === 'review') {
|
|
161
|
-
filteredArtifactsInPhase = filteredArtifactsInPhase.filter(a => a.subType === 'Review');
|
|
162
|
-
} else if (phase.id === 'ci') {
|
|
163
|
-
filteredArtifactsInPhase = filteredArtifactsInPhase.filter(a => a.type === 'PIPELINE' || (a.type === 'INFRASTRUCTURE' && a.subType?.includes('CI')));
|
|
164
|
-
} else if (phase.id === 'build') {
|
|
165
|
-
filteredArtifactsInPhase = filteredArtifactsInPhase.filter(a => a.type === 'INFRASTRUCTURE' && (a.subType === 'Build' || a.subType === 'Package'));
|
|
166
|
-
} else if (phase.id === 'maint') {
|
|
167
|
-
filteredArtifactsInPhase = filteredArtifactsInPhase.filter(a => a.type === 'MAINTENANCE' && a.subType !== 'Retirement');
|
|
168
|
-
} else if (phase.id === 'retire') {
|
|
169
|
-
filteredArtifactsInPhase = filteredArtifactsInPhase.filter(a => a.type === 'MAINTENANCE' && a.subType === 'Retirement');
|
|
170
|
-
}
|
|
171
|
-
|
|
172
164
|
const subTypes = Array.from(new Set(filteredArtifactsInPhase.map(a => a.subType).filter(Boolean))) as string[];
|
|
173
165
|
|
|
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[];
|
|
176
|
-
for (const s of compSubTypes) {
|
|
177
|
-
if (!subTypes.includes(s)) subTypes.push(s);
|
|
178
|
-
}
|
|
179
|
-
if (compSubTypes.length === 0 && !subTypes.includes('Component')) {
|
|
180
|
-
subTypes.push('Component');
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
166
|
return {
|
|
184
167
|
...phase,
|
|
185
168
|
count: filteredArtifactsInPhase.length,
|
|
@@ -486,26 +469,8 @@ export const DocumentationView: React.FC = () => {
|
|
|
486
469
|
|
|
487
470
|
let phaseArtifacts = phase.types.flatMap(type => filteredGroups[type] || []);
|
|
488
471
|
|
|
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
|
-
}
|
|
502
|
-
|
|
503
472
|
if (selectedSubType) {
|
|
504
|
-
|
|
505
|
-
phaseArtifacts = phaseArtifacts.filter(a => a.type === 'COMPONENT' && !a.subType);
|
|
506
|
-
} else {
|
|
507
|
-
phaseArtifacts = phaseArtifacts.filter(a => a.subType === selectedSubType);
|
|
508
|
-
}
|
|
473
|
+
phaseArtifacts = phaseArtifacts.filter(a => a.subType === selectedSubType);
|
|
509
474
|
}
|
|
510
475
|
|
|
511
476
|
if (!shouldShow(phaseArtifacts.length > 0)) return null;
|
|
@@ -513,7 +478,7 @@ export const DocumentationView: React.FC = () => {
|
|
|
513
478
|
|
|
514
479
|
return (
|
|
515
480
|
<section key={phase.id} className="mb-16">
|
|
516
|
-
<h2 className="text-
|
|
481
|
+
<h2 className="text-xl font-serif text-white mb-6 p-4 bg-white/5 border-l-4 border-emerald-500/50">
|
|
517
482
|
{idx + 1}. {phase.title}
|
|
518
483
|
</h2>
|
|
519
484
|
{phaseArtifacts.length === 0 && (
|
|
@@ -521,30 +486,66 @@ export const DocumentationView: React.FC = () => {
|
|
|
521
486
|
No active artifacts for this phase.
|
|
522
487
|
</p>
|
|
523
488
|
)}
|
|
524
|
-
<div className="space-y-
|
|
525
|
-
{
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
489
|
+
<div className="space-y-12">
|
|
490
|
+
{(() => {
|
|
491
|
+
// Extract and group artifacts by subType
|
|
492
|
+
const groupedBySubType: Record<string, Artifact[]> = {};
|
|
493
|
+
const noSubTypeArtifacts: Artifact[] = [];
|
|
494
|
+
|
|
495
|
+
phaseArtifacts.forEach(a => {
|
|
496
|
+
if (a.subType) {
|
|
497
|
+
if (!groupedBySubType[a.subType]) groupedBySubType[a.subType] = [];
|
|
498
|
+
groupedBySubType[a.subType].push(a);
|
|
499
|
+
} else {
|
|
500
|
+
noSubTypeArtifacts.push(a);
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
const renderArtifacts = (artifactsToRender: Artifact[]) => (
|
|
505
|
+
<div className="space-y-6">
|
|
506
|
+
{artifactsToRender.map(artifact => (
|
|
507
|
+
<div key={artifact.id} id={artifact.id} className="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 print-block-break-inside-avoid">
|
|
508
|
+
<h3 className="font-serif text-lg text-white flex items-center gap-4 flex-wrap mb-4">
|
|
509
|
+
<button
|
|
510
|
+
onClick={() => setSelectedArtifact(artifact.id)}
|
|
511
|
+
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'}`}
|
|
512
|
+
>
|
|
513
|
+
{artifact.id}
|
|
514
|
+
</button>
|
|
515
|
+
{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>}
|
|
516
|
+
{orphanArtifactIds.has(artifact.id) && (
|
|
517
|
+
<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">
|
|
518
|
+
<AlertCircle size={10} />
|
|
519
|
+
Traceability Gap
|
|
520
|
+
</span>
|
|
521
|
+
)}
|
|
522
|
+
<span>{artifact.title}</span>
|
|
523
|
+
{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>}
|
|
524
|
+
</h3>
|
|
525
|
+
<OwnershipBadge artifact={artifact} />
|
|
526
|
+
<MarkdownRenderer content={artifact.description} />
|
|
527
|
+
</div>
|
|
528
|
+
))}
|
|
529
|
+
</div>
|
|
530
|
+
);
|
|
531
|
+
|
|
532
|
+
return (
|
|
533
|
+
<>
|
|
534
|
+
{noSubTypeArtifacts.length > 0 && renderArtifacts(noSubTypeArtifacts)}
|
|
535
|
+
{Object.entries(groupedBySubType)
|
|
536
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
537
|
+
.map(([subType, stArtifacts]) => (
|
|
538
|
+
<div key={subType} className="mt-8">
|
|
539
|
+
<h3 className="text-xs uppercase tracking-widest text-emerald-400/80 mb-4 flex items-center gap-2">
|
|
540
|
+
<Layers size={14} />
|
|
541
|
+
{subType}
|
|
542
|
+
</h3>
|
|
543
|
+
{renderArtifacts(stArtifacts)}
|
|
544
|
+
</div>
|
|
545
|
+
))}
|
|
546
|
+
</>
|
|
547
|
+
);
|
|
548
|
+
})()}
|
|
548
549
|
</div>
|
|
549
550
|
</section>
|
|
550
551
|
);
|
|
@@ -6,15 +6,16 @@ import Markdown from 'react-markdown';
|
|
|
6
6
|
import '@xyflow/react/dist/style.css';
|
|
7
7
|
import { useStore } from '../store';
|
|
8
8
|
import { ArtifactType } from '../types';
|
|
9
|
+
import { getArtifactLayer, getArtifactOwner, getArtifactTeam } from '../utils/artifactUtils';
|
|
9
10
|
|
|
10
11
|
const typeColors: Record<ArtifactType, string> = {
|
|
11
12
|
REQUIREMENT: 'text-blue-400 border-blue-400',
|
|
12
13
|
USE_CASE: 'text-indigo-400 border-indigo-400',
|
|
13
14
|
DESIGN: 'text-purple-400 border-purple-400',
|
|
14
|
-
COMPONENT: 'text-amber-400 border-amber-400',
|
|
15
|
-
CODE_ENTITY: 'text-emerald-400 border-emerald-400',
|
|
16
|
-
TEST: 'text-rose-400 border-rose-400',
|
|
17
|
-
DOCUMENTATION: 'text-slate-400 border-slate-400',
|
|
15
|
+
COMPONENT: 'text-amber-400 border-amber-400',
|
|
16
|
+
CODE_ENTITY: 'text-emerald-400 border-emerald-400',
|
|
17
|
+
TEST: 'text-rose-400 border-rose-400',
|
|
18
|
+
DOCUMENTATION: 'text-slate-400 border-slate-400',
|
|
18
19
|
INCIDENT: 'text-red-400 border-red-400',
|
|
19
20
|
INFRASTRUCTURE: 'text-cyan-400 border-cyan-400',
|
|
20
21
|
DEPLOYMENT: 'text-sky-400 border-sky-400',
|
|
@@ -311,9 +312,12 @@ const GraphFlow: React.FC = () => {
|
|
|
311
312
|
isConnectedToSelected = connectedNodes.has(a.id);
|
|
312
313
|
}
|
|
313
314
|
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
315
|
+
const computedLayer = getArtifactLayer(a);
|
|
316
|
+
const computedOwner = getArtifactOwner(a, fullGraph);
|
|
317
|
+
const computedTeam = getArtifactTeam(a, fullGraph);
|
|
318
|
+
const isFilteredOut = (filterLayer !== 'ALL' && computedLayer !== filterLayer) ||
|
|
319
|
+
(filterOwner !== 'ALL' && computedOwner !== filterOwner) ||
|
|
320
|
+
(filterTeam !== 'ALL' && computedTeam !== filterTeam);
|
|
317
321
|
|
|
318
322
|
const isDimmed = isFilteredOut || (selectedArtifactId !== null && !isSelected && !isConnectedToSelected);
|
|
319
323
|
const isOrphan = orphanIds.has(a.id);
|
|
@@ -334,9 +338,16 @@ const GraphFlow: React.FC = () => {
|
|
|
334
338
|
|
|
335
339
|
const sourceNode = graph.artifacts.find(a => a.id === r.from);
|
|
336
340
|
const targetNode = graph.artifacts.find(a => a.id === r.to);
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
341
|
+
const sourceLayer = sourceNode ? getArtifactLayer(sourceNode) : undefined;
|
|
342
|
+
const targetLayer = targetNode ? getArtifactLayer(targetNode) : undefined;
|
|
343
|
+
const sourceOwner = sourceNode ? getArtifactOwner(sourceNode, fullGraph) : undefined;
|
|
344
|
+
const targetOwner = targetNode ? getArtifactOwner(targetNode, fullGraph) : undefined;
|
|
345
|
+
const sourceTeam = sourceNode ? getArtifactTeam(sourceNode, fullGraph) : undefined;
|
|
346
|
+
const targetTeam = targetNode ? getArtifactTeam(targetNode, fullGraph) : undefined;
|
|
347
|
+
|
|
348
|
+
const isFilteredOut = (filterLayer !== 'ALL' && (sourceLayer !== filterLayer || targetLayer !== filterLayer)) ||
|
|
349
|
+
(filterOwner !== 'ALL' && (sourceOwner !== filterOwner || targetOwner !== filterOwner)) ||
|
|
350
|
+
(filterTeam !== 'ALL' && (sourceTeam !== filterTeam || targetTeam !== filterTeam));
|
|
340
351
|
|
|
341
352
|
const isDimmed = isFilteredOut || (selectedArtifactId !== null && !isConnectedToSelected);
|
|
342
353
|
|
|
@@ -549,4 +560,5 @@ export const GraphView: React.FC = () => {
|
|
|
549
560
|
<GraphFlow />
|
|
550
561
|
</ReactFlowProvider>
|
|
551
562
|
);
|
|
552
|
-
};
|
|
563
|
+
};
|
|
564
|
+
|
|
@@ -39,7 +39,7 @@ export const GuideView: React.FC = () => {
|
|
|
39
39
|
refactor: { label: "REFACTOR", desc: "Code restructurings without changing external behavior." },
|
|
40
40
|
adaptation: { label: "ADAPTATION", desc: "Changes to adjust to new environmental/integration constraints." },
|
|
41
41
|
frontmatter: "Official Markdown Format",
|
|
42
|
-
frontmatterDesc: "Each artifact requires a YAML frontmatter. Mandatory
|
|
42
|
+
frontmatterDesc: "Each artifact requires a YAML frontmatter. Mandatory: id, type, status, title. Optional: subType (for taxonomic classification).",
|
|
43
43
|
statusFlow: "Lifecycle Status Flow",
|
|
44
44
|
statusFlowDesc: "Evolution of artifacts. The system tolerates missing relations in 'draft', but expects full traceability in 'closed'.",
|
|
45
45
|
versions: "Project & System Versions",
|
|
@@ -93,7 +93,7 @@ export const GuideView: React.FC = () => {
|
|
|
93
93
|
refactor: { label: "REFACTOR", desc: "Reestructuraciones sin alterar el comportamiento externo." },
|
|
94
94
|
adaptation: { label: "ADAPTATION", desc: "Ajustes para nuevas integraciones o restricciones del entorno." },
|
|
95
95
|
frontmatter: "Formato Oficial Markdown",
|
|
96
|
-
frontmatterDesc: "Cada artefacto requiere un YAML frontmatter. Campos obligatorios: id, type, status, title.",
|
|
96
|
+
frontmatterDesc: "Cada artefacto requiere un YAML frontmatter. Campos obligatorios: id, type, status, title. Opcional: subType (clasificación taxonómica).",
|
|
97
97
|
statusFlow: "Estados y Ciclo de Vida",
|
|
98
98
|
statusFlowDesc: "En estado 'draft' se permiten huérfanos, pero en 'closed' se exige trazabilidad completa.",
|
|
99
99
|
versions: "Versiones del Sistema y Proyecto",
|
|
@@ -173,7 +173,7 @@ export const GuideView: React.FC = () => {
|
|
|
173
173
|
<h3 className="text-sm font-bold text-white/80 mb-2 uppercase tracking-widest">{c.implementationLayer}</h3>
|
|
174
174
|
<p className="text-[11px] text-white/40 leading-relaxed mb-4">{c.implementationLayerDesc}</p>
|
|
175
175
|
<div className="flex flex-wrap gap-2">
|
|
176
|
-
{['CODE_ENTITY', 'TEST_CASE', 'DATABASE_ENTITY', 'CHANGE', 'BUG', 'RISK'
|
|
176
|
+
{['CODE_ENTITY', 'TEST_CASE', 'DATABASE_ENTITY', 'CHANGE', 'BUG', 'RISK'].map(t => (
|
|
177
177
|
<span key={t} className="text-[9px] bg-white/5 px-1.5 py-0.5 rounded text-white/60">{t}</span>
|
|
178
178
|
))}
|
|
179
179
|
</div>
|
|
@@ -306,6 +306,7 @@ export const GuideView: React.FC = () => {
|
|
|
306
306
|
{`---
|
|
307
307
|
id: REQ-001
|
|
308
308
|
type: REQUIREMENT
|
|
309
|
+
subType: SYSTEM
|
|
309
310
|
status: draft
|
|
310
311
|
layer: BUSINESS
|
|
311
312
|
title: Generar graph-data.json
|
|
@@ -452,6 +453,7 @@ We will use JWT tokens for authentication...`}
|
|
|
452
453
|
{`---
|
|
453
454
|
id: CODE-auth-service
|
|
454
455
|
type: CODE_ENTITY
|
|
456
|
+
subType: Microservice
|
|
455
457
|
status: in_progress
|
|
456
458
|
layer: IMPLEMENTATION
|
|
457
459
|
title: AuthService.ts
|
|
@@ -2,6 +2,7 @@ import React, { useState, useMemo, useEffect } from 'react';
|
|
|
2
2
|
import { useStore } from '../store';
|
|
3
3
|
import { Activity, ArrowRight, GitCommit, Search, ShieldAlert, Zap } from 'lucide-react';
|
|
4
4
|
import { Artifact } from '../types';
|
|
5
|
+
import { getArtifactLayer, getArtifactOwner, getArtifactTeam } from '../utils/artifactUtils';
|
|
5
6
|
|
|
6
7
|
export const ImpactView: React.FC = () => {
|
|
7
8
|
const { changes, versions, fullGraph: graph, systemVersions, globalFilters, setGlobalFilter } = useStore();
|
|
@@ -20,9 +21,9 @@ export const ImpactView: React.FC = () => {
|
|
|
20
21
|
|
|
21
22
|
const filterOptions = useMemo(() => {
|
|
22
23
|
if (!graph || !graph.artifacts) return { layers: [], owners: [], teams: [] };
|
|
23
|
-
const layers = Array.from(new Set(graph.artifacts.map(a => a
|
|
24
|
-
const owners = Array.from(new Set(graph.artifacts.map(a => a
|
|
25
|
-
const teams = Array.from(new Set(graph.artifacts.map(a => a
|
|
24
|
+
const layers = Array.from(new Set(graph.artifacts.map(a => getArtifactLayer(a)).filter(Boolean))) as string[];
|
|
25
|
+
const owners = Array.from(new Set(graph.artifacts.map(a => getArtifactOwner(a, graph)).filter(Boolean))) as string[];
|
|
26
|
+
const teams = Array.from(new Set(graph.artifacts.map(a => getArtifactTeam(a, graph)).filter(Boolean))) as string[];
|
|
26
27
|
return { layers, owners, teams };
|
|
27
28
|
}, [graph]);
|
|
28
29
|
|
|
@@ -42,9 +43,12 @@ export const ImpactView: React.FC = () => {
|
|
|
42
43
|
const affectedLayerTeamOwner = (c.affects || []).some(id => {
|
|
43
44
|
const artifact = graph?.artifacts.find(a => a.id === id);
|
|
44
45
|
if (!artifact) return false;
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
const
|
|
46
|
+
const computedLayer = getArtifactLayer(artifact);
|
|
47
|
+
const computedOwner = getArtifactOwner(artifact, graph);
|
|
48
|
+
const computedTeam = getArtifactTeam(artifact, graph);
|
|
49
|
+
const matchesLayer = filterLayer === 'ALL' || computedLayer === filterLayer;
|
|
50
|
+
const matchesOwner = filterOwner === 'ALL' || computedOwner === filterOwner;
|
|
51
|
+
const matchesTeam = filterTeam === 'ALL' || computedTeam === filterTeam;
|
|
48
52
|
return matchesLayer && matchesOwner && matchesTeam;
|
|
49
53
|
});
|
|
50
54
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useMemo, useState } from 'react';
|
|
2
2
|
import { useStore } from '../store';
|
|
3
3
|
import { ArtifactType } from '../types';
|
|
4
|
+
import { getArtifactLayer, getArtifactOwner, getArtifactTeam } from '../utils/artifactUtils';
|
|
4
5
|
import { AlertCircle, Search, Trash2, ChevronRight, FileText, ExternalLink, ShieldAlert, AlertTriangle, Info, Download } from 'lucide-react';
|
|
5
6
|
import { generateGapsReport, downloadTextFile } from '../lib/reportUtils';
|
|
6
7
|
|
|
@@ -72,10 +73,13 @@ export const OrphansView: React.FC = () => {
|
|
|
72
73
|
const filteredGaps = useMemo(() => {
|
|
73
74
|
return gaps.filter(gap => {
|
|
74
75
|
const art = gap.artifact;
|
|
76
|
+
const computedLayer = getArtifactLayer(art);
|
|
77
|
+
const computedOwner = getArtifactOwner(art, graph);
|
|
78
|
+
const computedTeam = getArtifactTeam(art, graph);
|
|
75
79
|
|
|
76
|
-
if (filterLayer !== 'ALL' &&
|
|
77
|
-
if (filterOwner !== 'ALL' &&
|
|
78
|
-
if (filterTeam !== 'ALL' &&
|
|
80
|
+
if (filterLayer !== 'ALL' && computedLayer !== filterLayer) return false;
|
|
81
|
+
if (filterOwner !== 'ALL' && computedOwner !== filterOwner) return false;
|
|
82
|
+
if (filterTeam !== 'ALL' && computedTeam !== filterTeam) return false;
|
|
79
83
|
|
|
80
84
|
if (selectedArtifactId && art.id !== selectedArtifactId) return false;
|
|
81
85
|
|
|
@@ -95,7 +99,7 @@ export const OrphansView: React.FC = () => {
|
|
|
95
99
|
// Match the grouped keys in DocumentationView
|
|
96
100
|
return [
|
|
97
101
|
'REQUIREMENT', 'USE_CASE', 'DESIGN', 'COMPONENT',
|
|
98
|
-
'CODE_ENTITY', '
|
|
102
|
+
'CODE_ENTITY', 'TEST_CASE', 'DOCUMENTATION', 'INCIDENT',
|
|
99
103
|
'INFRASTRUCTURE', 'DEPLOYMENT', 'MONITORING', 'MAINTENANCE'
|
|
100
104
|
];
|
|
101
105
|
}, []);
|
|
@@ -357,4 +361,3 @@ export const OrphansView: React.FC = () => {
|
|
|
357
361
|
</div>
|
|
358
362
|
);
|
|
359
363
|
};
|
|
360
|
-
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Artifact, ArtifactType, ArtifactLayer, GraphSnapshot } from '../types';
|
|
2
|
+
|
|
3
|
+
export function getImplicitLayer(type: ArtifactType): ArtifactLayer | undefined {
|
|
4
|
+
switch (type) {
|
|
5
|
+
case 'PROJECT':
|
|
6
|
+
case 'EPIC':
|
|
7
|
+
case 'FEATURE':
|
|
8
|
+
case 'REQUIREMENT':
|
|
9
|
+
case 'USE_CASE':
|
|
10
|
+
case 'BUSINESS_RULE':
|
|
11
|
+
return 'BUSINESS';
|
|
12
|
+
case 'DESIGN':
|
|
13
|
+
case 'DECISION':
|
|
14
|
+
case 'API':
|
|
15
|
+
case 'COMPONENT':
|
|
16
|
+
return 'ARCHITECTURE';
|
|
17
|
+
case 'CODE_ENTITY':
|
|
18
|
+
case 'TEST_CASE':
|
|
19
|
+
case 'DATABASE_ENTITY':
|
|
20
|
+
case 'CHANGE':
|
|
21
|
+
case 'BUG':
|
|
22
|
+
case 'LIBRARY':
|
|
23
|
+
case 'ENVIRONMENT':
|
|
24
|
+
return 'IMPLEMENTATION';
|
|
25
|
+
case 'INFRASTRUCTURE':
|
|
26
|
+
case 'DEPLOYMENT':
|
|
27
|
+
case 'MONITORING':
|
|
28
|
+
case 'INCIDENT':
|
|
29
|
+
case 'MAINTENANCE':
|
|
30
|
+
case 'PIPELINE':
|
|
31
|
+
case 'CHECK':
|
|
32
|
+
return 'OPERATIONS';
|
|
33
|
+
case 'GLOSSARY_TERM':
|
|
34
|
+
case 'DOCUMENTATION':
|
|
35
|
+
case 'PROCESS':
|
|
36
|
+
case 'VERSION':
|
|
37
|
+
case 'SYSTEM_VERSION':
|
|
38
|
+
return 'DOCUMENTATION';
|
|
39
|
+
default:
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getArtifactLayer(artifact: Artifact): ArtifactLayer | undefined {
|
|
45
|
+
if (artifact.layer) {
|
|
46
|
+
return artifact.layer;
|
|
47
|
+
}
|
|
48
|
+
return getImplicitLayer(artifact.type);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Traverse up REFINES or IMPLEMENTS relations to find inherited ownership
|
|
52
|
+
function findInheritedOwnership(
|
|
53
|
+
artifactId: string,
|
|
54
|
+
graph: GraphSnapshot,
|
|
55
|
+
visited: Set<string>
|
|
56
|
+
): { owner?: string; team?: string } {
|
|
57
|
+
if (visited.has(artifactId)) return {};
|
|
58
|
+
visited.add(artifactId);
|
|
59
|
+
|
|
60
|
+
const artifact = graph.artifacts.find(a => a.id === artifactId);
|
|
61
|
+
if (!artifact) return {};
|
|
62
|
+
|
|
63
|
+
if (artifact.ownership?.owner || artifact.ownership?.team) {
|
|
64
|
+
return { owner: artifact.ownership.owner, team: artifact.ownership.team };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Look for incoming REFINES or IMPLEMENTS (e.g. this artifact REFINES parent or this artifact IMPLEMENTS parent)
|
|
68
|
+
// Actually, wait: 'from' REFINES 'to' meant "from is smaller, to is bigger". So 'to' is the parent!
|
|
69
|
+
const parentRelations = graph.relations.filter(
|
|
70
|
+
r => r.from === artifactId && (r.type === 'REFINES' || r.type === 'IMPLEMENTS')
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
for (const rel of parentRelations) {
|
|
74
|
+
const parentOwnership = findInheritedOwnership(rel.to, graph, visited);
|
|
75
|
+
if (parentOwnership.owner || parentOwnership.team) {
|
|
76
|
+
return parentOwnership;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function getArtifactOwner(artifact: Artifact, graph: GraphSnapshot | null): string | undefined {
|
|
84
|
+
if (artifact.ownership?.owner) return artifact.ownership.owner;
|
|
85
|
+
if (!graph) return undefined;
|
|
86
|
+
return findInheritedOwnership(artifact.id, graph, new Set()).owner;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function getArtifactTeam(artifact: Artifact, graph: GraphSnapshot | null): string | undefined {
|
|
90
|
+
if (artifact.ownership?.team) return artifact.ownership.team;
|
|
91
|
+
if (!graph) return undefined;
|
|
92
|
+
return findInheritedOwnership(artifact.id, graph, new Set()).team;
|
|
93
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{ai as o,aj as n}from"./index-wflyqKCW.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-727SXJPM-Ceirad8r.js";import{_ as i}from"./index-wflyqKCW.js";import"./chunk-FMBD7UC4-DGLw7-i0.js";import"./chunk-ND2GUHAM-bdCVW2vW.js";import"./chunk-55IACEB6-DhMUe2Z8.js";import"./chunk-2J33WTMH-CI2-2ImK.js";var u={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{u as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-727SXJPM-Ceirad8r.js";import{_ as i}from"./index-wflyqKCW.js";import"./chunk-FMBD7UC4-DGLw7-i0.js";import"./chunk-ND2GUHAM-bdCVW2vW.js";import"./chunk-55IACEB6-DhMUe2Z8.js";import"./chunk-2J33WTMH-CI2-2ImK.js";var u={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{u as diagram};
|