refactor(dashboard): refine setup checklist UI
- Removed progress bar from hero for cleaner look - Made setup checklist collapsible (auto-collapsed when complete) - Improved checklist item styling with active/inactive states
This commit is contained in:
@@ -7,7 +7,6 @@ import { YStack, XStack } from '@tamagui/stacks';
|
||||
import { SizableText as Text } from '@tamagui/text';
|
||||
import { Pressable } from '@tamagui/react-native-web-lite';
|
||||
import { Image } from '@tamagui/image';
|
||||
import { Progress } from '@tamagui/progress';
|
||||
import { isSameDay, isPast, isFuture, parseISO, differenceInDays, startOfDay } from 'date-fns';
|
||||
|
||||
import { MobileShell } from './components/MobileShell';
|
||||
@@ -158,10 +157,6 @@ export default function MobileDashboardPage() {
|
||||
},
|
||||
});
|
||||
|
||||
// Calculate Readiness for Setup Checklist
|
||||
const readiness = useEventReadiness(activeEvent, t as any);
|
||||
const phase = activeEvent ? getEventPhase(activeEvent) : 'setup';
|
||||
|
||||
const locale = i18n.language?.startsWith('en') ? 'en-GB' : 'de-DE';
|
||||
|
||||
React.useEffect(() => {
|
||||
@@ -189,6 +184,10 @@ export default function MobileDashboardPage() {
|
||||
);
|
||||
}
|
||||
|
||||
// Calculate Readiness
|
||||
const readiness = useEventReadiness(activeEvent, t as any);
|
||||
const phase = activeEvent ? getEventPhase(activeEvent) : 'setup';
|
||||
|
||||
return (
|
||||
<MobileShell activeTab="home" title={t('mobileDashboard.title', 'Dashboard')}>
|
||||
|
||||
@@ -203,8 +202,8 @@ export default function MobileDashboardPage() {
|
||||
readiness={readiness}
|
||||
/>
|
||||
|
||||
{/* 1b. SETUP CHECKLIST (Only in Setup Phase) */}
|
||||
{phase === 'setup' && !readiness.isReady && (
|
||||
{/* 1b. SETUP CHECKLIST */}
|
||||
{phase === 'setup' && (
|
||||
<SetupChecklist
|
||||
steps={readiness.steps}
|
||||
title={t('management:photobooth.checklist.title', 'Checklist')}
|
||||
@@ -334,9 +333,10 @@ function LifecycleHero({ event, stats, locale, navigate, onSwitch, canSwitch, re
|
||||
);
|
||||
}
|
||||
|
||||
// SETUP PHASE
|
||||
// We removed the big button. We show high-level status.
|
||||
const progressPercent = (readiness.progress ?? 0) * 100;
|
||||
// SETUP
|
||||
const nextStep = readiness.nextStep;
|
||||
const ctaLabel = nextStep ? nextStep.ctaLabel : t('dashboard:onboarding.hero.cta', 'Setup Complete');
|
||||
const ctaAction = nextStep ? () => navigate(adminPath(nextStep.targetPath)) : undefined;
|
||||
|
||||
return (
|
||||
<YStack>
|
||||
@@ -358,17 +358,21 @@ function LifecycleHero({ event, stats, locale, navigate, onSwitch, canSwitch, re
|
||||
|
||||
<YStack height={1} backgroundColor={theme.border} />
|
||||
|
||||
<YStack space="$1.5">
|
||||
<XStack alignItems="center" justifyContent="space-between">
|
||||
<Text fontSize="$sm" color={theme.textStrong} fontWeight="600">
|
||||
{t('management:photobooth.checklist.title', 'Setup Status')}
|
||||
</Text>
|
||||
<Text fontSize="$xs" color={theme.muted}>{readiness.completedSteps}/{readiness.totalSteps}</Text>
|
||||
{/* Main CTA if not ready */}
|
||||
{!readiness.isReady && (
|
||||
<ModernButton
|
||||
label={ctaLabel}
|
||||
tone='primary'
|
||||
icon={<ArrowRight size={16} color="white" />}
|
||||
onPress={ctaAction}
|
||||
/>
|
||||
)}
|
||||
{readiness.isReady && (
|
||||
<XStack alignItems="center" space="$2">
|
||||
<CheckCircle2 size={18} color={theme.successText} />
|
||||
<Text fontSize="$sm" color={theme.successText} fontWeight="700">Ready for Liftoff</Text>
|
||||
</XStack>
|
||||
<Progress value={progressPercent} max={100} size="$2" backgroundColor={theme.surfaceMuted} borderWidth={0}>
|
||||
<Progress.Indicator backgroundColor={theme.primary} />
|
||||
</Progress>
|
||||
</YStack>
|
||||
)}
|
||||
</ModernCard>
|
||||
</YStack>
|
||||
);
|
||||
@@ -589,4 +593,4 @@ function EmptyState({ canManage, onCreate }: any) {
|
||||
{canManage && <ModernButton label={t('management:events.list.actions.create', 'Create Event')} onPress={onCreate} />}
|
||||
</YStack>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { YStack, XStack } from '@tamagui/stacks';
|
||||
import { SizableText as Text } from '@tamagui/text';
|
||||
import { Pressable } from '@tamagui/react-native-web-lite';
|
||||
import { CheckCircle2, Circle, ChevronRight, ArrowRight } from 'lucide-react';
|
||||
import { CheckCircle2, Circle, ChevronDown, ChevronUp } from 'lucide-react';
|
||||
import { useAdminTheme } from '../theme';
|
||||
import { ReadinessStep } from '../hooks/useEventReadiness';
|
||||
import { adminPath } from '../../constants';
|
||||
@@ -11,10 +11,10 @@ import { adminPath } from '../../constants';
|
||||
export function SetupChecklist({ steps, title }: { steps: ReadinessStep[]; title: string }) {
|
||||
const theme = useAdminTheme();
|
||||
const navigate = useNavigate();
|
||||
const isAllComplete = steps.every(s => s.isComplete);
|
||||
const [collapsed, setCollapsed] = React.useState(isAllComplete);
|
||||
|
||||
if (steps.every(s => s.isComplete)) {
|
||||
return null; // Don't show if all done
|
||||
}
|
||||
const completedCount = steps.filter(s => s.isComplete).length;
|
||||
|
||||
return (
|
||||
<YStack
|
||||
@@ -26,66 +26,82 @@ export function SetupChecklist({ steps, title }: { steps: ReadinessStep[]; title
|
||||
overflow="hidden"
|
||||
style={{ boxShadow: `0 2px 8px ${theme.shadow}` }}
|
||||
>
|
||||
<YStack padding="$3.5" paddingBottom="$2">
|
||||
<Text fontSize="$xs" fontWeight="700" color={theme.muted} textTransform="uppercase" letterSpacing={1}>
|
||||
{title}
|
||||
</Text>
|
||||
</YStack>
|
||||
<Pressable onPress={() => setCollapsed(!collapsed)}>
|
||||
<XStack padding="$3.5" paddingVertical="$3" alignItems="center" justifyContent="space-between">
|
||||
<XStack alignItems="center" space="$2">
|
||||
<Text fontSize="$xs" fontWeight="700" color={theme.muted} textTransform="uppercase" letterSpacing={1}>
|
||||
{title}
|
||||
</Text>
|
||||
{isAllComplete && (
|
||||
<CheckCircle2 size={14} color={theme.successText} />
|
||||
)}
|
||||
</XStack>
|
||||
|
||||
<XStack alignItems="center" space="$2">
|
||||
<Text fontSize="$xs" color={theme.muted} fontWeight="600">
|
||||
{completedCount}/{steps.length}
|
||||
</Text>
|
||||
{collapsed ? <ChevronDown size={16} color={theme.muted} /> : <ChevronUp size={16} color={theme.muted} />}
|
||||
</XStack>
|
||||
</XStack>
|
||||
</Pressable>
|
||||
|
||||
<YStack>
|
||||
{steps.map((step, index) => {
|
||||
const isNext = !step.isComplete && steps.slice(0, index).every(s => s.isComplete);
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
key={step.id}
|
||||
onPress={() => navigate(adminPath(step.targetPath))}
|
||||
style={{
|
||||
backgroundColor: isNext ? theme.surface : 'transparent',
|
||||
}}
|
||||
>
|
||||
<XStack
|
||||
paddingHorizontal="$3.5"
|
||||
paddingVertical="$3"
|
||||
alignItems="center"
|
||||
space="$3"
|
||||
borderTopWidth={1}
|
||||
borderColor={theme.border}
|
||||
>
|
||||
{step.isComplete ? (
|
||||
<CheckCircle2 size={20} color={theme.successText} />
|
||||
) : isNext ? (
|
||||
<Circle size={20} color={theme.primary} strokeWidth={2.5} />
|
||||
) : (
|
||||
<Circle size={20} color={theme.border} />
|
||||
)}
|
||||
{!collapsed && (
|
||||
<YStack>
|
||||
{steps.map((step, index) => {
|
||||
const isNext = !step.isComplete && steps.slice(0, index).every(s => s.isComplete);
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
key={step.id}
|
||||
onPress={() => navigate(adminPath(step.targetPath))}
|
||||
style={{
|
||||
backgroundColor: isNext ? theme.surface : 'transparent',
|
||||
}}
|
||||
>
|
||||
<XStack
|
||||
paddingHorizontal="$3.5"
|
||||
paddingVertical="$3"
|
||||
alignItems="center"
|
||||
space="$3"
|
||||
borderTopWidth={1}
|
||||
borderColor={theme.border}
|
||||
>
|
||||
{step.isComplete ? (
|
||||
<CheckCircle2 size={20} color={theme.successText} />
|
||||
) : isNext ? (
|
||||
<Circle size={20} color={theme.primary} strokeWidth={2.5} />
|
||||
) : (
|
||||
<Circle size={20} color={theme.border} />
|
||||
)}
|
||||
|
||||
<YStack flex={1} space="$0.5">
|
||||
<Text
|
||||
fontSize="$sm"
|
||||
fontWeight={isNext ? "700" : "500"}
|
||||
color={step.isComplete ? theme.muted : theme.textStrong}
|
||||
textDecorationLine={step.isComplete ? 'line-through' : 'none'}
|
||||
>
|
||||
{step.label}
|
||||
</Text>
|
||||
{step.description && !step.isComplete && (
|
||||
<Text fontSize="$xs" color={theme.muted}>
|
||||
{step.description}
|
||||
</Text>
|
||||
)}
|
||||
</YStack>
|
||||
<YStack flex={1} space="$0.5">
|
||||
<Text
|
||||
fontSize="$sm"
|
||||
fontWeight={isNext ? "700" : "500"}
|
||||
color={step.isComplete ? theme.muted : theme.textStrong}
|
||||
textDecorationLine={step.isComplete ? 'line-through' : 'none'}
|
||||
>
|
||||
{step.label}
|
||||
</Text>
|
||||
{step.description && !step.isComplete && (
|
||||
<Text fontSize="$xs" color={theme.muted}>
|
||||
{step.description}
|
||||
</Text>
|
||||
)}
|
||||
</YStack>
|
||||
|
||||
{isNext && (
|
||||
<YStack backgroundColor={theme.primary} borderRadius={999} paddingHorizontal="$2.5" paddingVertical="$1.5">
|
||||
<Text fontSize="$xs" color="white" fontWeight="700">Start</Text>
|
||||
</YStack>
|
||||
)}
|
||||
</XStack>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</YStack>
|
||||
{isNext && (
|
||||
<YStack backgroundColor={theme.primary} borderRadius={999} paddingHorizontal="$2.5" paddingVertical="$1.5">
|
||||
<Text fontSize="$xs" color="white" fontWeight="700">Start</Text>
|
||||
</YStack>
|
||||
)}
|
||||
</XStack>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</YStack>
|
||||
)}
|
||||
</YStack>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user