Initial portfolio commit (Next.js + Docker + Gitea integration)
This commit is contained in:
180
components/architecture-breakdown.tsx
Normal file
180
components/architecture-breakdown.tsx
Normal file
@@ -0,0 +1,180 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { Reveal } from "@/components/reveal";
|
||||
|
||||
export type ArchitectureLayer = {
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
bullets: string[];
|
||||
};
|
||||
|
||||
export type ArchitectureInterface = {
|
||||
label: string;
|
||||
detail: string;
|
||||
};
|
||||
|
||||
export type ArchitectureBreakdownData = {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
layers: ArchitectureLayer[];
|
||||
flow: Array<{ from: string; to: string; note?: string }>;
|
||||
interfaces: ArchitectureInterface[];
|
||||
};
|
||||
|
||||
export function ArchitectureBreakdown({
|
||||
title = "Architecture",
|
||||
subtitle = "How the system is structured and how state moves through it.",
|
||||
data,
|
||||
}: {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
data: ArchitectureBreakdownData;
|
||||
}) {
|
||||
return (
|
||||
<section className="relative overflow-hidden">
|
||||
<div className="glass-strong noise-overlay rounded-3xl p-6 sm:p-8 shadow-sm relative overflow-hidden">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h2 className="text-xl sm:text-2xl font-semibold tracking-tight">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="text-sm sm:text-[15px] text-black/60 max-w-3xl">
|
||||
{subtitle}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Layers */}
|
||||
<div className="mt-6 sm:mt-8 grid gap-4 lg:grid-cols-3">
|
||||
{data.layers.map((layer, idx) => (
|
||||
<Reveal key={`${layer.title}-${idx}`}>
|
||||
<div className="glass rounded-3xl p-5 sm:p-6 h-full">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="text-[11px] uppercase tracking-[0.18em] text-black/45">
|
||||
Layer {String(idx + 1).padStart(2, "0")}
|
||||
</div>
|
||||
<h3 className="mt-2 text-base sm:text-lg font-semibold tracking-tight">
|
||||
{layer.title}
|
||||
</h3>
|
||||
{layer.subtitle ? (
|
||||
<p className="mt-1 text-sm text-black/60">
|
||||
{layer.subtitle}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="shrink-0">
|
||||
<span className="glass px-2.5 py-1 rounded-full text-[11px] text-black/55 tabular-nums">
|
||||
{String(idx + 1).padStart(2, "0")}/03
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul className="mt-4 grid gap-2 text-sm text-black/70">
|
||||
{layer.bullets.map((b, bi) => (
|
||||
<li key={`${layer.title}-b-${bi}`} className="flex gap-2">
|
||||
<span className="mt-[7px] h-1.5 w-1.5 rounded-full bg-black/25" />
|
||||
<span className="min-w-0">{b}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</Reveal>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Flow */}
|
||||
<div className="mt-6 sm:mt-8">
|
||||
<Reveal>
|
||||
<div className="glass rounded-3xl p-5 sm:p-6">
|
||||
<div className="flex flex-col sm:flex-row sm:items-end sm:justify-between gap-2">
|
||||
<div>
|
||||
<div className="text-[11px] uppercase tracking-[0.18em] text-black/45">
|
||||
Data flow
|
||||
</div>
|
||||
<h3 className="mt-2 text-base sm:text-lg font-semibold tracking-tight">
|
||||
State moves as a pipeline
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-black/60 max-w-3xl">
|
||||
Configuration is authored on desktop, executed deterministically, then mirrored back as
|
||||
LED feedback so the device stays readable at a glance.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-5 grid gap-3">
|
||||
{data.flow.map((step, i) => (
|
||||
<div
|
||||
key={`${step.from}-${step.to}-${i}`}
|
||||
className="glass rounded-2xl px-4 py-3"
|
||||
>
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
|
||||
<div className="min-w-0">
|
||||
<div className="text-sm text-black/75">
|
||||
<span className="font-semibold">{step.from}</span>
|
||||
<span className="mx-2 text-black/30">→</span>
|
||||
<span className="font-semibold">{step.to}</span>
|
||||
</div>
|
||||
{step.note ? (
|
||||
<div className="mt-1 text-[13px] text-black/60">
|
||||
{step.note}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="shrink-0 text-[12px] text-black/45 tabular-nums">
|
||||
Step {String(i + 1).padStart(2, "0")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
</div>
|
||||
|
||||
{/* Interfaces */}
|
||||
<div className="mt-6 sm:mt-8">
|
||||
<Reveal>
|
||||
<div className="glass rounded-3xl p-5 sm:p-6">
|
||||
<div className="text-[11px] uppercase tracking-[0.18em] text-black/45">
|
||||
Interfaces
|
||||
</div>
|
||||
<h3 className="mt-2 text-base sm:text-lg font-semibold tracking-tight">
|
||||
Stable contracts between modules
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-black/60 max-w-3xl">
|
||||
The system stays maintainable because boundaries are explicit and small.
|
||||
</p>
|
||||
|
||||
<div className="mt-5 grid gap-3 sm:grid-cols-2">
|
||||
{data.interfaces.map((it, idx) => (
|
||||
<div
|
||||
key={`${it.label}-${idx}`}
|
||||
className="glass rounded-2xl p-4"
|
||||
>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="text-sm font-semibold text-black/75">
|
||||
{it.label}
|
||||
</div>
|
||||
<div className="mt-1 text-sm text-black/60">
|
||||
{it.detail}
|
||||
</div>
|
||||
</div>
|
||||
<span className="glass px-2.5 py-1 rounded-full text-[11px] text-black/50 tabular-nums">
|
||||
{String(idx + 1).padStart(2, "0")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
</div>
|
||||
|
||||
{/* Specular highlight */}
|
||||
<div className="pointer-events-none specular absolute -top-24 -right-24 h-64 w-64 rounded-full opacity-50 blur-2xl" />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user