der tenant admin hat eine neue, mobil unterstützende UI, login redirect funktioniert, typescript fehler wurden bereinigt. Neue Blog Posts von ChatGPT eingebaut, übersetzt von Gemini 2.5
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import React from 'react';
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
|
||||
import { FrostedCard } from './frosted-surface';
|
||||
import { ChecklistRow } from './checklist-row';
|
||||
|
||||
export type ChecklistStep = {
|
||||
key: string;
|
||||
title: string;
|
||||
description: string;
|
||||
done: boolean;
|
||||
ctaLabel?: string | null;
|
||||
onAction?: () => void;
|
||||
icon: LucideIcon;
|
||||
};
|
||||
|
||||
type TenantOnboardingChecklistCardProps = {
|
||||
title: string;
|
||||
description?: string;
|
||||
steps: ChecklistStep[];
|
||||
completedLabel: string;
|
||||
pendingLabel: string;
|
||||
completionPercent: number;
|
||||
completedCount: number;
|
||||
totalCount: number;
|
||||
emptyCopy?: string;
|
||||
fallbackActionLabel?: string;
|
||||
};
|
||||
|
||||
export function TenantOnboardingChecklistCard({
|
||||
title,
|
||||
description,
|
||||
steps,
|
||||
completedLabel,
|
||||
pendingLabel,
|
||||
completionPercent,
|
||||
completedCount,
|
||||
totalCount,
|
||||
emptyCopy,
|
||||
fallbackActionLabel,
|
||||
}: TenantOnboardingChecklistCardProps) {
|
||||
return (
|
||||
<FrostedCard className="relative overflow-hidden">
|
||||
<CardHeader className="relative flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute -top-12 -right-16 h-32 w-32 rounded-full bg-rose-200/40 blur-3xl"
|
||||
/>
|
||||
<div className="relative">
|
||||
<CardTitle>{title}</CardTitle>
|
||||
{description ? <CardDescription>{description}</CardDescription> : null}
|
||||
</div>
|
||||
<Badge className="bg-brand-rose-soft text-brand-rose">
|
||||
{completionPercent}% · {completedCount}/{totalCount}
|
||||
</Badge>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<Progress value={completionPercent} className="h-2 bg-rose-100 motion-safe:animate-pulse" />
|
||||
<div className="space-y-3">
|
||||
{steps.map((step) => {
|
||||
const Icon = step.icon;
|
||||
|
||||
return (
|
||||
<ChecklistRow
|
||||
key={step.key}
|
||||
icon={<Icon className="h-5 w-5" />}
|
||||
label={step.title}
|
||||
hint={step.description}
|
||||
completed={step.done}
|
||||
status={{ complete: completedLabel, pending: pendingLabel }}
|
||||
action={
|
||||
step.done || (!step.ctaLabel && !fallbackActionLabel)
|
||||
? undefined
|
||||
: {
|
||||
label: step.ctaLabel ?? fallbackActionLabel ?? '',
|
||||
onClick: () => step.onAction?.(),
|
||||
disabled: !step.onAction,
|
||||
}
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{steps.length === 0 && emptyCopy ? (
|
||||
<p className="text-sm text-slate-600 dark:text-slate-300">{emptyCopy}</p>
|
||||
) : null}
|
||||
</CardContent>
|
||||
</FrostedCard>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user