UI/UX-Prototyp der Analytics-Dashboards in HiveGrid, fachlich auf einen SHK-/Handwerksbetrieb zugeschnitten. Ziel ist die Übergabe an die Entwicklung, nicht der Produktivbetrieb. Inhalt: sechs Seiten (Übersicht, Finanzen, Aufträge, Kunden, Team, Komponenten), 26 KPIs, neun Chart-Bausteine, Filtersystem mit seitenabhängiger Relevanz und ein deterministischer Demo-Datensatz (420 Kunden, 1.900 Aufträge, Stichtag 30.07.2026). Drei Quellen sind zusammengeführt: die fachliche Planung mit dem Branchenpartner (Januar 2026), die Subframe-Komponentenbibliothek aus dem HiveGrid-Pitch und IBM Carbon Data Visualization für die Diagrammregeln. Herkunft und bewusste Abweichungen stehen in docs/HERKUNFT.md. Dokumentation: README, HANDOVER (was echte Logik braucht), docs/DESIGN-SYSTEM, KPI-KATALOG, DATENMODELL, NAVIGATION, HERKUNFT, ZIELSYSTEM (Einbauort in HiveGrid Cloud) und LEITPLANKEN (welche Cloud-Screens als Vorbild taugen). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPeZNwSeU7qG3RRCwaxEZX
152 lines
5.0 KiB
TypeScript
152 lines
5.0 KiB
TypeScript
"use client";
|
|
/*
|
|
* Documentation:
|
|
* Progress — https://app.subframe.com/f023064a5ca0/library?component=Progress_60964db0-a1bf-428b-b9d5-f34cdf58ea77
|
|
* Toast — https://app.subframe.com/f023064a5ca0/library?component=Toast_2c7966c2-a95d-468a-83fe-bf196b95be7a
|
|
*/
|
|
|
|
import React from "react";
|
|
import { FeatherAlertCircle } from "@subframe/core";
|
|
import { FeatherAlertTriangle } from "@subframe/core";
|
|
import { FeatherCheckCircle } from "@subframe/core";
|
|
import { FeatherInbox } from "@subframe/core";
|
|
import { FeatherInfo } from "@subframe/core";
|
|
import { FeatherX } from "@subframe/core";
|
|
import * as SubframeUtils from "../../utils";
|
|
import { Progress } from "../Progress";
|
|
|
|
export interface ToastRootProps
|
|
extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
|
variant?: "information" | "success" | "warning" | "error" | "neutral";
|
|
dismissible?: boolean;
|
|
hideProgress?: boolean;
|
|
hideUndo?: boolean;
|
|
title?: React.ReactNode;
|
|
description?: React.ReactNode;
|
|
progress?: number;
|
|
undoLabel?: React.ReactNode;
|
|
className?: string;
|
|
}
|
|
|
|
const ToastRoot = React.forwardRef<HTMLDivElement, ToastRootProps>(
|
|
function ToastRoot(
|
|
{
|
|
variant = "information",
|
|
dismissible = false,
|
|
hideProgress = false,
|
|
hideUndo = false,
|
|
title,
|
|
description,
|
|
progress = 30,
|
|
undoLabel,
|
|
className,
|
|
...otherProps
|
|
}: ToastRootProps,
|
|
ref
|
|
) {
|
|
return (
|
|
<div
|
|
className={SubframeUtils.twClassNames(
|
|
"group/2c7966c2 flex min-w-[384px] max-w-[448px] flex-col items-start gap-3 rounded-md border border-solid border-border-default bg-surface-neutral-secondary px-5 py-5 shadow-lg",
|
|
{ "gap-4": hideUndo },
|
|
className
|
|
)}
|
|
ref={ref}
|
|
{...otherProps}
|
|
>
|
|
<div className="flex w-full items-start gap-3">
|
|
<FeatherInfo
|
|
className={SubframeUtils.twClassNames(
|
|
"text-heading-0 font-heading-0 text-icon-feedback-information",
|
|
{
|
|
hidden:
|
|
variant === "neutral" ||
|
|
variant === "error" ||
|
|
variant === "warning" ||
|
|
variant === "success",
|
|
}
|
|
)}
|
|
/>
|
|
<FeatherCheckCircle
|
|
className={SubframeUtils.twClassNames(
|
|
"hidden text-heading-0 font-heading-0 text-icon-feedback-positive",
|
|
{ "inline-flex": variant === "success" }
|
|
)}
|
|
/>
|
|
<FeatherAlertTriangle
|
|
className={SubframeUtils.twClassNames(
|
|
"hidden text-heading-0 font-heading-0 text-icon-feedback-warning",
|
|
{ "inline-flex": variant === "warning" }
|
|
)}
|
|
/>
|
|
<FeatherAlertCircle
|
|
className={SubframeUtils.twClassNames(
|
|
"hidden text-heading-0 font-heading-0 text-icon-feedback-negative",
|
|
{ "inline-flex": variant === "error" }
|
|
)}
|
|
/>
|
|
<FeatherInbox
|
|
className={SubframeUtils.twClassNames(
|
|
"hidden text-heading-0 font-heading-0 text-icon-neutral-secondary",
|
|
{ "inline-flex": variant === "neutral" }
|
|
)}
|
|
/>
|
|
<div className="flex grow shrink-0 basis-0 flex-col items-start gap-4">
|
|
{title ? (
|
|
<span className="text-body-default-medium-0 font-body-default-medium-0 text-text-neutral-primary">
|
|
{title}
|
|
</span>
|
|
) : null}
|
|
{description ? (
|
|
<span className="text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-secondary">
|
|
{description}
|
|
</span>
|
|
) : null}
|
|
</div>
|
|
<div
|
|
className={SubframeUtils.twClassNames(
|
|
"hidden items-center justify-center",
|
|
{ flex: dismissible }
|
|
)}
|
|
>
|
|
<FeatherX className="text-body-default-regular-0 font-body-default-regular-0 text-icon-neutral-primary" />
|
|
</div>
|
|
</div>
|
|
<div
|
|
className={SubframeUtils.twClassNames(
|
|
"flex w-full items-center gap-6",
|
|
{ "justify-end": hideProgress }
|
|
)}
|
|
>
|
|
<div
|
|
className={SubframeUtils.twClassNames(
|
|
"flex grow shrink-0 basis-0 items-center",
|
|
{ hidden: hideProgress }
|
|
)}
|
|
>
|
|
<Progress
|
|
hideLabel={true}
|
|
value={progress}
|
|
label="Uploading file"
|
|
statusText="30%"
|
|
/>
|
|
</div>
|
|
<div
|
|
className={SubframeUtils.twClassNames("flex items-start", {
|
|
hidden: hideUndo,
|
|
})}
|
|
>
|
|
{undoLabel ? (
|
|
<span className="text-body-default-medium-0 font-body-default-medium-0 text-text-feedback-link-default hover:text-text-feedback-link-hover">
|
|
{undoLabel}
|
|
</span>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
);
|
|
|
|
export const Toast = ToastRoot;
|