import { router } from 'expo-router'; import React, { useState } from 'react'; import { ActivityIndicator, Pressable, RefreshControl, ScrollView, StyleSheet, Text, TextInput, View, } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { clearCache } from '../lib/api'; import { C } from '../lib/theme'; import type { Priority } from '../lib/types'; /* Scrollable screen shell with pull-to-refresh (clears the API cache) */ export function Screen({ children, onRefresh, }: { children: React.ReactNode; onRefresh?: () => void; }) { const insets = useSafeAreaInsets(); const [refreshing, setRefreshing] = useState(false); return ( { setRefreshing(true); clearCache(); onRefresh(); setTimeout(() => setRefreshing(false), 400); }} /> ) : undefined }> {children} ); } export function Loading() { return ( ); } export function ErrBox({ msg }: { msg: string }) { return ( Could not load data — {msg}. Pull down to retry. ); } export function Sec({ title, count }: { title: string; count?: number | string }) { return ( {title.toUpperCase()} {count != null && {count}} ); } export function TileGrid({ children }: { children: React.ReactNode }) { return {children}; } export function Tile({ label, value, unit, sub, delta, deltaTone, }: { label: string; value: string; unit?: string; sub?: string; delta?: string; deltaTone?: 'good' | 'bad' | 'neutral'; }) { const toneColor = deltaTone === 'good' ? C.green : deltaTone === 'bad' ? C.red : C.muted; return ( {label.toUpperCase()} {value} {unit ? {unit} : null} {delta ? {delta} : null} {sub ? {sub} : null} ); } /* Today-screen navigation card */ export function NavCard({ title, big, unit, note, flagline, okline, href, }: { title: string; big: string; unit?: string; note?: string; flagline?: string; okline?: string; href: string; }) { return ( [s.navcard, pressed && { borderColor: C.accent }]} onPress={() => router.push(href as any)}> {title.toUpperCase()} {big} {unit ? {unit} : null} {flagline ? {flagline} : null} {okline ? {okline} : null} {note ? ( {note} ) : null} ); } export function Panel({ title, sub, children, }: { title?: string; sub?: string; children: React.ReactNode; }) { return ( {title ? ( {title} {sub ? {sub} : null} ) : null} {children} ); } export function PriorityCard({ p, rank }: { p: Priority; rank: number }) { const color = p.severity === 'flag' ? C.red : C.yellow; const border = p.severity === 'flag' ? 'rgba(255,69,58,0.35)' : 'rgba(245,197,66,0.35)'; return ( #{rank} · {String(p.severity).toUpperCase()} {p.title} {p.detail} ); } export function SearchInput({ value, onChange, placeholder, }: { value: string; onChange: (v: string) => void; placeholder: string; }) { return ( ); } export function SrcNote({ children }: { children: React.ReactNode }) { return {children}; } export function LegendRow({ items }: { items: [string, string][] }) { return ( {items.map(([label, color]) => ( {label} ))} ); } const s = StyleSheet.create({ errBox: { borderWidth: 1, borderColor: 'rgba(255,69,58,0.3)', backgroundColor: C.redDim, borderRadius: 12, padding: 14, marginVertical: 12, }, sec: { fontSize: 11, fontWeight: '600', letterSpacing: 0.8, color: C.muted, marginTop: 26, marginBottom: 10, }, tiles: { flexDirection: 'row', flexWrap: 'wrap', gap: 10 }, tile: { backgroundColor: C.surface, borderWidth: 1, borderColor: C.border, borderRadius: 14, padding: 14, flexBasis: '47%', flexGrow: 1, }, tileLbl: { fontSize: 11, color: C.muted, letterSpacing: 0.5, marginBottom: 6 }, tileVal: { fontSize: 24, fontWeight: '300', letterSpacing: -0.6, color: C.text }, tileUnit: { fontSize: 12, color: C.muted, fontWeight: '400', letterSpacing: 0 }, tileSub: { fontSize: 11.5, color: C.muted, marginTop: 5 }, navcard: { backgroundColor: C.surface, borderWidth: 1, borderColor: C.border, borderRadius: 14, padding: 14, flexBasis: '47%', flexGrow: 1, }, navHead: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8, }, navT: { fontSize: 12, fontWeight: '600', color: C.muted, letterSpacing: 0.6 }, navBig: { fontSize: 22, fontWeight: '300', letterSpacing: -0.5, color: C.text }, navNote: { fontSize: 11.5, color: C.muted, marginTop: 4 }, panel: { backgroundColor: C.surface, borderWidth: 1, borderColor: C.border, borderRadius: 14, padding: 14, marginBottom: 10, }, panelHead: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 10, }, priority: { borderWidth: 1, borderRadius: 12, paddingVertical: 13, paddingHorizontal: 14, backgroundColor: C.surface, marginBottom: 8, }, search: { paddingVertical: 10, paddingHorizontal: 14, borderRadius: 12, borderWidth: 1, borderColor: C.border, backgroundColor: C.surface, color: C.text, fontSize: 14, marginTop: 4, marginBottom: 12, }, srcNote: { fontSize: 11, color: C.dim, marginTop: 12, lineHeight: 16 }, });