Refine dashboard overview layout
This commit is contained in:
@@ -1,33 +1,50 @@
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Card } from '@tamagui/card';
|
||||
import { YGroup } from '@tamagui/group';
|
||||
import { ListItem } from '@tamagui/list-item';
|
||||
import { YStack, XStack } from '@tamagui/stacks';
|
||||
import { SizableText as Text } from '@tamagui/text';
|
||||
import { Pressable } from '@tamagui/react-native-web-lite';
|
||||
import { CheckCircle2, Circle, ChevronDown, ChevronUp } from 'lucide-react';
|
||||
import { Separator } from 'tamagui';
|
||||
import { useAdminTheme } from '../theme';
|
||||
import { ReadinessStep } from '../hooks/useEventReadiness';
|
||||
import { adminPath } from '../../constants';
|
||||
import { PillBadge } from './Primitives';
|
||||
|
||||
export function SetupChecklist({ steps, title }: { steps: ReadinessStep[]; title: string }) {
|
||||
export function SetupChecklist({
|
||||
steps,
|
||||
title,
|
||||
variant = 'card',
|
||||
}: {
|
||||
steps: ReadinessStep[];
|
||||
title: string;
|
||||
variant?: 'card' | 'embedded';
|
||||
}) {
|
||||
const theme = useAdminTheme();
|
||||
const navigate = useNavigate();
|
||||
const isAllComplete = steps.every(s => s.isComplete);
|
||||
const [collapsed, setCollapsed] = React.useState(isAllComplete);
|
||||
const isEmbedded = variant === 'embedded';
|
||||
|
||||
const completedCount = steps.filter(s => s.isComplete).length;
|
||||
|
||||
return (
|
||||
<YStack
|
||||
<Card
|
||||
backgroundColor={theme.surface}
|
||||
borderRadius={16}
|
||||
borderRadius={isEmbedded ? 16 : 20}
|
||||
borderWidth={1}
|
||||
borderColor={theme.border}
|
||||
padding="$0"
|
||||
overflow="hidden"
|
||||
style={{ boxShadow: `0 2px 8px ${theme.shadow}` }}
|
||||
shadowColor={theme.shadow}
|
||||
shadowOpacity={isEmbedded ? 0 : 0.16}
|
||||
shadowRadius={isEmbedded ? 0 : 16}
|
||||
shadowOffset={isEmbedded ? { width: 0, height: 0 } : { width: 0, height: 10 }}
|
||||
>
|
||||
<Pressable onPress={() => setCollapsed(!collapsed)}>
|
||||
<XStack padding="$3.5" paddingVertical="$3" alignItems="center" justifyContent="space-between">
|
||||
<XStack padding="$3" paddingVertical="$2.5" alignItems="center" justifyContent="space-between">
|
||||
<XStack alignItems="center" space="$2">
|
||||
<Text fontSize="$xs" fontWeight="700" color={theme.muted} textTransform="uppercase" letterSpacing={1}>
|
||||
{title}
|
||||
@@ -48,60 +65,54 @@ export function SetupChecklist({ steps, title }: { steps: ReadinessStep[]; title
|
||||
|
||||
{!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} />
|
||||
)}
|
||||
<Separator backgroundColor={theme.border} opacity={0.6} />
|
||||
<YGroup {...({ borderRadius: "$4", borderWidth: 1, borderColor: theme.border, overflow: "hidden" } as any)}>
|
||||
{steps.map((step, index) => {
|
||||
const isNext = !step.isComplete && steps.slice(0, index).every(s => s.isComplete);
|
||||
|
||||
<YStack flex={1} space="$0.5">
|
||||
<Text
|
||||
fontSize="$sm"
|
||||
fontWeight={isNext ? "700" : "500"}
|
||||
return (
|
||||
<YGroup.Item key={step.id}>
|
||||
<ListItem
|
||||
hoverTheme
|
||||
pressTheme
|
||||
paddingVertical="$2"
|
||||
paddingHorizontal="$3"
|
||||
backgroundColor={isNext ? theme.surfaceMuted : 'transparent'}
|
||||
onPress={() => navigate(adminPath(step.targetPath))}
|
||||
title={
|
||||
<XStack alignItems="center" space="$2.5">
|
||||
{step.isComplete ? (
|
||||
<CheckCircle2 size={18} color={theme.successText} />
|
||||
) : isNext ? (
|
||||
<Circle size={18} color={theme.primary} strokeWidth={2.5} />
|
||||
) : (
|
||||
<Circle size={18} color={theme.border} />
|
||||
)}
|
||||
<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>
|
||||
);
|
||||
})}
|
||||
</Text>
|
||||
</XStack>
|
||||
}
|
||||
subTitle={
|
||||
step.description && !step.isComplete ? (
|
||||
<Text fontSize="$xs" color={theme.muted}>
|
||||
{step.description}
|
||||
</Text>
|
||||
) : undefined
|
||||
}
|
||||
iconAfter={isNext ? <PillBadge tone="success">Start</PillBadge> : undefined}
|
||||
/>
|
||||
</YGroup.Item>
|
||||
);
|
||||
})}
|
||||
</YGroup>
|
||||
</YStack>
|
||||
)}
|
||||
</YStack>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user