Compact dashboard overview

This commit is contained in:
Codex Agent
2026-01-22 16:51:05 +01:00
parent 20f60e9277
commit 7ed4e581f7
2 changed files with 43 additions and 30 deletions

View File

@@ -199,16 +199,15 @@ export default function MobileDashboardPage() {
return ( return (
<MobileShell activeTab="home" title={t('mobileDashboard.title', 'Dashboard')}> <MobileShell activeTab="home" title={t('mobileDashboard.title', 'Dashboard')}>
<DashboardCard padding="$0"> <DashboardCard padding="$0">
<YStack padding="$3.5" space="$2"> <YStack padding="$3" space="$2">
<SectionHeader <SectionHeader
title={t('dashboard:overview.title', 'At a glance')} title={t('dashboard:overview.title', 'At a glance')}
subtitle={t('dashboard:overview.description', 'Key customer metrics at a glance.')}
showSeparator={false} showSeparator={false}
compact compact
/> />
</YStack> </YStack>
<Separator backgroundColor={theme.border} opacity={0.6} /> <Separator backgroundColor={theme.border} opacity={0.6} />
<YStack padding="$3.5" space="$2.5"> <YStack padding="$3" space="$2.5">
{/* 1. LIFECYCLE HERO */} {/* 1. LIFECYCLE HERO */}
<LifecycleHero <LifecycleHero
event={activeEvent} event={activeEvent}
@@ -223,12 +222,13 @@ export default function MobileDashboardPage() {
{phase === 'setup' && ( {phase === 'setup' && (
<SetupChecklist <SetupChecklist
steps={readiness.steps} steps={readiness.steps}
title={t('management:photobooth.checklist.title', 'Checklist')} title={t('dashboard:readiness.title', 'Bereit für den Eventstart')}
variant="embedded" variant="embedded"
/> />
)} )}
{/* 2. PULSE STRIP */} {/* 2. PULSE STRIP */}
<Separator backgroundColor={theme.border} opacity={0.6} />
<PulseStrip event={activeEvent} stats={stats} /> <PulseStrip event={activeEvent} stats={stats} />
</YStack> </YStack>
</DashboardCard> </DashboardCard>
@@ -400,6 +400,7 @@ function LifecycleHero({ event, stats, locale, navigate, readiness, variant = 'd
const nextStep = readiness.nextStep; const nextStep = readiness.nextStep;
const ctaLabel = nextStep ? nextStep.ctaLabel : t('dashboard:onboarding.hero.cta', 'Setup Complete'); const ctaLabel = nextStep ? nextStep.ctaLabel : t('dashboard:onboarding.hero.cta', 'Setup Complete');
const ctaAction = nextStep ? () => navigate(adminPath(nextStep.targetPath)) : undefined; const ctaAction = nextStep ? () => navigate(adminPath(nextStep.targetPath)) : undefined;
const showCta = Boolean(nextStep);
return ( return (
<YStack space="$2"> <YStack space="$2">
@@ -423,9 +424,9 @@ function LifecycleHero({ event, stats, locale, navigate, readiness, variant = 'd
</YStack> </YStack>
</XStack> </XStack>
<Separator backgroundColor={theme.border} opacity={0.6} /> {showCta ? <Separator backgroundColor={theme.border} opacity={0.6} /> : null}
{!readiness.isReady ? ( {showCta ? (
<Button <Button
onPress={ctaAction} onPress={ctaAction}
backgroundColor={theme.primary} backgroundColor={theme.primary}
@@ -440,14 +441,7 @@ function LifecycleHero({ event, stats, locale, navigate, readiness, variant = 'd
<ChevronRight size={16} color="white" /> <ChevronRight size={16} color="white" />
</XStack> </XStack>
</Button> </Button>
) : ( ) : null}
<XStack alignItems="center" space="$2">
<CheckCircle2 size={18} color={theme.successText} />
<Text fontSize="$sm" color={theme.successText} fontWeight="700">
{t('management:mobileDashboard.readyForLiftoff', 'Ready for Liftoff')}
</Text>
</XStack>
)}
</YStack> </YStack>
</DashboardCard> </DashboardCard>
</YStack> </YStack>

View File

@@ -1,8 +1,10 @@
import React from 'react'; import React from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Card } from '@tamagui/card'; import { Card } from '@tamagui/card';
import { YGroup } from '@tamagui/group'; import { YGroup } from '@tamagui/group';
import { ListItem } from '@tamagui/list-item'; import { ListItem } from '@tamagui/list-item';
import { Progress } from '@tamagui/progress';
import { YStack, XStack } from '@tamagui/stacks'; import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text'; import { SizableText as Text } from '@tamagui/text';
import { Pressable } from '@tamagui/react-native-web-lite'; import { Pressable } from '@tamagui/react-native-web-lite';
@@ -23,12 +25,15 @@ export function SetupChecklist({
variant?: 'card' | 'embedded'; variant?: 'card' | 'embedded';
}) { }) {
const theme = useAdminTheme(); const theme = useAdminTheme();
const { t } = useTranslation('dashboard');
const navigate = useNavigate(); const navigate = useNavigate();
const isAllComplete = steps.every(s => s.isComplete); const isAllComplete = steps.every(s => s.isComplete);
const [collapsed, setCollapsed] = React.useState(isAllComplete); const [collapsed, setCollapsed] = React.useState(isAllComplete);
const isEmbedded = variant === 'embedded'; const isEmbedded = variant === 'embedded';
const completedCount = steps.filter(s => s.isComplete).length; const completedCount = steps.filter(s => s.isComplete).length;
const totalSteps = steps.length;
const progressValue = totalSteps > 0 ? Math.round((completedCount / totalSteps) * 100) : 0;
return ( return (
<Card <Card
@@ -44,23 +49,37 @@ export function SetupChecklist({
shadowOffset={isEmbedded ? { width: 0, height: 0 } : { width: 0, height: 10 }} shadowOffset={isEmbedded ? { width: 0, height: 0 } : { width: 0, height: 10 }}
> >
<Pressable onPress={() => setCollapsed(!collapsed)}> <Pressable onPress={() => setCollapsed(!collapsed)}>
<XStack padding="$3" paddingVertical="$2.5" alignItems="center" justifyContent="space-between"> <YStack padding="$3" paddingVertical="$2.5" space="$2">
<XStack alignItems="center" space="$2"> <XStack alignItems="center" justifyContent="space-between">
<Text fontSize="$xs" fontWeight="700" color={theme.muted} textTransform="uppercase" letterSpacing={1}> <XStack alignItems="center" space="$2">
<Text fontSize="$xs" fontWeight="700" color={theme.muted} textTransform="uppercase" letterSpacing={1}>
{title} {title}
</Text> </Text>
{isAllComplete && ( <PillBadge tone={isAllComplete ? 'success' : 'warning'}>
<CheckCircle2 size={14} color={theme.successText} /> {isAllComplete ? t('readiness.complete', 'Erledigt') : t('readiness.pending', 'Noch offen')}
)} </PillBadge>
</XStack> </XStack>
<XStack alignItems="center" space="$2"> <XStack alignItems="center" space="$2">
<Text fontSize="$xs" color={theme.muted} fontWeight="600"> <Text fontSize="$xs" color={theme.muted} fontWeight="600">
{completedCount}/{steps.length} {completedCount}/{steps.length}
</Text> </Text>
{collapsed ? <ChevronDown size={16} color={theme.muted} /> : <ChevronUp size={16} color={theme.muted} />} {collapsed ? <ChevronDown size={16} color={theme.muted} /> : <ChevronUp size={16} color={theme.muted} />}
</XStack> </XStack>
</XStack> </XStack>
<Progress
value={progressValue}
size="$1"
backgroundColor={theme.surfaceMuted}
height={6}
>
<Progress.Indicator
backgroundColor={isAllComplete ? theme.success : theme.primary}
animation="bouncy"
/>
</Progress>
</YStack>
</Pressable> </Pressable>
{!collapsed && ( {!collapsed && (