@nextsparkjs/mobile 0.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +339 -0
  2. package/dist/api/client.d.ts +102 -0
  3. package/dist/api/client.js +189 -0
  4. package/dist/api/client.js.map +1 -0
  5. package/dist/api/client.types.d.ts +39 -0
  6. package/dist/api/client.types.js +12 -0
  7. package/dist/api/client.types.js.map +1 -0
  8. package/dist/api/core/auth.d.ts +26 -0
  9. package/dist/api/core/auth.js +52 -0
  10. package/dist/api/core/auth.js.map +1 -0
  11. package/dist/api/core/index.d.ts +4 -0
  12. package/dist/api/core/index.js +5 -0
  13. package/dist/api/core/index.js.map +1 -0
  14. package/dist/api/core/teams.d.ts +20 -0
  15. package/dist/api/core/teams.js +19 -0
  16. package/dist/api/core/teams.js.map +1 -0
  17. package/dist/api/core/types.d.ts +58 -0
  18. package/dist/api/core/types.js +1 -0
  19. package/dist/api/core/types.js.map +1 -0
  20. package/dist/api/core/users.d.ts +43 -0
  21. package/dist/api/core/users.js +41 -0
  22. package/dist/api/core/users.js.map +1 -0
  23. package/dist/api/entities/factory.d.ts +43 -0
  24. package/dist/api/entities/factory.js +31 -0
  25. package/dist/api/entities/factory.js.map +1 -0
  26. package/dist/api/entities/index.d.ts +3 -0
  27. package/dist/api/entities/index.js +3 -0
  28. package/dist/api/entities/index.js.map +1 -0
  29. package/dist/api/entities/types.d.ts +32 -0
  30. package/dist/api/entities/types.js +1 -0
  31. package/dist/api/entities/types.js.map +1 -0
  32. package/dist/api/index.d.ts +7 -0
  33. package/dist/api/index.js +15 -0
  34. package/dist/api/index.js.map +1 -0
  35. package/dist/hooks/index.d.ts +4 -0
  36. package/dist/hooks/index.js +5 -0
  37. package/dist/hooks/index.js.map +1 -0
  38. package/dist/index.d.ts +14 -0
  39. package/dist/index.js +28 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/lib/alert.d.ts +34 -0
  42. package/dist/lib/alert.js +73 -0
  43. package/dist/lib/alert.js.map +1 -0
  44. package/dist/lib/index.d.ts +2 -0
  45. package/dist/lib/index.js +10 -0
  46. package/dist/lib/index.js.map +1 -0
  47. package/dist/lib/storage.d.ts +1 -0
  48. package/dist/lib/storage.js +29 -0
  49. package/dist/lib/storage.js.map +1 -0
  50. package/dist/providers/AuthProvider.d.ts +21 -0
  51. package/dist/providers/AuthProvider.js +113 -0
  52. package/dist/providers/AuthProvider.js.map +1 -0
  53. package/dist/providers/QueryProvider.d.ts +11 -0
  54. package/dist/providers/QueryProvider.js +23 -0
  55. package/dist/providers/QueryProvider.js.map +1 -0
  56. package/dist/providers/index.d.ts +6 -0
  57. package/dist/providers/index.js +9 -0
  58. package/dist/providers/index.js.map +1 -0
  59. package/dist/storage-BaRppHUz.d.ts +22 -0
  60. package/package.json +99 -0
  61. package/templates/app/(app)/_layout.tsx +216 -0
  62. package/templates/app/(app)/customer/[id].tsx +68 -0
  63. package/templates/app/(app)/customer/create.tsx +24 -0
  64. package/templates/app/(app)/customers.tsx +164 -0
  65. package/templates/app/(app)/index.tsx +310 -0
  66. package/templates/app/(app)/notifications.tsx +242 -0
  67. package/templates/app/(app)/profile.tsx +254 -0
  68. package/templates/app/(app)/settings.tsx +241 -0
  69. package/templates/app/(app)/task/[id].tsx +70 -0
  70. package/templates/app/(app)/task/create.tsx +24 -0
  71. package/templates/app/(app)/tasks.tsx +164 -0
  72. package/templates/app/_layout.tsx +54 -0
  73. package/templates/app/index.tsx +35 -0
  74. package/templates/app/login.tsx +179 -0
  75. package/templates/app.config.ts +39 -0
  76. package/templates/babel.config.js +9 -0
  77. package/templates/eas.json +18 -0
  78. package/templates/jest.config.js +12 -0
  79. package/templates/metro.config.js +23 -0
  80. package/templates/package.json.template +52 -0
  81. package/templates/src/components/entities/customers/CustomerCard.tsx +59 -0
  82. package/templates/src/components/entities/customers/CustomerForm.tsx +194 -0
  83. package/templates/src/components/entities/customers/index.ts +6 -0
  84. package/templates/src/components/entities/index.ts +9 -0
  85. package/templates/src/components/entities/tasks/TaskCard.tsx +89 -0
  86. package/templates/src/components/entities/tasks/TaskForm.tsx +231 -0
  87. package/templates/src/components/entities/tasks/index.ts +6 -0
  88. package/templates/src/components/features/index.ts +6 -0
  89. package/templates/src/components/navigation/BottomTabBar.tsx +80 -0
  90. package/templates/src/components/navigation/CreateSheet.tsx +108 -0
  91. package/templates/src/components/navigation/MoreSheet.tsx +403 -0
  92. package/templates/src/components/navigation/TopBar.tsx +74 -0
  93. package/templates/src/components/navigation/index.ts +8 -0
  94. package/templates/src/components/ui/index.ts +89 -0
  95. package/templates/src/components/ui/text.tsx +64 -0
  96. package/templates/src/config/api.config.ts +26 -0
  97. package/templates/src/config/app.config.ts +15 -0
  98. package/templates/src/config/hooks.ts +58 -0
  99. package/templates/src/config/permissions.config.ts +119 -0
  100. package/templates/src/constants/colors.ts +55 -0
  101. package/templates/src/data/notifications.mock.json +100 -0
  102. package/templates/src/entities/customers/api.ts +10 -0
  103. package/templates/src/entities/customers/constants.internal.ts +6 -0
  104. package/templates/src/entities/customers/constants.ts +14 -0
  105. package/templates/src/entities/customers/index.ts +9 -0
  106. package/templates/src/entities/customers/mutations.ts +58 -0
  107. package/templates/src/entities/customers/queries.ts +40 -0
  108. package/templates/src/entities/customers/types.ts +43 -0
  109. package/templates/src/entities/index.ts +8 -0
  110. package/templates/src/entities/tasks/api.ts +10 -0
  111. package/templates/src/entities/tasks/constants.internal.ts +6 -0
  112. package/templates/src/entities/tasks/constants.ts +39 -0
  113. package/templates/src/entities/tasks/index.ts +9 -0
  114. package/templates/src/entities/tasks/mutations.ts +108 -0
  115. package/templates/src/entities/tasks/queries.ts +42 -0
  116. package/templates/src/entities/tasks/types.ts +52 -0
  117. package/templates/src/hooks/useCustomers.ts +17 -0
  118. package/templates/src/hooks/useTasks.ts +18 -0
  119. package/templates/src/lib/utils.ts +10 -0
  120. package/templates/src/styles/globals.css +103 -0
  121. package/templates/src/types/index.ts +45 -0
  122. package/templates/tailwind.config.js +108 -0
  123. package/templates/tsconfig.json +15 -0
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Edit Customer Screen
3
+ */
4
+
5
+ import { useLocalSearchParams, router } from 'expo-router'
6
+ import { View, Text, ActivityIndicator, StyleSheet } from 'react-native'
7
+ import { CustomerForm } from '@/src/components/entities/customers'
8
+ import { useCustomer, useUpdateCustomer, useDeleteCustomer, type UpdateCustomerInput } from '@/src/entities/customers'
9
+
10
+ export default function EditCustomerScreen() {
11
+ const { id } = useLocalSearchParams<{ id: string }>()
12
+ const { data, isLoading, error } = useCustomer(id)
13
+ const updateCustomer = useUpdateCustomer()
14
+ const deleteCustomer = useDeleteCustomer()
15
+
16
+ const handleSubmit = async (formData: UpdateCustomerInput) => {
17
+ if (!id) return
18
+ await updateCustomer.mutateAsync({ id, data: formData })
19
+ router.back()
20
+ }
21
+
22
+ const handleDelete = async () => {
23
+ if (!id) return
24
+ await deleteCustomer.mutateAsync(id)
25
+ router.replace('/(app)/customers')
26
+ }
27
+
28
+ if (isLoading) {
29
+ return (
30
+ <View style={styles.centered}>
31
+ <ActivityIndicator size="large" color="#10B981" />
32
+ </View>
33
+ )
34
+ }
35
+
36
+ if (error || !data?.data) {
37
+ return (
38
+ <View style={styles.centered}>
39
+ <Text style={styles.errorText}>
40
+ {error instanceof Error ? error.message : 'Customer not found'}
41
+ </Text>
42
+ </View>
43
+ )
44
+ }
45
+
46
+ return (
47
+ <CustomerForm
48
+ mode="edit"
49
+ initialData={data.data}
50
+ onSubmit={handleSubmit}
51
+ onDelete={handleDelete}
52
+ isLoading={updateCustomer.isPending || deleteCustomer.isPending}
53
+ />
54
+ )
55
+ }
56
+
57
+ const styles = StyleSheet.create({
58
+ centered: {
59
+ flex: 1,
60
+ justifyContent: 'center',
61
+ alignItems: 'center',
62
+ backgroundColor: '#F9FAFB',
63
+ },
64
+ errorText: {
65
+ color: '#DC2626',
66
+ fontSize: 16,
67
+ },
68
+ })
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Create Customer Screen
3
+ */
4
+
5
+ import { router } from 'expo-router'
6
+ import { CustomerForm } from '@/src/components/entities/customers'
7
+ import { useCreateCustomer, type CreateCustomerInput } from '@/src/entities/customers'
8
+
9
+ export default function CreateCustomerScreen() {
10
+ const createCustomer = useCreateCustomer()
11
+
12
+ const handleSubmit = async (data: CreateCustomerInput) => {
13
+ await createCustomer.mutateAsync(data)
14
+ router.back()
15
+ }
16
+
17
+ return (
18
+ <CustomerForm
19
+ mode="create"
20
+ onSubmit={handleSubmit}
21
+ isLoading={createCustomer.isPending}
22
+ />
23
+ )
24
+ }
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Customers List Screen
3
+ */
4
+
5
+ import { useCallback, useState } from 'react'
6
+ import {
7
+ View,
8
+ Text,
9
+ FlatList,
10
+ RefreshControl,
11
+ StyleSheet,
12
+ } from 'react-native'
13
+ import { router } from 'expo-router'
14
+ import { useCustomers, type Customer } from '@/src/entities/customers'
15
+ import { CustomerCard } from '@/src/components/entities/customers'
16
+ import { Button } from '@/src/components/ui'
17
+ import { Colors } from '@/src/constants/colors'
18
+
19
+ export default function CustomersListScreen() {
20
+ const { data, isLoading, error, refetch } = useCustomers()
21
+ const [refreshing, setRefreshing] = useState(false)
22
+
23
+ const onRefresh = useCallback(async () => {
24
+ setRefreshing(true)
25
+ try {
26
+ await refetch()
27
+ } finally {
28
+ setRefreshing(false)
29
+ }
30
+ }, [refetch])
31
+
32
+ const handleCustomerPress = (customer: Customer) => {
33
+ router.push(`/(app)/customer/${customer.id}`)
34
+ }
35
+
36
+ const renderCustomer = ({ item }: { item: Customer }) => (
37
+ <CustomerCard customer={item} onPress={() => handleCustomerPress(item)} />
38
+ )
39
+
40
+ const renderEmpty = () => {
41
+ if (isLoading) return null
42
+
43
+ return (
44
+ <View style={styles.emptyContainer}>
45
+ <Text style={styles.emptyIcon}>👥</Text>
46
+ <Text style={styles.emptyTitle}>Sin clientes</Text>
47
+ <Text style={styles.emptyText}>
48
+ Toca el botón Crear para agregar tu primer cliente
49
+ </Text>
50
+ </View>
51
+ )
52
+ }
53
+
54
+ const renderError = () => (
55
+ <View style={styles.errorContainer}>
56
+ <Text style={styles.errorTitle}>Algo salió mal</Text>
57
+ <Text style={styles.errorText}>
58
+ {error instanceof Error ? error.message : 'Error al cargar clientes'}
59
+ </Text>
60
+ <Button onPress={() => refetch()}>
61
+ Reintentar
62
+ </Button>
63
+ </View>
64
+ )
65
+
66
+ return (
67
+ <View style={styles.container}>
68
+ {/* Page Header */}
69
+ <View style={styles.header}>
70
+ <Text style={styles.pageTitle}>Clientes</Text>
71
+ <Text style={styles.pageSubtitle}>
72
+ Gestiona tu cartera de clientes
73
+ </Text>
74
+ </View>
75
+
76
+ {/* Error State */}
77
+ {error && renderError()}
78
+
79
+ {/* Customers List */}
80
+ {!error && (
81
+ <FlatList
82
+ data={data?.data || []}
83
+ renderItem={renderCustomer}
84
+ keyExtractor={(item) => item.id}
85
+ contentContainerStyle={styles.listContent}
86
+ refreshControl={
87
+ <RefreshControl
88
+ refreshing={refreshing}
89
+ onRefresh={onRefresh}
90
+ tintColor={Colors.primary}
91
+ />
92
+ }
93
+ ListEmptyComponent={renderEmpty}
94
+ showsVerticalScrollIndicator={false}
95
+ />
96
+ )}
97
+ </View>
98
+ )
99
+ }
100
+
101
+ const styles = StyleSheet.create({
102
+ container: {
103
+ flex: 1,
104
+ backgroundColor: Colors.backgroundSecondary,
105
+ },
106
+ header: {
107
+ padding: 20,
108
+ paddingBottom: 12,
109
+ },
110
+ pageTitle: {
111
+ fontSize: 28,
112
+ fontWeight: '700',
113
+ color: Colors.foreground,
114
+ marginBottom: 4,
115
+ },
116
+ pageSubtitle: {
117
+ fontSize: 15,
118
+ color: Colors.foregroundSecondary,
119
+ },
120
+ listContent: {
121
+ paddingBottom: 12,
122
+ flexGrow: 1,
123
+ },
124
+ emptyContainer: {
125
+ flex: 1,
126
+ justifyContent: 'center',
127
+ alignItems: 'center',
128
+ paddingHorizontal: 32,
129
+ },
130
+ emptyIcon: {
131
+ fontSize: 48,
132
+ marginBottom: 16,
133
+ opacity: 0.5,
134
+ },
135
+ emptyTitle: {
136
+ fontSize: 18,
137
+ fontWeight: '600',
138
+ color: Colors.foreground,
139
+ marginBottom: 8,
140
+ },
141
+ emptyText: {
142
+ fontSize: 14,
143
+ color: Colors.foregroundSecondary,
144
+ textAlign: 'center',
145
+ },
146
+ errorContainer: {
147
+ flex: 1,
148
+ justifyContent: 'center',
149
+ alignItems: 'center',
150
+ paddingHorizontal: 32,
151
+ },
152
+ errorTitle: {
153
+ fontSize: 18,
154
+ fontWeight: '600',
155
+ color: Colors.destructive,
156
+ marginBottom: 8,
157
+ },
158
+ errorText: {
159
+ fontSize: 14,
160
+ color: Colors.foregroundSecondary,
161
+ textAlign: 'center',
162
+ marginBottom: 16,
163
+ },
164
+ })
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Home/Dashboard Screen
3
+ */
4
+
5
+ import { useState } from 'react'
6
+ import { View, Text, TouchableOpacity, StyleSheet, ScrollView } from 'react-native'
7
+ import { router } from 'expo-router'
8
+ import { useAuth } from '@nextsparkjs/mobile'
9
+ import { useTasks } from '@/src/entities/tasks'
10
+ import { useCustomers } from '@/src/entities/customers'
11
+ import { Colors } from '@/src/constants/colors'
12
+ import { Button } from '@/src/components/ui'
13
+ import {
14
+ Progress,
15
+ Tabs,
16
+ TabsList,
17
+ TabsTrigger,
18
+ TabsContent,
19
+ Accordion,
20
+ AccordionItem,
21
+ AccordionTrigger,
22
+ AccordionContent,
23
+ Slider,
24
+ } from '@nextsparkjs/ui'
25
+
26
+ export default function HomeScreen() {
27
+ const { user, team } = useAuth()
28
+ const { data: tasksData } = useTasks()
29
+ const { data: customersData } = useCustomers()
30
+ const [activeTab, setActiveTab] = useState('tab1')
31
+ const [sliderValue, setSliderValue] = useState([50])
32
+
33
+ const firstName = user?.name?.split(' ')[0] || 'Usuario'
34
+ const taskCount = tasksData?.data?.length || 0
35
+ const customerCount = customersData?.data?.length || 0
36
+
37
+ // Count tasks by status
38
+ const todoTasks = tasksData?.data?.filter((t) => t.status === 'todo').length || 0
39
+ const inProgressTasks =
40
+ tasksData?.data?.filter((t) => t.status === 'in-progress').length || 0
41
+
42
+ return (
43
+ <ScrollView style={styles.container}>
44
+ {/* Welcome Header */}
45
+ <View style={styles.header}>
46
+ <Text style={styles.pageTitle}>Dashboard</Text>
47
+ <Text style={styles.welcomeText}>Bienvenido de vuelta, {firstName}</Text>
48
+ </View>
49
+
50
+ {/* Stats Cards */}
51
+ <View style={styles.cardsContainer}>
52
+ {/* Account Status Card */}
53
+ <View style={styles.card}>
54
+ <View style={styles.cardHeader}>
55
+ <Text style={styles.cardLabel}>Estado de Cuenta</Text>
56
+ <Text style={styles.cardIcon}>📈</Text>
57
+ </View>
58
+ <Text style={styles.cardValue}>Activo</Text>
59
+ <View style={styles.badge}>
60
+ <Text style={styles.badgeText}>verificado</Text>
61
+ </View>
62
+ </View>
63
+
64
+ {/* Plan Card */}
65
+ <View style={styles.card}>
66
+ <View style={styles.cardHeader}>
67
+ <Text style={styles.cardLabel}>Plan</Text>
68
+ <Text style={styles.cardIcon}>💳</Text>
69
+ </View>
70
+ <Text style={styles.cardValue}>Gratis</Text>
71
+ <Button variant="link" size="sm" style={{ alignSelf: 'flex-start', height: 'auto', paddingHorizontal: 0 }}>
72
+ Actualizar plan →
73
+ </Button>
74
+ </View>
75
+
76
+ {/* Tasks Summary Card */}
77
+ <TouchableOpacity
78
+ style={styles.card}
79
+ onPress={() => router.push('/(app)/tasks')}
80
+ activeOpacity={0.7}
81
+ >
82
+ <View style={styles.cardHeader}>
83
+ <Text style={styles.cardLabel}>Mis Tareas</Text>
84
+ <Text style={styles.cardIcon}>☑</Text>
85
+ </View>
86
+ <Text style={styles.cardValue}>{taskCount}</Text>
87
+ <View style={styles.statsRow}>
88
+ <Text style={styles.statItem}>
89
+ <Text style={styles.statNumber}>{todoTasks}</Text> pendientes
90
+ </Text>
91
+ <Text style={styles.statItem}>
92
+ <Text style={styles.statNumber}>{inProgressTasks}</Text> en progreso
93
+ </Text>
94
+ </View>
95
+ </TouchableOpacity>
96
+
97
+ {/* Customers Card */}
98
+ <TouchableOpacity
99
+ style={styles.card}
100
+ onPress={() => router.push('/(app)/customers')}
101
+ activeOpacity={0.7}
102
+ >
103
+ <View style={styles.cardHeader}>
104
+ <Text style={styles.cardLabel}>Clientes</Text>
105
+ <Text style={styles.cardIcon}>👥</Text>
106
+ </View>
107
+ <Text style={styles.cardValue}>{customerCount}</Text>
108
+ <Text style={styles.cardLink}>Ver todos →</Text>
109
+ </TouchableOpacity>
110
+
111
+ {/* Team Card */}
112
+ {team && (
113
+ <View style={styles.card}>
114
+ <View style={styles.cardHeader}>
115
+ <Text style={styles.cardLabel}>Equipo Actual</Text>
116
+ <Text style={styles.cardIcon}>🏢</Text>
117
+ </View>
118
+ <Text style={styles.cardValue}>{team.name}</Text>
119
+ <View style={styles.badge}>
120
+ <Text style={styles.badgeText}>{team.role}</Text>
121
+ </View>
122
+ </View>
123
+ )}
124
+ </View>
125
+
126
+ {/* === TEST COMPONENTS - Phase 3 Migration === */}
127
+ <View style={styles.testSection}>
128
+ <Text style={styles.testTitle}>Component Test (Phase 3)</Text>
129
+
130
+ {/* Progress Test */}
131
+ <View style={styles.testCard}>
132
+ <Text style={styles.testLabel}>Progress Component</Text>
133
+ <Progress value={30} style={{ marginBottom: 8 }} />
134
+ <Progress value={60} style={{ marginBottom: 8 }} />
135
+ <Progress value={90} />
136
+ </View>
137
+
138
+ {/* Tabs Test */}
139
+ <View style={styles.testCard}>
140
+ <Text style={styles.testLabel}>Tabs Component</Text>
141
+ <Tabs value={activeTab} onValueChange={setActiveTab}>
142
+ <TabsList>
143
+ <TabsTrigger value="tab1">Tab 1</TabsTrigger>
144
+ <TabsTrigger value="tab2">Tab 2</TabsTrigger>
145
+ <TabsTrigger value="tab3">Tab 3</TabsTrigger>
146
+ </TabsList>
147
+ <TabsContent value="tab1">
148
+ <Text style={styles.testText}>Content for Tab 1</Text>
149
+ </TabsContent>
150
+ <TabsContent value="tab2">
151
+ <Text style={styles.testText}>Content for Tab 2</Text>
152
+ </TabsContent>
153
+ <TabsContent value="tab3">
154
+ <Text style={styles.testText}>Content for Tab 3</Text>
155
+ </TabsContent>
156
+ </Tabs>
157
+ </View>
158
+
159
+ {/* Accordion Test */}
160
+ <View style={styles.testCard}>
161
+ <Text style={styles.testLabel}>Accordion Component</Text>
162
+ <Accordion type="single" collapsible>
163
+ <AccordionItem value="item1">
164
+ <AccordionTrigger>Section 1</AccordionTrigger>
165
+ <AccordionContent>
166
+ <Text style={styles.testText}>This is the content for section 1.</Text>
167
+ </AccordionContent>
168
+ </AccordionItem>
169
+ <AccordionItem value="item2">
170
+ <AccordionTrigger>Section 2</AccordionTrigger>
171
+ <AccordionContent>
172
+ <Text style={styles.testText}>This is the content for section 2.</Text>
173
+ </AccordionContent>
174
+ </AccordionItem>
175
+ </Accordion>
176
+ </View>
177
+
178
+ {/* Slider Test */}
179
+ <View style={styles.testCard}>
180
+ <Text style={styles.testLabel}>Slider Component</Text>
181
+ <Text style={styles.testText}>Value: {sliderValue[0]}</Text>
182
+ <Slider
183
+ value={sliderValue}
184
+ onValueChange={setSliderValue}
185
+ min={0}
186
+ max={100}
187
+ step={1}
188
+ />
189
+ </View>
190
+ </View>
191
+ {/* === END TEST COMPONENTS === */}
192
+
193
+ <View style={styles.spacer} />
194
+ </ScrollView>
195
+ )
196
+ }
197
+
198
+ const styles = StyleSheet.create({
199
+ container: {
200
+ flex: 1,
201
+ backgroundColor: Colors.backgroundSecondary,
202
+ },
203
+ header: {
204
+ padding: 20,
205
+ paddingBottom: 12,
206
+ },
207
+ pageTitle: {
208
+ fontSize: 28,
209
+ fontWeight: '700',
210
+ color: Colors.foreground,
211
+ marginBottom: 4,
212
+ },
213
+ welcomeText: {
214
+ fontSize: 15,
215
+ color: Colors.foregroundSecondary,
216
+ },
217
+ cardsContainer: {
218
+ paddingHorizontal: 16,
219
+ gap: 12,
220
+ },
221
+ card: {
222
+ backgroundColor: Colors.card,
223
+ borderRadius: 12,
224
+ padding: 20,
225
+ borderWidth: 1,
226
+ borderColor: Colors.border,
227
+ },
228
+ cardHeader: {
229
+ flexDirection: 'row',
230
+ justifyContent: 'space-between',
231
+ alignItems: 'center',
232
+ marginBottom: 12,
233
+ },
234
+ cardLabel: {
235
+ fontSize: 14,
236
+ color: Colors.foregroundSecondary,
237
+ fontWeight: '500',
238
+ },
239
+ cardIcon: {
240
+ fontSize: 18,
241
+ },
242
+ cardValue: {
243
+ fontSize: 24,
244
+ fontWeight: '700',
245
+ color: Colors.foreground,
246
+ marginBottom: 8,
247
+ },
248
+ badge: {
249
+ alignSelf: 'flex-start',
250
+ backgroundColor: Colors.backgroundTertiary,
251
+ paddingHorizontal: 10,
252
+ paddingVertical: 4,
253
+ borderRadius: 6,
254
+ },
255
+ badgeText: {
256
+ fontSize: 12,
257
+ color: Colors.foregroundSecondary,
258
+ fontWeight: '500',
259
+ },
260
+ cardLink: {
261
+ fontSize: 14,
262
+ color: Colors.foreground,
263
+ fontWeight: '500',
264
+ },
265
+ statsRow: {
266
+ flexDirection: 'row',
267
+ gap: 16,
268
+ },
269
+ statItem: {
270
+ fontSize: 13,
271
+ color: Colors.foregroundSecondary,
272
+ },
273
+ statNumber: {
274
+ fontWeight: '600',
275
+ color: Colors.foreground,
276
+ },
277
+ spacer: {
278
+ height: 40,
279
+ },
280
+ // Test section styles
281
+ testSection: {
282
+ padding: 16,
283
+ marginTop: 16,
284
+ },
285
+ testTitle: {
286
+ fontSize: 20,
287
+ fontWeight: '700',
288
+ color: Colors.foreground,
289
+ marginBottom: 16,
290
+ },
291
+ testCard: {
292
+ backgroundColor: Colors.card,
293
+ borderRadius: 12,
294
+ padding: 16,
295
+ marginBottom: 12,
296
+ borderWidth: 1,
297
+ borderColor: Colors.border,
298
+ },
299
+ testLabel: {
300
+ fontSize: 14,
301
+ fontWeight: '600',
302
+ color: Colors.foregroundSecondary,
303
+ marginBottom: 12,
304
+ },
305
+ testText: {
306
+ fontSize: 14,
307
+ color: Colors.foreground,
308
+ paddingVertical: 8,
309
+ },
310
+ })