first implementation of tamagui mobile pages

This commit is contained in:
Codex Agent
2025-12-10 15:49:08 +01:00
parent 5c93bfa405
commit 9930b272ca
39 changed files with 491904 additions and 2727 deletions

View File

@@ -0,0 +1,159 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Button } from '@tamagui/button';
import { Pressable } from '@tamagui/react-native-web-lite';
import { useTheme } from '@tamagui/core';
import { Home, BarChart2, Settings } from 'lucide-react';
export function AppCard({ children, padding = '$4', ...rest }: React.ComponentProps<typeof YStack> & { padding?: keyof typeof rest }) {
return (
<YStack
bg="$surface"
borderRadius="$card"
borderWidth={1}
borderColor="$muted"
shadowColor="#0f172a"
shadowOpacity={0.05}
shadowRadius={12}
shadowOffset={{ width: 0, height: 8 }}
padding={padding as any}
space="$3"
{...rest}
>
{children}
</YStack>
);
}
export function StatusPill({ tone = 'muted', children }: { tone?: 'success' | 'warning' | 'muted'; children: React.ReactNode }) {
const colors: Record<typeof tone, { bg: string; color: string; border: string }> = {
success: { bg: '#ecfdf3', color: '#047857', border: '#bbf7d0' },
warning: { bg: '#fffbeb', color: '#92400e', border: '#fef3c7' },
muted: { bg: '#f3f4f6', color: '#374151', border: '#e5e7eb' },
};
const palette = colors[tone] ?? colors.muted;
return (
<XStack
alignItems="center"
paddingHorizontal="$2.5"
paddingVertical="$1"
borderRadius="$pill"
borderWidth={1}
backgroundColor={palette.bg}
borderColor={palette.border}
alignSelf="flex-start"
>
<Text fontSize={11} fontWeight="700" color={palette.color}>
{children}
</Text>
</XStack>
);
}
export function PrimaryCTA({ label, onPress }: { label: string; onPress: () => void }) {
return (
<Button
backgroundColor="$primary"
color="white"
height={56}
borderRadius="$card"
fontWeight="700"
onPress={onPress}
pressStyle={{ opacity: 0.9 }}
>
{label}
</Button>
);
}
export function Segmented({
options,
value,
onChange,
}: {
options: Array<{ key: string; label: string }>;
value: string;
onChange: (key: string) => void;
}) {
return (
<XStack bg="$muted" borderRadius="$pill" borderWidth={1} borderColor="$muted" padding="$1" space="$1">
{options.map((option) => {
const active = option.key === value;
return (
<Pressable key={option.key} onPress={() => onChange(option.key)} style={{ flex: 1 }}>
<YStack
bg={active ? '$primary' : 'transparent'}
borderRadius="$pill"
paddingVertical="$2"
alignItems="center"
justifyContent="center"
>
<Text color={active ? 'white' : '$color'} fontWeight="700" fontSize="$sm">
{option.label}
</Text>
</YStack>
</Pressable>
);
})}
</XStack>
);
}
export function MetaRow({ date, location, status }: { date: string; location: string; status: string }) {
return (
<YStack space="$1">
<Text fontSize="$sm" color="$color">{date}</Text>
<Text fontSize="$sm" color="$color">{location}</Text>
<StatusPill tone="muted">{status}</StatusPill>
</YStack>
);
}
export function BottomNav({
active,
onNavigate,
}: {
active: 'events' | 'analytics' | 'settings';
onNavigate: (key: 'events' | 'analytics' | 'settings') => void;
}) {
const theme = useTheme();
const items = [
{ key: 'events', icon: Home, label: 'Events' },
{ key: 'analytics', icon: BarChart2, label: 'Analytics' },
{ key: 'settings', icon: Settings, label: 'Settings' },
];
return (
<XStack
position="fixed"
bottom={0}
left={0}
right={0}
bg="$background"
borderTopWidth={1}
borderColor="$muted"
padding="$3"
justifyContent="space-around"
shadowColor="#0f172a"
shadowOpacity={0.08}
shadowRadius={12}
shadowOffset={{ width: 0, height: -4 }}
zIndex={50}
>
{items.map((item) => {
const activeState = item.key === active;
const IconCmp = item.icon;
return (
<Pressable key={item.key} onPress={() => onNavigate(item.key as typeof active)}>
<YStack alignItems="center" space="$1">
<IconCmp size={20} color={activeState ? String(theme.primary?.val ?? '#007AFF') : '#9ca3af'} />
<Text fontSize="$xs" color={activeState ? '$primary' : '$muted'}>
{item.label}
</Text>
</YStack>
</Pressable>
);
})}
</XStack>
);
}