import React from 'react'; import { Card } from '@tamagui/card'; import { YStack, XStack } from '@tamagui/stacks'; import { SizableText as Text } from '@tamagui/text'; import { Pressable } from '@tamagui/react-native-web-lite'; import { Tabs, Separator } from 'tamagui'; import { useAdminTheme } from '../theme'; import { withAlpha } from './colors'; export function MobileCard({ children, className, style, ...rest }: React.ComponentProps) { const { surface, border, shadow, glassSurface, glassBorder, glassShadow } = useAdminTheme(); return ( {children} ); } export function PillBadge({ tone = 'muted', children, }: { tone?: 'success' | 'warning' | 'danger' | 'muted'; children: React.ReactNode; }) { const { successText, warningText, dangerText, muted, surfaceMuted, border, successBg, warningBg, dangerBg } = useAdminTheme(); const palette: Record = { success: { bg: successBg ?? withAlpha(successText, 0.16), text: successText, border: withAlpha(successText, 0.35), }, warning: { bg: warningBg ?? withAlpha(warningText, 0.16), text: warningText, border: withAlpha(warningText, 0.35), }, danger: { bg: dangerBg ?? withAlpha(dangerText, 0.16), text: dangerText, border: withAlpha(dangerText, 0.35), }, muted: { bg: surfaceMuted, text: muted, border, }, }; const colors = palette[tone] ?? palette.muted; return ( {children} ); } export function CTAButton({ label, onPress, tone = 'primary', fullWidth = true, disabled = false, loading = false, style, iconLeft, iconRight, }: { label: string; onPress?: () => void; tone?: 'primary' | 'ghost' | 'danger'; fullWidth?: boolean; disabled?: boolean; loading?: boolean; style?: any; iconLeft?: React.ReactNode; iconRight?: React.ReactNode; }) { const { primary, surface, border, text, danger, glassSurfaceStrong } = useAdminTheme(); const isPrimary = tone === 'primary'; const isDanger = tone === 'danger'; const isDisabled = disabled || loading || !onPress; const backgroundColor = isDanger ? danger : isPrimary ? primary : glassSurfaceStrong ?? surface; const borderColor = isPrimary || isDanger ? 'transparent' : border; const labelColor = isPrimary || isDanger ? 'white' : text; const primaryStyle = isPrimary ? { boxShadow: `0 18px 28px ${withAlpha(primary, 0.4)}`, } : undefined; return ( {iconLeft} {label} {iconRight} ); } export function KpiTile({ icon: IconCmp, label, value, note, color, }: { icon: React.ComponentType<{ size?: number; color?: string }>; label: string; value: string | number; note?: string; color?: string; }) { const { textStrong, textMuted, primary, accentSoft } = useAdminTheme(); const iconBg = color ? withAlpha(color, 0.12) : accentSoft; const iconColor = color || primary; return ( {value} {label} {note ? ( {note} ) : null} ); } export function KpiStrip({ items }: { items: Array<{ icon: React.ComponentType<{ size?: number; color?: string }>; label: string; value: string | number; color?: string; note?: string; tone?: 'accent' | 'neutral'; }> }) { const { glassSurface, glassSurfaceStrong, glassBorder, border, textStrong, textMuted, primary, surfaceMuted, surface } = useAdminTheme(); const cardSurface = surfaceMuted ?? glassSurfaceStrong ?? glassSurface ?? surface; const cardBorder = glassBorder ?? border; const separatorColor = withAlpha(textStrong, 0.12); const innerSeparatorColor = withAlpha(textStrong, 0.08); return ( {items.map((item, index) => { const isNeutral = item.tone === 'neutral'; const iconColor = isNeutral ? textStrong : item.color || primary; const iconBg = isNeutral ? surfaceMuted : withAlpha(iconColor, 0.12); return ( {item.value} {item.label} {item.note ? ( {item.note} ) : null} ); })} ); } export function SkeletonCard({ height = 80 }: { height?: number }) { return ( ); } export function ActionTile({ icon: IconCmp, label, note, noteTone = 'muted', color, onPress, disabled = false, variant = 'grid', delayMs = 0, }: { icon: React.ComponentType<{ size?: number; color?: string }>; label: string; note?: string; noteTone?: 'warning' | 'muted'; color: string; onPress?: () => void; disabled?: boolean; variant?: 'grid' | 'cluster'; delayMs?: number; }) { const { textStrong, glassSurface, muted, warningText } = useAdminTheme(); const noteColor = noteTone === 'warning' ? warningText : muted; const isCluster = variant === 'cluster'; const backgroundColor = withAlpha(color, 0.12); const borderColor = withAlpha(color, 0.4); const shadowColor = withAlpha(color, 0.35); const iconShadow = withAlpha(color, 0.5); const tileStyle = { ...(delayMs ? { animationDelay: `${delayMs}ms` } : {}), backgroundImage: `linear-gradient(135deg, ${backgroundColor}, ${withAlpha(color, 0.08)})`, boxShadow: isCluster ? `0 12px 18px ${shadowColor}` : `0 20px 30px ${shadowColor}`, }; return ( {label} {note ? ( {note} ) : null} ); } export function FloatingActionButton({ onPress, label, icon: IconCmp, }: { onPress: () => void; label: string; icon: React.ComponentType<{ size?: number; color?: string }>; }) { const { primary, shadow } = useAdminTheme(); const [pressed, setPressed] = React.useState(false); return ( setPressed(true)} onPressOut={() => setPressed(false)} onPointerLeave={() => setPressed(false)} style={{ position: 'fixed', right: 18, bottom: 'calc(env(safe-area-inset-bottom, 0px) + 96px)', zIndex: 60, transform: pressed ? 'scale(0.96)' : 'scale(1)', opacity: pressed ? 0.92 : 1, transition: 'transform 140ms ease, opacity 140ms ease', }} aria-label={label} > {label} ); } export function ContentTabs({ value, onValueChange, tabs, header, }: { value: string; onValueChange: (val: string) => void; tabs: { value: string; label: string; content: React.ReactNode }[]; header?: React.ReactNode; }) { const { border, muted, primary } = useAdminTheme(); return ( } disablePassBorderRadius="bottom" backgroundColor="$surface"> {tabs.map((tab) => ( {tab.label} ))} {header} {tabs.map((tab) => ( {tab.content} ))} ); }