105 lines
4.5 KiB
TypeScript
105 lines
4.5 KiB
TypeScript
import { useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { Icon, type IconName } from "./Icons";
|
|
import "../styles-architecture-flow.css";
|
|
|
|
type ArchitectureStepId = "Frontend" | "Api" | "Database" | "Outbox" | "External";
|
|
type ArchitectureZone = "intent" | "transaction" | "edge";
|
|
|
|
const ARCHITECTURE_STEPS: Array<{
|
|
id: ArchitectureStepId;
|
|
icon: IconName;
|
|
zone: ArchitectureZone;
|
|
}> = [
|
|
{ id: "Frontend", icon: "user", zone: "intent" },
|
|
{ id: "Api", icon: "shield", zone: "transaction" },
|
|
{ id: "Database", icon: "audit", zone: "transaction" },
|
|
{ id: "Outbox", icon: "activity", zone: "transaction" },
|
|
{ id: "External", icon: "integrations", zone: "edge" },
|
|
];
|
|
|
|
const ZONE_TRANSLATION_KEYS: Record<ArchitectureZone, string> = {
|
|
intent: "architectureZoneIntent",
|
|
transaction: "architectureZoneTransaction",
|
|
edge: "architectureZoneEdge",
|
|
};
|
|
|
|
export function ArchitectureFlow() {
|
|
const { t } = useTranslation("demo");
|
|
const [activeStepId, setActiveStepId] = useState<ArchitectureStepId>("Database");
|
|
const activeIndex = ARCHITECTURE_STEPS.findIndex((step) => step.id === activeStepId);
|
|
const activeStep = ARCHITECTURE_STEPS[activeIndex];
|
|
|
|
return (
|
|
<div className="architecture-explorer">
|
|
<div className="architecture-zones" aria-label={t("about.architectureZonesLabel")}>
|
|
<span className="zone-intent"><Icon name="user" /> {t("about.architectureZoneIntent")}</span>
|
|
<span className="zone-transaction"><Icon name="shield" /> {t("about.architectureZoneTransaction")}</span>
|
|
<span className="zone-edge"><Icon name="integrations" /> {t("about.architectureZoneEdge")}</span>
|
|
</div>
|
|
|
|
<ol className="architecture-flow" aria-label={t("about.architectureFlowLabel")}>
|
|
{ARCHITECTURE_STEPS.map((step, index) => {
|
|
const isActive = step.id === activeStepId;
|
|
const isCommitBoundary = step.id === "Outbox";
|
|
return (
|
|
<li
|
|
className={`architecture-step architecture-step-${step.zone}${isActive ? " is-active" : ""}`}
|
|
key={step.id}
|
|
>
|
|
<button
|
|
type="button"
|
|
aria-pressed={isActive}
|
|
aria-controls="architecture-active-detail"
|
|
onClick={() => setActiveStepId(step.id)}
|
|
>
|
|
<span className="architecture-step-top">
|
|
<span className="architecture-step-number">0{index + 1}</span>
|
|
<span className="architecture-step-icon"><Icon name={step.icon} /></span>
|
|
</span>
|
|
<strong>{t(`about.architecture${step.id}`)}</strong>
|
|
<small>{t(`about.architectureDetails.${step.id}.summary`)}</small>
|
|
<span className="architecture-step-meta">
|
|
<span className="architecture-zone-tag">{t(`about.${ZONE_TRANSLATION_KEYS[step.zone]}`)}</span>
|
|
{isCommitBoundary ? <span className="architecture-commit-note">→ {t("about.architectureCommitBoundary")}</span> : null}
|
|
</span>
|
|
</button>
|
|
{index < ARCHITECTURE_STEPS.length - 1 ? (
|
|
<span className={`architecture-connector${isCommitBoundary ? " is-commit-boundary" : ""}`} aria-hidden="true">
|
|
<span className="architecture-signal" />
|
|
</span>
|
|
) : null}
|
|
</li>
|
|
);
|
|
})}
|
|
</ol>
|
|
|
|
<p className="architecture-interaction-hint">{t("about.architectureInteractionHint")}</p>
|
|
<section
|
|
className={`architecture-active-detail architecture-active-detail-${activeStep.zone}`}
|
|
id="architecture-active-detail"
|
|
aria-live="polite"
|
|
>
|
|
<div className="architecture-active-copy">
|
|
<span>{t("about.architectureSelectedStep", { current: activeIndex + 1, total: ARCHITECTURE_STEPS.length })}</span>
|
|
<h3>{t(`about.architecture${activeStep.id}`)}</h3>
|
|
<p>{t(`about.architectureDetails.${activeStep.id}.body`)}</p>
|
|
</div>
|
|
<dl className="architecture-active-evidence">
|
|
<div>
|
|
<dt>{t("about.architectureBoundaryLabel")}</dt>
|
|
<dd>{t(`about.${ZONE_TRANSLATION_KEYS[activeStep.zone]}`)}</dd>
|
|
</div>
|
|
<div>
|
|
<dt>{t("about.architectureEvidenceLabel")}</dt>
|
|
<dd>
|
|
<span>{t(`about.architectureDetails.${activeStep.id}.proofOne`)}</span>
|
|
<span>{t(`about.architectureDetails.${activeStep.id}.proofTwo`)}</span>
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
</section>
|
|
</div>
|
|
);
|
|
}
|