Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"test": "vitest run"
},
"dependencies": {
"@dagrejs/dagre": "^2.0.4",
Expand All @@ -22,6 +23,7 @@
"autoprefixer": "^10.4.0",
"postcss": "^8.4.0",
"tailwindcss": "^3.4.0",
"typescript": "^5.4.0"
"typescript": "^5.4.0",
"vitest": "^4.1.0"
}
}
16 changes: 13 additions & 3 deletions apps/web/src/components/ArchMapShell.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"use client";

import { useState } from "react";
import { useState, useMemo } from "react";
import GraphView from "./GraphView";
import GraphTabs, { type ViewTab } from "./GraphTabs";
import type { GraphData } from "@/types/graph";
import { buildDataFlowView } from "@/lib/buildDataFlowView";
import { buildFunctionFlowView } from "@/lib/buildFunctionFlowView";

interface Props {
graph: GraphData;
Expand All @@ -13,14 +15,22 @@ export default function ArchMapShell({ graph }: Props) {
const [activeTab, setActiveTab] = useState<ViewTab>("serviceFlow");
const [selectedServiceId, setSelectedServiceId] = useState<string | null>(null);

const view = graph.views[activeTab];
const dataFlowView = useMemo(() => buildDataFlowView(graph.services ?? []), [graph.services]);
const functionFlowView = useMemo(() => buildFunctionFlowView(graph.services ?? []), [graph.services]);

const view =
activeTab === "dataFlow" ? dataFlowView :
activeTab === "functionFlow" ? functionFlowView :
graph.views[activeTab];
const serviceCount = graph.services?.length ?? 0;
const services = (graph.services ?? []).map((s) => ({ id: s.id, name: s.name }));
const allServices = graph.services ?? [];

function handleDrillIn(serviceId: string) {
setSelectedServiceId(serviceId);
setActiveTab("functionFlow");
if (activeTab !== "dataFlow" && activeTab !== "containerDiagram") {
setActiveTab("functionFlow");
}
}

return (
Expand Down
58 changes: 58 additions & 0 deletions apps/web/src/components/DtoJsonModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"use client";

import type { DataType, AnalyzedService } from "@/types/graph";
import { buildExample } from "@/lib/buildDtoExample";

interface Props {
dto: DataType;
allServices: AnalyzedService[];
onClose: () => void;
}

export default function DtoJsonModal({ dto, allServices, onClose }: Props) {
const dtoMap = new Map<string, DataType>();
for (const svc of allServices) {
for (const dt of svc.dataTypes) {
if (!dtoMap.has(dt.name)) dtoMap.set(dt.name, dt);
}
}

const isEnum = dto.fields.every((f) => f.type === "enum constant");
const json = isEnum
? dto.fields.map((f) => f.name)
: buildExample(dto, dtoMap);

const jsonStr = JSON.stringify(json, null, 2);

return (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm"
onClick={onClose}
>
<div
className="bg-gray-900 border border-gray-700 rounded-xl shadow-2xl w-full max-w-lg mx-4 overflow-hidden"
onClick={(e) => e.stopPropagation()}
>
<div className="flex items-center justify-between px-4 py-3 border-b border-gray-700">
<div className="flex items-center gap-2">
<span className={`text-xs px-1.5 py-0.5 rounded ${isEnum ? "bg-amber-900 text-amber-300" : "bg-purple-900 text-purple-300"}`}>
{isEnum ? "ENUM" : "DTO"}
</span>
<span className="text-white font-semibold">{dto.name}</span>
</div>
<button
onClick={onClose}
className="text-gray-500 hover:text-gray-300 text-lg leading-none"
>
</button>
</div>
<div className="p-4 overflow-auto max-h-[60vh]">
<pre className="text-xs font-mono text-gray-300 whitespace-pre leading-5">
{jsonStr}
</pre>
</div>
</div>
</div>
);
}
Loading
Loading