neues Admin UI Layout eingeführt. Alle Tests auf den neusten Stand gebracht.
This commit is contained in:
@@ -2,20 +2,25 @@ import React from 'react';
|
||||
import { YStack, XStack } from '@tamagui/stacks';
|
||||
import { SizableText as Text } from '@tamagui/text';
|
||||
import { Pressable } from '@tamagui/react-native-web-lite';
|
||||
import { useTheme } from '@tamagui/core';
|
||||
import { useAdminTheme } from '../theme';
|
||||
|
||||
export function MobileCard({ children, ...rest }: React.ComponentProps<typeof YStack>) {
|
||||
const theme = useTheme();
|
||||
export function MobileCard({
|
||||
children,
|
||||
className,
|
||||
...rest
|
||||
}: React.ComponentProps<typeof YStack>) {
|
||||
const { surface, border, shadow } = useAdminTheme();
|
||||
return (
|
||||
<YStack
|
||||
backgroundColor={String(theme.surface?.val ?? 'white')}
|
||||
borderRadius={16}
|
||||
className={['admin-fade-up', className].filter(Boolean).join(' ')}
|
||||
backgroundColor={surface}
|
||||
borderRadius={18}
|
||||
borderWidth={1}
|
||||
borderColor={String(theme.borderColor?.val ?? '#e5e7eb')}
|
||||
shadowColor="#0f172a"
|
||||
shadowOpacity={0.06}
|
||||
shadowRadius={12}
|
||||
shadowOffset={{ width: 0, height: 8 }}
|
||||
borderColor={border}
|
||||
shadowColor={shadow}
|
||||
shadowOpacity={0.08}
|
||||
shadowRadius={14}
|
||||
shadowOffset={{ width: 0, height: 10 }}
|
||||
padding="$3.5"
|
||||
space="$2"
|
||||
{...rest}
|
||||
@@ -32,7 +37,7 @@ export function PillBadge({
|
||||
tone?: 'success' | 'warning' | 'muted';
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
const { theme } = useAdminTheme();
|
||||
const palette: Record<typeof tone, { bg: string; text: string; border: string }> = {
|
||||
success: {
|
||||
bg: String(theme.backgroundStrong?.val ?? '#ecfdf3'),
|
||||
@@ -83,7 +88,7 @@ export function CTAButton({
|
||||
disabled?: boolean;
|
||||
loading?: boolean;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
const { primary, surface, border, text } = useAdminTheme();
|
||||
const isPrimary = tone === 'primary';
|
||||
const isDisabled = disabled || loading;
|
||||
return (
|
||||
@@ -97,15 +102,15 @@ export function CTAButton({
|
||||
}}
|
||||
>
|
||||
<XStack
|
||||
height={56}
|
||||
borderRadius={14}
|
||||
height={52}
|
||||
borderRadius={16}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
backgroundColor={isPrimary ? String(theme.primary?.val ?? '#007AFF') : String(theme.surface?.val ?? 'white')}
|
||||
backgroundColor={isPrimary ? primary : surface}
|
||||
borderWidth={isPrimary ? 0 : 1}
|
||||
borderColor={isPrimary ? 'transparent' : String(theme.borderColor?.val ?? '#e5e7eb')}
|
||||
borderColor={isPrimary ? 'transparent' : border}
|
||||
>
|
||||
<Text fontSize="$sm" fontWeight="800" color={isPrimary ? 'white' : String(theme.color?.val ?? '#111827')}>
|
||||
<Text fontSize="$sm" fontWeight="800" color={isPrimary ? 'white' : text}>
|
||||
{label}
|
||||
</Text>
|
||||
</XStack>
|
||||
@@ -122,7 +127,7 @@ export function KpiTile({
|
||||
label: string;
|
||||
value: string | number;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
const { accentSoft, primary, text } = useAdminTheme();
|
||||
return (
|
||||
<MobileCard borderRadius={14} padding="$3" width="32%" minWidth={110} alignItems="flex-start">
|
||||
<XStack alignItems="center" space="$2">
|
||||
@@ -130,17 +135,17 @@ export function KpiTile({
|
||||
width={32}
|
||||
height={32}
|
||||
borderRadius={12}
|
||||
backgroundColor={String(theme.blue3?.val ?? '#e5f0ff')}
|
||||
backgroundColor={accentSoft}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<IconCmp size={16} color={String(theme.primary?.val ?? '#2563eb')} />
|
||||
<IconCmp size={16} color={primary} />
|
||||
</XStack>
|
||||
<Text fontSize="$xs" color={String(theme.color?.val ?? '#111827')}>
|
||||
<Text fontSize="$xs" color={text}>
|
||||
{label}
|
||||
</Text>
|
||||
</XStack>
|
||||
<Text fontSize="$xl" fontWeight="800" color={String(theme.color?.val ?? '#111827')}>
|
||||
<Text fontSize="$xl" fontWeight="800" color={text}>
|
||||
{value}
|
||||
</Text>
|
||||
</MobileCard>
|
||||
@@ -159,15 +164,16 @@ export function ActionTile({
|
||||
color,
|
||||
onPress,
|
||||
disabled = false,
|
||||
delayMs = 0,
|
||||
}: {
|
||||
icon: React.ComponentType<{ size?: number; color?: string }>;
|
||||
label: string;
|
||||
color: string;
|
||||
onPress?: () => void;
|
||||
disabled?: boolean;
|
||||
delayMs?: number;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
const text = String(theme.color12?.val ?? theme.color?.val ?? '#f8fafc');
|
||||
const { textStrong } = useAdminTheme();
|
||||
return (
|
||||
<Pressable
|
||||
onPress={disabled ? undefined : onPress}
|
||||
@@ -175,6 +181,8 @@ export function ActionTile({
|
||||
disabled={disabled}
|
||||
>
|
||||
<YStack
|
||||
className="admin-fade-up"
|
||||
style={delayMs ? { animationDelay: `${delayMs}ms` } : undefined}
|
||||
borderRadius={16}
|
||||
padding="$3"
|
||||
space="$2.5"
|
||||
@@ -188,7 +196,7 @@ export function ActionTile({
|
||||
<XStack width={34} height={34} borderRadius={12} backgroundColor={color} alignItems="center" justifyContent="center">
|
||||
<IconCmp size={16} color="white" />
|
||||
</XStack>
|
||||
<Text fontSize="$sm" fontWeight="700" color={text} textAlign="center">
|
||||
<Text fontSize="$sm" fontWeight="700" color={textStrong} textAlign="center">
|
||||
{label}
|
||||
</Text>
|
||||
</YStack>
|
||||
@@ -205,7 +213,7 @@ export function FloatingActionButton({
|
||||
label: string;
|
||||
icon: React.ComponentType<{ size?: number; color?: string }>;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
const { primary, shadow } = useAdminTheme();
|
||||
const [pressed, setPressed] = React.useState(false);
|
||||
return (
|
||||
<Pressable
|
||||
@@ -231,8 +239,8 @@ export function FloatingActionButton({
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
space="$2"
|
||||
backgroundColor={String(theme.primary?.val ?? '#007AFF')}
|
||||
shadowColor="#0f172a"
|
||||
backgroundColor={primary}
|
||||
shadowColor={shadow}
|
||||
shadowOpacity={0.2}
|
||||
shadowRadius={16}
|
||||
shadowOffset={{ width: 0, height: 8 }}
|
||||
|
||||
Reference in New Issue
Block a user