Publish curated MobilityOps source snapshot
This commit is contained in:
commit
7adc072f5e
317 files changed
+48851
No files matched your search
@@ -0,0 +1,122 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Icon, type IconName } from "./Icons";
|
||||
|
||||
export function PageHeader({
|
||||
eyebrow,
|
||||
title,
|
||||
description,
|
||||
actions,
|
||||
}: {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
actions?: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<header className="page-header">
|
||||
<div>
|
||||
<p className="page-eyebrow">{eyebrow}</p>
|
||||
<h1>{title}</h1>
|
||||
{description && <p className="page-description">{description}</p>}
|
||||
</div>
|
||||
{actions && <div className="page-actions">{actions}</div>}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export function SectionHeading({
|
||||
title,
|
||||
description,
|
||||
action,
|
||||
headingId,
|
||||
}: {
|
||||
title: string;
|
||||
description?: string;
|
||||
action?: ReactNode;
|
||||
headingId?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="section-heading">
|
||||
<div>
|
||||
<h2 id={headingId}>{title}</h2>
|
||||
{description && <p>{description}</p>}
|
||||
</div>
|
||||
{action}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function LoadingState({ label }: { label?: string }) {
|
||||
const { t } = useTranslation("common");
|
||||
return (
|
||||
<div className="state-panel" role="status">
|
||||
<span className="spinner" aria-hidden="true" />
|
||||
<p>{label ?? t("states.loadingDefault")}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ErrorState({
|
||||
message,
|
||||
onRetry,
|
||||
retryLabel,
|
||||
}: {
|
||||
message: string;
|
||||
onRetry?: () => void;
|
||||
retryLabel?: string;
|
||||
}) {
|
||||
const { t } = useTranslation("common");
|
||||
return (
|
||||
<div className="state-panel state-error" role="alert">
|
||||
<Icon name="alert" />
|
||||
<div>
|
||||
<strong>{t("states.errorTitle")}</strong>
|
||||
<p>{message}</p>
|
||||
{onRetry ? <button type="button" className="button button-secondary state-retry" onClick={onRetry}>{retryLabel ?? t("actions.retry")}</button> : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Shared rendering for describeApiError()'s output: a localized title + explanation +
|
||||
// optional next step, with the raw backend/network text demoted to a "Technical
|
||||
// details" disclosure -- never shown as the primary message. See
|
||||
// frontend/src/api/errorMessages.ts and docs/fleet-ops-final-localization/audit.md.
|
||||
export function ApiErrorNotice({ error }: { error: import("../api/errorMessages").ApiErrorInfo | null }) {
|
||||
const { t } = useTranslation("common");
|
||||
if (!error) return null;
|
||||
return (
|
||||
<div className="error api-error-notice" role="alert">
|
||||
<strong>{error.title}</strong>
|
||||
<p>{error.explanation}</p>
|
||||
{error.nextStep && <p className="api-error-next-step">{error.nextStep}</p>}
|
||||
<details className="evidence-disclosure">
|
||||
<summary>{t("actions.technicalDetails")}</summary>
|
||||
<pre className="evidence-block">{error.technical}</pre>
|
||||
</details>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function EmptyState({
|
||||
icon = "check",
|
||||
title,
|
||||
detail,
|
||||
}: {
|
||||
icon?: IconName;
|
||||
title: string;
|
||||
detail: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="state-panel state-empty">
|
||||
<Icon name={icon} />
|
||||
<div><strong>{title}</strong><p>{detail}</p></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function IntegrationMark({ kind }: { kind: "n8n" | "rag" | "mcp" }) {
|
||||
const labels = { n8n: "n8n", rag: "RA", mcp: "MC" };
|
||||
return <span className={`integration-mark integration-${kind}`} aria-hidden="true">{labels[kind]}</span>;
|
||||
}
|
||||
Reference in new issue
Block a user