@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.
- package/README.md +339 -0
- package/dist/api/client.d.ts +102 -0
- package/dist/api/client.js +189 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/client.types.d.ts +39 -0
- package/dist/api/client.types.js +12 -0
- package/dist/api/client.types.js.map +1 -0
- package/dist/api/core/auth.d.ts +26 -0
- package/dist/api/core/auth.js +52 -0
- package/dist/api/core/auth.js.map +1 -0
- package/dist/api/core/index.d.ts +4 -0
- package/dist/api/core/index.js +5 -0
- package/dist/api/core/index.js.map +1 -0
- package/dist/api/core/teams.d.ts +20 -0
- package/dist/api/core/teams.js +19 -0
- package/dist/api/core/teams.js.map +1 -0
- package/dist/api/core/types.d.ts +58 -0
- package/dist/api/core/types.js +1 -0
- package/dist/api/core/types.js.map +1 -0
- package/dist/api/core/users.d.ts +43 -0
- package/dist/api/core/users.js +41 -0
- package/dist/api/core/users.js.map +1 -0
- package/dist/api/entities/factory.d.ts +43 -0
- package/dist/api/entities/factory.js +31 -0
- package/dist/api/entities/factory.js.map +1 -0
- package/dist/api/entities/index.d.ts +3 -0
- package/dist/api/entities/index.js +3 -0
- package/dist/api/entities/index.js.map +1 -0
- package/dist/api/entities/types.d.ts +32 -0
- package/dist/api/entities/types.js +1 -0
- package/dist/api/entities/types.js.map +1 -0
- package/dist/api/index.d.ts +7 -0
- package/dist/api/index.js +15 -0
- package/dist/api/index.js.map +1 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.js +5 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/alert.d.ts +34 -0
- package/dist/lib/alert.js +73 -0
- package/dist/lib/alert.js.map +1 -0
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.js +10 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/storage.d.ts +1 -0
- package/dist/lib/storage.js +29 -0
- package/dist/lib/storage.js.map +1 -0
- package/dist/providers/AuthProvider.d.ts +21 -0
- package/dist/providers/AuthProvider.js +113 -0
- package/dist/providers/AuthProvider.js.map +1 -0
- package/dist/providers/QueryProvider.d.ts +11 -0
- package/dist/providers/QueryProvider.js +23 -0
- package/dist/providers/QueryProvider.js.map +1 -0
- package/dist/providers/index.d.ts +6 -0
- package/dist/providers/index.js +9 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/storage-BaRppHUz.d.ts +22 -0
- package/package.json +99 -0
- package/templates/app/(app)/_layout.tsx +216 -0
- package/templates/app/(app)/customer/[id].tsx +68 -0
- package/templates/app/(app)/customer/create.tsx +24 -0
- package/templates/app/(app)/customers.tsx +164 -0
- package/templates/app/(app)/index.tsx +310 -0
- package/templates/app/(app)/notifications.tsx +242 -0
- package/templates/app/(app)/profile.tsx +254 -0
- package/templates/app/(app)/settings.tsx +241 -0
- package/templates/app/(app)/task/[id].tsx +70 -0
- package/templates/app/(app)/task/create.tsx +24 -0
- package/templates/app/(app)/tasks.tsx +164 -0
- package/templates/app/_layout.tsx +54 -0
- package/templates/app/index.tsx +35 -0
- package/templates/app/login.tsx +179 -0
- package/templates/app.config.ts +39 -0
- package/templates/babel.config.js +9 -0
- package/templates/eas.json +18 -0
- package/templates/jest.config.js +12 -0
- package/templates/metro.config.js +23 -0
- package/templates/package.json.template +52 -0
- package/templates/src/components/entities/customers/CustomerCard.tsx +59 -0
- package/templates/src/components/entities/customers/CustomerForm.tsx +194 -0
- package/templates/src/components/entities/customers/index.ts +6 -0
- package/templates/src/components/entities/index.ts +9 -0
- package/templates/src/components/entities/tasks/TaskCard.tsx +89 -0
- package/templates/src/components/entities/tasks/TaskForm.tsx +231 -0
- package/templates/src/components/entities/tasks/index.ts +6 -0
- package/templates/src/components/features/index.ts +6 -0
- package/templates/src/components/navigation/BottomTabBar.tsx +80 -0
- package/templates/src/components/navigation/CreateSheet.tsx +108 -0
- package/templates/src/components/navigation/MoreSheet.tsx +403 -0
- package/templates/src/components/navigation/TopBar.tsx +74 -0
- package/templates/src/components/navigation/index.ts +8 -0
- package/templates/src/components/ui/index.ts +89 -0
- package/templates/src/components/ui/text.tsx +64 -0
- package/templates/src/config/api.config.ts +26 -0
- package/templates/src/config/app.config.ts +15 -0
- package/templates/src/config/hooks.ts +58 -0
- package/templates/src/config/permissions.config.ts +119 -0
- package/templates/src/constants/colors.ts +55 -0
- package/templates/src/data/notifications.mock.json +100 -0
- package/templates/src/entities/customers/api.ts +10 -0
- package/templates/src/entities/customers/constants.internal.ts +6 -0
- package/templates/src/entities/customers/constants.ts +14 -0
- package/templates/src/entities/customers/index.ts +9 -0
- package/templates/src/entities/customers/mutations.ts +58 -0
- package/templates/src/entities/customers/queries.ts +40 -0
- package/templates/src/entities/customers/types.ts +43 -0
- package/templates/src/entities/index.ts +8 -0
- package/templates/src/entities/tasks/api.ts +10 -0
- package/templates/src/entities/tasks/constants.internal.ts +6 -0
- package/templates/src/entities/tasks/constants.ts +39 -0
- package/templates/src/entities/tasks/index.ts +9 -0
- package/templates/src/entities/tasks/mutations.ts +108 -0
- package/templates/src/entities/tasks/queries.ts +42 -0
- package/templates/src/entities/tasks/types.ts +52 -0
- package/templates/src/hooks/useCustomers.ts +17 -0
- package/templates/src/hooks/useTasks.ts +18 -0
- package/templates/src/lib/utils.ts +10 -0
- package/templates/src/styles/globals.css +103 -0
- package/templates/src/types/index.ts +45 -0
- package/templates/tailwind.config.js +108 -0
- package/templates/tsconfig.json +15 -0
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MoreSheet Component - "Más Opciones" bottom sheet
|
|
3
|
+
* With team switching functionality
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useState } from 'react'
|
|
7
|
+
import {
|
|
8
|
+
View,
|
|
9
|
+
Text,
|
|
10
|
+
TouchableOpacity,
|
|
11
|
+
StyleSheet,
|
|
12
|
+
Modal,
|
|
13
|
+
Pressable,
|
|
14
|
+
ScrollView,
|
|
15
|
+
} from 'react-native'
|
|
16
|
+
import { Colors } from '../../constants/colors'
|
|
17
|
+
import { useAuth } from '@nextsparkjs/mobile'
|
|
18
|
+
import { Button } from '../ui'
|
|
19
|
+
import type { Team } from '../../types'
|
|
20
|
+
|
|
21
|
+
interface MoreSheetProps {
|
|
22
|
+
visible: boolean
|
|
23
|
+
onClose: () => void
|
|
24
|
+
onNavigate: (screen: string) => void
|
|
25
|
+
onLogout: () => void
|
|
26
|
+
onTeamChange?: () => void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface MenuItem {
|
|
30
|
+
key: string
|
|
31
|
+
label: string
|
|
32
|
+
icon: string
|
|
33
|
+
screen?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const MENU_ITEMS: MenuItem[] = [
|
|
37
|
+
{ key: 'profile', label: 'Perfil', icon: '👤', screen: 'profile' },
|
|
38
|
+
{ key: 'billing', label: 'Facturación', icon: '💳', screen: 'billing' },
|
|
39
|
+
{ key: 'api-keys', label: 'Claves API', icon: '🔑', screen: 'api-keys' },
|
|
40
|
+
{ key: 'settings', label: 'Ajustes', icon: '⚙', screen: 'settings' },
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
export function MoreSheet({
|
|
44
|
+
visible,
|
|
45
|
+
onClose,
|
|
46
|
+
onNavigate,
|
|
47
|
+
onLogout,
|
|
48
|
+
onTeamChange,
|
|
49
|
+
}: MoreSheetProps) {
|
|
50
|
+
const { team, teams, selectTeam } = useAuth()
|
|
51
|
+
const [showTeamList, setShowTeamList] = useState(false)
|
|
52
|
+
|
|
53
|
+
const handleMenuItem = (item: MenuItem) => {
|
|
54
|
+
if (item.screen) {
|
|
55
|
+
onNavigate(item.screen)
|
|
56
|
+
onClose()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const handleTeamSelect = async (selectedTeam: Team) => {
|
|
61
|
+
if (selectedTeam.id !== team?.id) {
|
|
62
|
+
await selectTeam(selectedTeam)
|
|
63
|
+
setShowTeamList(false)
|
|
64
|
+
onClose()
|
|
65
|
+
// Callback to refresh data in the app
|
|
66
|
+
onTeamChange?.()
|
|
67
|
+
} else {
|
|
68
|
+
setShowTeamList(false)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const toggleTeamList = () => {
|
|
73
|
+
setShowTeamList(!showTeamList)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<Modal
|
|
78
|
+
visible={visible}
|
|
79
|
+
transparent
|
|
80
|
+
animationType="slide"
|
|
81
|
+
onRequestClose={onClose}
|
|
82
|
+
>
|
|
83
|
+
<Pressable style={styles.overlay} onPress={onClose}>
|
|
84
|
+
<Pressable style={styles.sheet} onPress={(e) => e.stopPropagation()}>
|
|
85
|
+
{/* Handle bar */}
|
|
86
|
+
<View style={styles.handleBar} />
|
|
87
|
+
|
|
88
|
+
{/* Close button */}
|
|
89
|
+
<TouchableOpacity style={styles.closeButton} onPress={onClose}>
|
|
90
|
+
<Text style={styles.closeIcon}>✕</Text>
|
|
91
|
+
</TouchableOpacity>
|
|
92
|
+
|
|
93
|
+
{/* Header */}
|
|
94
|
+
<Text style={styles.title}>Más Opciones</Text>
|
|
95
|
+
<Text style={styles.subtitle}>
|
|
96
|
+
Accede a configuraciones y funciones adicionales
|
|
97
|
+
</Text>
|
|
98
|
+
|
|
99
|
+
<ScrollView showsVerticalScrollIndicator={false}>
|
|
100
|
+
{/* Menu Items */}
|
|
101
|
+
<View style={styles.menuSection}>
|
|
102
|
+
{MENU_ITEMS.map((item) => (
|
|
103
|
+
<TouchableOpacity
|
|
104
|
+
key={item.key}
|
|
105
|
+
style={styles.menuItem}
|
|
106
|
+
onPress={() => handleMenuItem(item)}
|
|
107
|
+
activeOpacity={0.7}
|
|
108
|
+
>
|
|
109
|
+
<Text style={styles.menuIcon}>{item.icon}</Text>
|
|
110
|
+
<Text style={styles.menuLabel}>{item.label}</Text>
|
|
111
|
+
</TouchableOpacity>
|
|
112
|
+
))}
|
|
113
|
+
</View>
|
|
114
|
+
|
|
115
|
+
{/* Team Section */}
|
|
116
|
+
{team && teams.length > 0 && (
|
|
117
|
+
<>
|
|
118
|
+
<View style={styles.divider} />
|
|
119
|
+
|
|
120
|
+
{/* Current Team - Clickable to toggle list */}
|
|
121
|
+
<TouchableOpacity
|
|
122
|
+
style={styles.teamItem}
|
|
123
|
+
onPress={toggleTeamList}
|
|
124
|
+
activeOpacity={0.7}
|
|
125
|
+
>
|
|
126
|
+
<View style={styles.teamAvatar}>
|
|
127
|
+
<Text style={styles.teamAvatarText}>
|
|
128
|
+
{team.name.substring(0, 2).toUpperCase()}
|
|
129
|
+
</Text>
|
|
130
|
+
</View>
|
|
131
|
+
<View style={styles.teamInfo}>
|
|
132
|
+
<Text style={styles.teamName}>{team.name}</Text>
|
|
133
|
+
<View style={styles.teamRoleRow}>
|
|
134
|
+
<Text style={styles.teamRoleIcon}>👥</Text>
|
|
135
|
+
<Text style={styles.teamRoleText}>
|
|
136
|
+
{teams.length > 1 ? `${teams.length} equipos disponibles` : 'Equipo'}
|
|
137
|
+
</Text>
|
|
138
|
+
</View>
|
|
139
|
+
</View>
|
|
140
|
+
<Text style={[styles.chevron, showTeamList && styles.chevronOpen]}>
|
|
141
|
+
{showTeamList ? '⌄' : '⌃'}
|
|
142
|
+
</Text>
|
|
143
|
+
</TouchableOpacity>
|
|
144
|
+
|
|
145
|
+
{/* Team List (expandable) */}
|
|
146
|
+
{showTeamList && teams.length > 1 && (
|
|
147
|
+
<View style={styles.teamListContainer}>
|
|
148
|
+
<Text style={styles.teamListTitle}>Cambiar de equipo</Text>
|
|
149
|
+
{teams.map((t) => {
|
|
150
|
+
const isCurrentTeam = t.id === team.id
|
|
151
|
+
return (
|
|
152
|
+
<TouchableOpacity
|
|
153
|
+
key={t.id}
|
|
154
|
+
style={[
|
|
155
|
+
styles.teamListItem,
|
|
156
|
+
isCurrentTeam && styles.teamListItemActive,
|
|
157
|
+
]}
|
|
158
|
+
onPress={() => handleTeamSelect(t)}
|
|
159
|
+
activeOpacity={0.7}
|
|
160
|
+
>
|
|
161
|
+
<View style={[
|
|
162
|
+
styles.teamListAvatar,
|
|
163
|
+
isCurrentTeam && styles.teamListAvatarActive,
|
|
164
|
+
]}>
|
|
165
|
+
<Text style={[
|
|
166
|
+
styles.teamListAvatarText,
|
|
167
|
+
isCurrentTeam && styles.teamListAvatarTextActive,
|
|
168
|
+
]}>
|
|
169
|
+
{t.name.substring(0, 2).toUpperCase()}
|
|
170
|
+
</Text>
|
|
171
|
+
</View>
|
|
172
|
+
<View style={styles.teamListInfo}>
|
|
173
|
+
<Text style={[
|
|
174
|
+
styles.teamListName,
|
|
175
|
+
isCurrentTeam && styles.teamListNameActive,
|
|
176
|
+
]}>
|
|
177
|
+
{t.name}
|
|
178
|
+
</Text>
|
|
179
|
+
<Text style={styles.teamListRole}>
|
|
180
|
+
{t.role}
|
|
181
|
+
</Text>
|
|
182
|
+
</View>
|
|
183
|
+
{isCurrentTeam && (
|
|
184
|
+
<Text style={styles.checkmark}>✓</Text>
|
|
185
|
+
)}
|
|
186
|
+
</TouchableOpacity>
|
|
187
|
+
)
|
|
188
|
+
})}
|
|
189
|
+
</View>
|
|
190
|
+
)}
|
|
191
|
+
</>
|
|
192
|
+
)}
|
|
193
|
+
|
|
194
|
+
{/* Logout */}
|
|
195
|
+
<View style={styles.divider} />
|
|
196
|
+
<Button
|
|
197
|
+
variant="destructive"
|
|
198
|
+
onPress={onLogout}
|
|
199
|
+
>
|
|
200
|
+
Cerrar Sesión
|
|
201
|
+
</Button>
|
|
202
|
+
</ScrollView>
|
|
203
|
+
</Pressable>
|
|
204
|
+
</Pressable>
|
|
205
|
+
</Modal>
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const styles = StyleSheet.create({
|
|
210
|
+
overlay: {
|
|
211
|
+
flex: 1,
|
|
212
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
213
|
+
justifyContent: 'flex-end',
|
|
214
|
+
},
|
|
215
|
+
sheet: {
|
|
216
|
+
backgroundColor: Colors.background,
|
|
217
|
+
borderTopLeftRadius: 20,
|
|
218
|
+
borderTopRightRadius: 20,
|
|
219
|
+
paddingHorizontal: 24,
|
|
220
|
+
paddingBottom: 40,
|
|
221
|
+
paddingTop: 12,
|
|
222
|
+
maxHeight: '85%',
|
|
223
|
+
},
|
|
224
|
+
handleBar: {
|
|
225
|
+
width: 40,
|
|
226
|
+
height: 4,
|
|
227
|
+
backgroundColor: Colors.borderSecondary,
|
|
228
|
+
borderRadius: 2,
|
|
229
|
+
alignSelf: 'center',
|
|
230
|
+
marginBottom: 16,
|
|
231
|
+
},
|
|
232
|
+
closeButton: {
|
|
233
|
+
position: 'absolute',
|
|
234
|
+
top: 20,
|
|
235
|
+
right: 20,
|
|
236
|
+
width: 32,
|
|
237
|
+
height: 32,
|
|
238
|
+
borderRadius: 16,
|
|
239
|
+
borderWidth: 1,
|
|
240
|
+
borderColor: Colors.border,
|
|
241
|
+
justifyContent: 'center',
|
|
242
|
+
alignItems: 'center',
|
|
243
|
+
zIndex: 10,
|
|
244
|
+
},
|
|
245
|
+
closeIcon: {
|
|
246
|
+
fontSize: 16,
|
|
247
|
+
color: Colors.foregroundSecondary,
|
|
248
|
+
},
|
|
249
|
+
title: {
|
|
250
|
+
fontSize: 20,
|
|
251
|
+
fontWeight: '600',
|
|
252
|
+
color: Colors.foreground,
|
|
253
|
+
textAlign: 'center',
|
|
254
|
+
marginTop: 8,
|
|
255
|
+
},
|
|
256
|
+
subtitle: {
|
|
257
|
+
fontSize: 14,
|
|
258
|
+
color: Colors.foregroundSecondary,
|
|
259
|
+
textAlign: 'center',
|
|
260
|
+
marginTop: 8,
|
|
261
|
+
marginBottom: 24,
|
|
262
|
+
},
|
|
263
|
+
menuSection: {
|
|
264
|
+
gap: 4,
|
|
265
|
+
},
|
|
266
|
+
menuItem: {
|
|
267
|
+
flexDirection: 'row',
|
|
268
|
+
alignItems: 'center',
|
|
269
|
+
paddingVertical: 14,
|
|
270
|
+
gap: 16,
|
|
271
|
+
},
|
|
272
|
+
menuIcon: {
|
|
273
|
+
fontSize: 20,
|
|
274
|
+
width: 28,
|
|
275
|
+
textAlign: 'center',
|
|
276
|
+
},
|
|
277
|
+
menuLabel: {
|
|
278
|
+
fontSize: 16,
|
|
279
|
+
color: Colors.foreground,
|
|
280
|
+
fontWeight: '400',
|
|
281
|
+
},
|
|
282
|
+
divider: {
|
|
283
|
+
height: 1,
|
|
284
|
+
backgroundColor: Colors.border,
|
|
285
|
+
marginVertical: 16,
|
|
286
|
+
},
|
|
287
|
+
teamItem: {
|
|
288
|
+
flexDirection: 'row',
|
|
289
|
+
alignItems: 'center',
|
|
290
|
+
paddingVertical: 8,
|
|
291
|
+
gap: 12,
|
|
292
|
+
},
|
|
293
|
+
teamAvatar: {
|
|
294
|
+
width: 40,
|
|
295
|
+
height: 40,
|
|
296
|
+
borderRadius: 20,
|
|
297
|
+
backgroundColor: Colors.backgroundTertiary,
|
|
298
|
+
justifyContent: 'center',
|
|
299
|
+
alignItems: 'center',
|
|
300
|
+
borderWidth: 1,
|
|
301
|
+
borderColor: Colors.border,
|
|
302
|
+
},
|
|
303
|
+
teamAvatarText: {
|
|
304
|
+
fontSize: 12,
|
|
305
|
+
fontWeight: '600',
|
|
306
|
+
color: Colors.foregroundSecondary,
|
|
307
|
+
},
|
|
308
|
+
teamInfo: {
|
|
309
|
+
flex: 1,
|
|
310
|
+
},
|
|
311
|
+
teamName: {
|
|
312
|
+
fontSize: 14,
|
|
313
|
+
fontWeight: '600',
|
|
314
|
+
color: Colors.foreground,
|
|
315
|
+
},
|
|
316
|
+
teamRoleRow: {
|
|
317
|
+
flexDirection: 'row',
|
|
318
|
+
alignItems: 'center',
|
|
319
|
+
gap: 4,
|
|
320
|
+
marginTop: 2,
|
|
321
|
+
},
|
|
322
|
+
teamRoleIcon: {
|
|
323
|
+
fontSize: 12,
|
|
324
|
+
},
|
|
325
|
+
teamRoleText: {
|
|
326
|
+
fontSize: 12,
|
|
327
|
+
color: Colors.foregroundSecondary,
|
|
328
|
+
},
|
|
329
|
+
chevron: {
|
|
330
|
+
fontSize: 14,
|
|
331
|
+
color: Colors.foregroundSecondary,
|
|
332
|
+
},
|
|
333
|
+
chevronOpen: {
|
|
334
|
+
transform: [{ rotate: '180deg' }],
|
|
335
|
+
},
|
|
336
|
+
teamListContainer: {
|
|
337
|
+
marginTop: 12,
|
|
338
|
+
paddingLeft: 8,
|
|
339
|
+
},
|
|
340
|
+
teamListTitle: {
|
|
341
|
+
fontSize: 12,
|
|
342
|
+
fontWeight: '600',
|
|
343
|
+
color: Colors.foregroundSecondary,
|
|
344
|
+
textTransform: 'uppercase',
|
|
345
|
+
letterSpacing: 0.5,
|
|
346
|
+
marginBottom: 12,
|
|
347
|
+
},
|
|
348
|
+
teamListItem: {
|
|
349
|
+
flexDirection: 'row',
|
|
350
|
+
alignItems: 'center',
|
|
351
|
+
paddingVertical: 10,
|
|
352
|
+
paddingHorizontal: 12,
|
|
353
|
+
marginBottom: 4,
|
|
354
|
+
borderRadius: 8,
|
|
355
|
+
gap: 12,
|
|
356
|
+
},
|
|
357
|
+
teamListItemActive: {
|
|
358
|
+
backgroundColor: Colors.backgroundTertiary,
|
|
359
|
+
},
|
|
360
|
+
teamListAvatar: {
|
|
361
|
+
width: 36,
|
|
362
|
+
height: 36,
|
|
363
|
+
borderRadius: 18,
|
|
364
|
+
backgroundColor: Colors.backgroundSecondary,
|
|
365
|
+
justifyContent: 'center',
|
|
366
|
+
alignItems: 'center',
|
|
367
|
+
borderWidth: 1,
|
|
368
|
+
borderColor: Colors.border,
|
|
369
|
+
},
|
|
370
|
+
teamListAvatarActive: {
|
|
371
|
+
backgroundColor: Colors.primary,
|
|
372
|
+
borderColor: Colors.primary,
|
|
373
|
+
},
|
|
374
|
+
teamListAvatarText: {
|
|
375
|
+
fontSize: 11,
|
|
376
|
+
fontWeight: '600',
|
|
377
|
+
color: Colors.foregroundSecondary,
|
|
378
|
+
},
|
|
379
|
+
teamListAvatarTextActive: {
|
|
380
|
+
color: Colors.primaryForeground,
|
|
381
|
+
},
|
|
382
|
+
teamListInfo: {
|
|
383
|
+
flex: 1,
|
|
384
|
+
},
|
|
385
|
+
teamListName: {
|
|
386
|
+
fontSize: 14,
|
|
387
|
+
fontWeight: '500',
|
|
388
|
+
color: Colors.foreground,
|
|
389
|
+
},
|
|
390
|
+
teamListNameActive: {
|
|
391
|
+
fontWeight: '600',
|
|
392
|
+
},
|
|
393
|
+
teamListRole: {
|
|
394
|
+
fontSize: 12,
|
|
395
|
+
color: Colors.foregroundSecondary,
|
|
396
|
+
marginTop: 1,
|
|
397
|
+
},
|
|
398
|
+
checkmark: {
|
|
399
|
+
fontSize: 16,
|
|
400
|
+
color: Colors.success,
|
|
401
|
+
fontWeight: '600',
|
|
402
|
+
},
|
|
403
|
+
})
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TopBar Component - NextSpark Mobile Style
|
|
3
|
+
* Shows user avatar, greeting, notifications, and theme toggle
|
|
4
|
+
* Migrated to NativeWind + UI primitives + Lucide icons
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { View, Pressable } from "react-native";
|
|
8
|
+
import { router } from "expo-router";
|
|
9
|
+
import { Bell, Moon } from "lucide-react-native";
|
|
10
|
+
import { useAuth } from "@nextsparkjs/mobile";
|
|
11
|
+
import {
|
|
12
|
+
Text,
|
|
13
|
+
Avatar,
|
|
14
|
+
AvatarImage,
|
|
15
|
+
AvatarFallback,
|
|
16
|
+
getInitials,
|
|
17
|
+
Badge,
|
|
18
|
+
} from "../ui";
|
|
19
|
+
|
|
20
|
+
interface TopBarProps {
|
|
21
|
+
notificationCount?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function TopBar({ notificationCount = 0 }: TopBarProps) {
|
|
25
|
+
const { user } = useAuth();
|
|
26
|
+
|
|
27
|
+
// Get first name for greeting
|
|
28
|
+
const getFirstName = (name?: string | null) => {
|
|
29
|
+
if (!name) return "Usuario";
|
|
30
|
+
return name.split(" ")[0];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<View className="flex-row items-center justify-between border-b border-border bg-background px-4 py-3">
|
|
35
|
+
{/* Left: Avatar + Greeting */}
|
|
36
|
+
<View className="flex-row items-center gap-3">
|
|
37
|
+
<Avatar style={{ backgroundColor: "#f59e0b" }}>
|
|
38
|
+
<AvatarImage src={user?.image} />
|
|
39
|
+
<AvatarFallback style={{ backgroundColor: "#f59e0b" }}>
|
|
40
|
+
{getInitials(user?.name)}
|
|
41
|
+
</AvatarFallback>
|
|
42
|
+
</Avatar>
|
|
43
|
+
<Text weight="medium" className="text-base">
|
|
44
|
+
Hola, {getFirstName(user?.name)}
|
|
45
|
+
</Text>
|
|
46
|
+
</View>
|
|
47
|
+
|
|
48
|
+
{/* Right: Notifications + Theme Toggle */}
|
|
49
|
+
<View className="flex-row items-center gap-2">
|
|
50
|
+
{/* Notification Bell */}
|
|
51
|
+
<Pressable
|
|
52
|
+
onPress={() => router.push("/(app)/notifications")}
|
|
53
|
+
className="relative h-10 w-10 items-center justify-center"
|
|
54
|
+
>
|
|
55
|
+
<Bell size={22} color="#171717" />
|
|
56
|
+
{notificationCount > 0 && (
|
|
57
|
+
<View className="absolute right-1 top-1">
|
|
58
|
+
<Badge variant="destructive" className="min-w-[18px] px-1 py-0.5">
|
|
59
|
+
<Text className="text-[10px] font-bold text-destructive-foreground">
|
|
60
|
+
{notificationCount > 9 ? "9+" : notificationCount}
|
|
61
|
+
</Text>
|
|
62
|
+
</Badge>
|
|
63
|
+
</View>
|
|
64
|
+
)}
|
|
65
|
+
</Pressable>
|
|
66
|
+
|
|
67
|
+
{/* Theme Toggle (Moon Icon) */}
|
|
68
|
+
<Pressable className="h-10 w-10 items-center justify-center">
|
|
69
|
+
<Moon size={22} color="#171717" />
|
|
70
|
+
</Pressable>
|
|
71
|
+
</View>
|
|
72
|
+
</View>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI Components - Barrel Exports
|
|
3
|
+
* Re-export ALL from shared @nextsparkjs/ui package
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Core primitives - Mobile specific (uses NativeWind locally)
|
|
7
|
+
export { Text, type TextProps } from "./text";
|
|
8
|
+
|
|
9
|
+
// Theme system
|
|
10
|
+
export {
|
|
11
|
+
ThemeProvider,
|
|
12
|
+
useTheme,
|
|
13
|
+
defaultColors,
|
|
14
|
+
type ThemeProviderProps,
|
|
15
|
+
type ThemeColors,
|
|
16
|
+
} from "@nextsparkjs/ui";
|
|
17
|
+
|
|
18
|
+
// Re-exported from shared package for web/mobile compatibility
|
|
19
|
+
export { Button, type ButtonProps, buttonVariants } from "@nextsparkjs/ui";
|
|
20
|
+
export { Input, type InputProps } from "@nextsparkjs/ui";
|
|
21
|
+
export { Textarea, type TextareaProps } from "@nextsparkjs/ui";
|
|
22
|
+
|
|
23
|
+
// Display components
|
|
24
|
+
export {
|
|
25
|
+
Badge,
|
|
26
|
+
PressableBadge,
|
|
27
|
+
type BadgeProps,
|
|
28
|
+
type PressableBadgeProps,
|
|
29
|
+
type BadgeVariant,
|
|
30
|
+
} from "@nextsparkjs/ui";
|
|
31
|
+
export {
|
|
32
|
+
Avatar,
|
|
33
|
+
AvatarImage,
|
|
34
|
+
AvatarFallback,
|
|
35
|
+
getInitials,
|
|
36
|
+
type AvatarProps,
|
|
37
|
+
type AvatarImageProps,
|
|
38
|
+
type AvatarFallbackProps,
|
|
39
|
+
type AvatarSize,
|
|
40
|
+
} from "@nextsparkjs/ui";
|
|
41
|
+
export {
|
|
42
|
+
Card,
|
|
43
|
+
PressableCard,
|
|
44
|
+
CardHeader,
|
|
45
|
+
CardTitle,
|
|
46
|
+
CardDescription,
|
|
47
|
+
CardContent,
|
|
48
|
+
CardFooter,
|
|
49
|
+
type CardProps,
|
|
50
|
+
type PressableCardProps,
|
|
51
|
+
type CardHeaderProps,
|
|
52
|
+
type CardTitleProps,
|
|
53
|
+
type CardDescriptionProps,
|
|
54
|
+
type CardContentProps,
|
|
55
|
+
type CardFooterProps,
|
|
56
|
+
} from "@nextsparkjs/ui";
|
|
57
|
+
export { Separator, type SeparatorProps } from "@nextsparkjs/ui";
|
|
58
|
+
export {
|
|
59
|
+
Skeleton,
|
|
60
|
+
SkeletonText,
|
|
61
|
+
SkeletonTitle,
|
|
62
|
+
SkeletonAvatar,
|
|
63
|
+
SkeletonCard,
|
|
64
|
+
type SkeletonProps,
|
|
65
|
+
} from "@nextsparkjs/ui";
|
|
66
|
+
|
|
67
|
+
// Form components
|
|
68
|
+
export { Checkbox, type CheckboxProps } from "@nextsparkjs/ui";
|
|
69
|
+
export { Switch, type SwitchProps } from "@nextsparkjs/ui";
|
|
70
|
+
export { Select, type SelectProps, type SelectOption } from "@nextsparkjs/ui";
|
|
71
|
+
export { Label, type LabelProps } from "@nextsparkjs/ui";
|
|
72
|
+
|
|
73
|
+
// Overlay components
|
|
74
|
+
export {
|
|
75
|
+
Dialog,
|
|
76
|
+
DialogContent,
|
|
77
|
+
DialogHeader,
|
|
78
|
+
DialogTitle,
|
|
79
|
+
DialogDescription,
|
|
80
|
+
DialogFooter,
|
|
81
|
+
DialogClose,
|
|
82
|
+
type DialogProps,
|
|
83
|
+
type DialogContentProps,
|
|
84
|
+
type DialogHeaderProps,
|
|
85
|
+
type DialogTitleProps,
|
|
86
|
+
type DialogDescriptionProps,
|
|
87
|
+
type DialogFooterProps,
|
|
88
|
+
type DialogCloseProps,
|
|
89
|
+
} from "@nextsparkjs/ui";
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text Component - Styled text with variants
|
|
3
|
+
* Following shadcn/ui patterns for React Native
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Text as RNText, type TextProps as RNTextProps } from "react-native";
|
|
7
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
8
|
+
import { cn } from "@/src/lib/utils";
|
|
9
|
+
|
|
10
|
+
const textVariants = cva("text-foreground", {
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default: "",
|
|
14
|
+
heading: "text-2xl font-bold",
|
|
15
|
+
subheading: "text-lg font-semibold",
|
|
16
|
+
body: "text-base",
|
|
17
|
+
small: "text-sm",
|
|
18
|
+
muted: "text-muted-foreground",
|
|
19
|
+
label: "text-sm font-semibold",
|
|
20
|
+
error: "text-destructive text-sm",
|
|
21
|
+
},
|
|
22
|
+
size: {
|
|
23
|
+
xs: "text-xs",
|
|
24
|
+
sm: "text-sm",
|
|
25
|
+
base: "text-base",
|
|
26
|
+
lg: "text-lg",
|
|
27
|
+
xl: "text-xl",
|
|
28
|
+
"2xl": "text-2xl",
|
|
29
|
+
},
|
|
30
|
+
weight: {
|
|
31
|
+
normal: "font-normal",
|
|
32
|
+
medium: "font-medium",
|
|
33
|
+
semibold: "font-semibold",
|
|
34
|
+
bold: "font-bold",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
defaultVariants: {
|
|
38
|
+
variant: "default",
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export interface TextProps
|
|
43
|
+
extends RNTextProps,
|
|
44
|
+
VariantProps<typeof textVariants> {
|
|
45
|
+
className?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function Text({
|
|
49
|
+
className,
|
|
50
|
+
variant,
|
|
51
|
+
size,
|
|
52
|
+
weight,
|
|
53
|
+
children,
|
|
54
|
+
...props
|
|
55
|
+
}: TextProps) {
|
|
56
|
+
return (
|
|
57
|
+
<RNText
|
|
58
|
+
className={cn(textVariants({ variant, size, weight }), className)}
|
|
59
|
+
{...props}
|
|
60
|
+
>
|
|
61
|
+
{children}
|
|
62
|
+
</RNText>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Configuration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import Constants from 'expo-constants'
|
|
6
|
+
|
|
7
|
+
const API_URL =
|
|
8
|
+
Constants.expoConfig?.extra?.apiUrl ||
|
|
9
|
+
(Constants.expoConfig?.hostUri
|
|
10
|
+
? `http://${Constants.expoConfig.hostUri.split(':')[0]}:5173`
|
|
11
|
+
: 'http://localhost:5173')
|
|
12
|
+
|
|
13
|
+
export const API_CONFIG = {
|
|
14
|
+
baseUrl: API_URL,
|
|
15
|
+
endpoints: {
|
|
16
|
+
auth: '/api/auth',
|
|
17
|
+
tasks: '/api/v1/tasks',
|
|
18
|
+
customers: '/api/v1/customers',
|
|
19
|
+
teams: '/api/v1/teams',
|
|
20
|
+
},
|
|
21
|
+
defaults: {
|
|
22
|
+
limit: 20,
|
|
23
|
+
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
24
|
+
cacheTime: 10 * 60 * 1000, // 10 minutes
|
|
25
|
+
},
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mobile App Configuration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const APP_CONFIG = {
|
|
6
|
+
app: {
|
|
7
|
+
name: 'NextSpark Mobile',
|
|
8
|
+
version: '0.1.0',
|
|
9
|
+
},
|
|
10
|
+
features: {
|
|
11
|
+
tasks: { enabled: true },
|
|
12
|
+
customers: { enabled: true },
|
|
13
|
+
},
|
|
14
|
+
// TODO: Add more config as needed
|
|
15
|
+
}
|