@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,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission hooks for mobile
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { useMemo } from 'react'
|
|
6
|
+
import { useAuth } from '@nextsparkjs/mobile'
|
|
7
|
+
import { canDoAction, PERMISSIONS_CONFIG } from './permissions.config'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Check if current user has permission to perform an action
|
|
11
|
+
*/
|
|
12
|
+
export function usePermission(action: string): boolean {
|
|
13
|
+
const { team } = useAuth()
|
|
14
|
+
|
|
15
|
+
return useMemo(() => {
|
|
16
|
+
if (!team?.role) return false
|
|
17
|
+
return canDoAction(team.role, action)
|
|
18
|
+
}, [team?.role, action])
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Check multiple permissions at once
|
|
23
|
+
*/
|
|
24
|
+
export function usePermissions<T extends Record<string, string>>(
|
|
25
|
+
permissions: T
|
|
26
|
+
): Record<keyof T, boolean> {
|
|
27
|
+
const { team } = useAuth()
|
|
28
|
+
|
|
29
|
+
return useMemo(() => {
|
|
30
|
+
const result = {} as Record<keyof T, boolean>
|
|
31
|
+
for (const [key, action] of Object.entries(permissions)) {
|
|
32
|
+
result[key as keyof T] = team?.role ? canDoAction(team.role, action) : false
|
|
33
|
+
}
|
|
34
|
+
return result
|
|
35
|
+
}, [team?.role, permissions])
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get current team role
|
|
40
|
+
*/
|
|
41
|
+
export function useTeamRole(): string | null {
|
|
42
|
+
const { team } = useAuth()
|
|
43
|
+
return team?.role ?? null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Check if user can perform entity action
|
|
48
|
+
*/
|
|
49
|
+
export function useEntityPermission(entity: 'tasks' | 'customers', action: string): boolean {
|
|
50
|
+
const { team } = useAuth()
|
|
51
|
+
|
|
52
|
+
return useMemo(() => {
|
|
53
|
+
if (!team?.role) return false
|
|
54
|
+
const entityPerms = PERMISSIONS_CONFIG.entities[entity]
|
|
55
|
+
const perm = entityPerms.find(p => p.action === action)
|
|
56
|
+
return perm ? perm.roles.includes(team.role) : false
|
|
57
|
+
}, [team?.role, entity, action])
|
|
58
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mobile App - Permissions Configuration
|
|
3
|
+
*
|
|
4
|
+
* SINGLE SOURCE OF TRUTH for all permissions and roles.
|
|
5
|
+
* Structure matches web theme: teams, entities, features
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Types (simplified for mobile - no ThemePermissionsConfig import)
|
|
9
|
+
interface Permission {
|
|
10
|
+
action: string
|
|
11
|
+
label: string
|
|
12
|
+
description: string
|
|
13
|
+
roles: string[]
|
|
14
|
+
dangerous?: boolean
|
|
15
|
+
category?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface PermissionsConfig {
|
|
19
|
+
teams: Permission[]
|
|
20
|
+
entities: {
|
|
21
|
+
tasks: Permission[]
|
|
22
|
+
customers: Permission[]
|
|
23
|
+
}
|
|
24
|
+
features: Permission[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const PERMISSIONS_CONFIG: PermissionsConfig = {
|
|
28
|
+
// ==========================================
|
|
29
|
+
// TEAM PERMISSIONS
|
|
30
|
+
// ==========================================
|
|
31
|
+
teams: [
|
|
32
|
+
// View permissions
|
|
33
|
+
{ action: 'team.view', label: 'View Team', description: 'Can view team details', roles: ['owner', 'admin', 'member', 'viewer'] },
|
|
34
|
+
{ action: 'team.members.view', label: 'View Members', description: 'Can see team member list', roles: ['owner', 'admin', 'member', 'viewer'] },
|
|
35
|
+
{ action: 'team.settings.view', label: 'View Settings', description: 'Can view team settings', roles: ['owner', 'admin', 'member'] },
|
|
36
|
+
|
|
37
|
+
// Edit permissions
|
|
38
|
+
{ action: 'team.edit', label: 'Edit Team', description: 'Can modify team name and details', roles: ['owner', 'admin'] },
|
|
39
|
+
{ action: 'team.settings.edit', label: 'Edit Settings', description: 'Can modify team settings', roles: ['owner', 'admin'] },
|
|
40
|
+
|
|
41
|
+
// Member management
|
|
42
|
+
{ action: 'team.members.invite', label: 'Invite Members', description: 'Can invite new team members', roles: ['owner', 'admin'] },
|
|
43
|
+
{ action: 'team.members.remove', label: 'Remove Members', description: 'Can remove team members', roles: ['owner', 'admin'] },
|
|
44
|
+
],
|
|
45
|
+
|
|
46
|
+
// ==========================================
|
|
47
|
+
// ENTITY PERMISSIONS
|
|
48
|
+
// ==========================================
|
|
49
|
+
entities: {
|
|
50
|
+
// TASKS
|
|
51
|
+
tasks: [
|
|
52
|
+
{ action: 'create', label: 'Create tasks', description: 'Can create new tasks', roles: ['owner', 'admin', 'member'] },
|
|
53
|
+
{ action: 'read', label: 'View tasks', description: 'Can view task details', roles: ['owner', 'admin', 'member'] },
|
|
54
|
+
{ action: 'list', label: 'List tasks', description: 'Can see the tasks list', roles: ['owner', 'admin', 'member'] },
|
|
55
|
+
{ action: 'update', label: 'Edit tasks', description: 'Can modify task information', roles: ['owner', 'admin', 'member'] },
|
|
56
|
+
{ action: 'delete', label: 'Delete tasks', description: 'Can delete tasks', roles: ['owner', 'admin'], dangerous: true },
|
|
57
|
+
],
|
|
58
|
+
|
|
59
|
+
// CUSTOMERS
|
|
60
|
+
customers: [
|
|
61
|
+
{ action: 'create', label: 'Create customers', description: 'Can create new customers', roles: ['owner', 'admin'] },
|
|
62
|
+
{ action: 'read', label: 'View customers', description: 'Can view customer details', roles: ['owner', 'admin', 'member'] },
|
|
63
|
+
{ action: 'list', label: 'List customers', description: 'Can see the customers list', roles: ['owner', 'admin', 'member'] },
|
|
64
|
+
{ action: 'update', label: 'Edit customers', description: 'Can modify customer information', roles: ['owner', 'admin'] },
|
|
65
|
+
{ action: 'delete', label: 'Delete customers', description: 'Can delete customers', roles: ['owner'], dangerous: true },
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
// ==========================================
|
|
70
|
+
// FEATURE PERMISSIONS
|
|
71
|
+
// ==========================================
|
|
72
|
+
features: [
|
|
73
|
+
// Offline Mode
|
|
74
|
+
{
|
|
75
|
+
action: 'offline.access',
|
|
76
|
+
label: 'Offline Mode',
|
|
77
|
+
description: 'Can use the app in offline mode',
|
|
78
|
+
category: 'Mobile',
|
|
79
|
+
roles: ['owner', 'admin', 'member'],
|
|
80
|
+
},
|
|
81
|
+
// Push Notifications
|
|
82
|
+
{
|
|
83
|
+
action: 'notifications.receive',
|
|
84
|
+
label: 'Push Notifications',
|
|
85
|
+
description: 'Can receive push notifications',
|
|
86
|
+
category: 'Mobile',
|
|
87
|
+
roles: ['owner', 'admin', 'member', 'viewer'],
|
|
88
|
+
},
|
|
89
|
+
// Quick Create
|
|
90
|
+
{
|
|
91
|
+
action: 'quick-create.access',
|
|
92
|
+
label: 'Quick Create',
|
|
93
|
+
description: 'Can use quick create from bottom nav',
|
|
94
|
+
category: 'Mobile',
|
|
95
|
+
roles: ['owner', 'admin', 'member'],
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Helper function (optional - for future use)
|
|
101
|
+
export function canDoAction(role: string, action: string): boolean {
|
|
102
|
+
// Check teams
|
|
103
|
+
const teamPerm = PERMISSIONS_CONFIG.teams.find(p => p.action === action)
|
|
104
|
+
if (teamPerm) return teamPerm.roles.includes(role)
|
|
105
|
+
|
|
106
|
+
// Check entities
|
|
107
|
+
for (const [entity, perms] of Object.entries(PERMISSIONS_CONFIG.entities)) {
|
|
108
|
+
const entityPerm = perms.find(p => `${entity}.${p.action}` === action)
|
|
109
|
+
if (entityPerm) return entityPerm.roles.includes(role)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Check features
|
|
113
|
+
const featurePerm = PERMISSIONS_CONFIG.features.find(p => p.action === action)
|
|
114
|
+
if (featurePerm) return featurePerm.roles.includes(role)
|
|
115
|
+
|
|
116
|
+
return false
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export default PERMISSIONS_CONFIG
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NextSpark Mobile Color Palette
|
|
3
|
+
* Black/White theme matching default NextSpark theme
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const Colors = {
|
|
7
|
+
// Primary
|
|
8
|
+
primary: '#000000',
|
|
9
|
+
primaryForeground: '#FFFFFF',
|
|
10
|
+
|
|
11
|
+
// Background
|
|
12
|
+
background: '#FFFFFF',
|
|
13
|
+
backgroundSecondary: '#F9FAFB',
|
|
14
|
+
backgroundTertiary: '#F3F4F6',
|
|
15
|
+
|
|
16
|
+
// Foreground
|
|
17
|
+
foreground: '#111827',
|
|
18
|
+
foregroundSecondary: '#6B7280',
|
|
19
|
+
foregroundMuted: '#9CA3AF',
|
|
20
|
+
|
|
21
|
+
// Border
|
|
22
|
+
border: '#E5E7EB',
|
|
23
|
+
borderSecondary: '#D1D5DB',
|
|
24
|
+
|
|
25
|
+
// Accent
|
|
26
|
+
accent: '#F59E0B', // Orange for avatar
|
|
27
|
+
accentForeground: '#FFFFFF',
|
|
28
|
+
|
|
29
|
+
// Destructive
|
|
30
|
+
destructive: '#DC2626',
|
|
31
|
+
destructiveForeground: '#FFFFFF',
|
|
32
|
+
|
|
33
|
+
// Success
|
|
34
|
+
success: '#10B981',
|
|
35
|
+
successForeground: '#FFFFFF',
|
|
36
|
+
|
|
37
|
+
// Card
|
|
38
|
+
card: '#FFFFFF',
|
|
39
|
+
cardForeground: '#111827',
|
|
40
|
+
|
|
41
|
+
// Status colors
|
|
42
|
+
statusTodo: '#6B7280',
|
|
43
|
+
statusInProgress: '#3B82F6',
|
|
44
|
+
statusReview: '#F59E0B',
|
|
45
|
+
statusDone: '#10B981',
|
|
46
|
+
statusBlocked: '#DC2626',
|
|
47
|
+
|
|
48
|
+
// Priority colors
|
|
49
|
+
priorityLow: '#6B7280',
|
|
50
|
+
priorityMedium: '#F59E0B',
|
|
51
|
+
priorityHigh: '#F97316',
|
|
52
|
+
priorityUrgent: '#DC2626',
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default Colors
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"notifications": [
|
|
3
|
+
{
|
|
4
|
+
"id": "notif-001",
|
|
5
|
+
"type": "task_assigned",
|
|
6
|
+
"title": "Nueva tarea asignada",
|
|
7
|
+
"message": "Se te ha asignado la tarea 'Revisar propuesta de diseño'",
|
|
8
|
+
"read": false,
|
|
9
|
+
"createdAt": "2025-01-24T10:30:00Z",
|
|
10
|
+
"data": {
|
|
11
|
+
"taskId": "task-123",
|
|
12
|
+
"taskTitle": "Revisar propuesta de diseño"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "notif-002",
|
|
17
|
+
"type": "task_completed",
|
|
18
|
+
"title": "Tarea completada",
|
|
19
|
+
"message": "La tarea 'Implementar autenticación' ha sido marcada como completada",
|
|
20
|
+
"read": false,
|
|
21
|
+
"createdAt": "2025-01-24T09:15:00Z",
|
|
22
|
+
"data": {
|
|
23
|
+
"taskId": "task-456",
|
|
24
|
+
"taskTitle": "Implementar autenticación"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "notif-003",
|
|
29
|
+
"type": "customer_added",
|
|
30
|
+
"title": "Nuevo cliente agregado",
|
|
31
|
+
"message": "Se ha agregado el cliente 'Acme Corporation' al sistema",
|
|
32
|
+
"read": true,
|
|
33
|
+
"createdAt": "2025-01-23T16:45:00Z",
|
|
34
|
+
"data": {
|
|
35
|
+
"customerId": "cust-789",
|
|
36
|
+
"customerName": "Acme Corporation"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": "notif-004",
|
|
41
|
+
"type": "task_due_soon",
|
|
42
|
+
"title": "Tarea por vencer",
|
|
43
|
+
"message": "La tarea 'Entregar reporte mensual' vence mañana",
|
|
44
|
+
"read": false,
|
|
45
|
+
"createdAt": "2025-01-23T14:00:00Z",
|
|
46
|
+
"data": {
|
|
47
|
+
"taskId": "task-101",
|
|
48
|
+
"taskTitle": "Entregar reporte mensual",
|
|
49
|
+
"dueDate": "2025-01-25T23:59:59Z"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "notif-005",
|
|
54
|
+
"type": "team_invite",
|
|
55
|
+
"title": "Invitación a equipo",
|
|
56
|
+
"message": "Has sido invitado a unirte al equipo 'Marketing Digital'",
|
|
57
|
+
"read": true,
|
|
58
|
+
"createdAt": "2025-01-22T11:20:00Z",
|
|
59
|
+
"data": {
|
|
60
|
+
"teamId": "team-202",
|
|
61
|
+
"teamName": "Marketing Digital"
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "notif-006",
|
|
66
|
+
"type": "task_comment",
|
|
67
|
+
"title": "Nuevo comentario",
|
|
68
|
+
"message": "Carlos Mendoza comentó en 'Planificación Q1'",
|
|
69
|
+
"read": true,
|
|
70
|
+
"createdAt": "2025-01-22T08:30:00Z",
|
|
71
|
+
"data": {
|
|
72
|
+
"taskId": "task-303",
|
|
73
|
+
"taskTitle": "Planificación Q1",
|
|
74
|
+
"commentBy": "Carlos Mendoza"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"id": "notif-007",
|
|
79
|
+
"type": "system",
|
|
80
|
+
"title": "Actualización del sistema",
|
|
81
|
+
"message": "Se han realizado mejoras de rendimiento en la plataforma",
|
|
82
|
+
"read": true,
|
|
83
|
+
"createdAt": "2025-01-21T12:00:00Z",
|
|
84
|
+
"data": {}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"id": "notif-008",
|
|
88
|
+
"type": "task_overdue",
|
|
89
|
+
"title": "Tarea vencida",
|
|
90
|
+
"message": "La tarea 'Actualizar documentación' está vencida desde hace 2 días",
|
|
91
|
+
"read": false,
|
|
92
|
+
"createdAt": "2025-01-20T09:00:00Z",
|
|
93
|
+
"data": {
|
|
94
|
+
"taskId": "task-404",
|
|
95
|
+
"taskTitle": "Actualizar documentación",
|
|
96
|
+
"dueDate": "2025-01-18T23:59:59Z"
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Customers API Client
|
|
3
|
+
*
|
|
4
|
+
* Auto-generated entity API using createEntityApi factory.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createEntityApi } from '@nextsparkjs/mobile'
|
|
8
|
+
import type { Customer, CreateCustomerInput, UpdateCustomerInput } from './types'
|
|
9
|
+
|
|
10
|
+
export const customersApi = createEntityApi<Customer, CreateCustomerInput, UpdateCustomerInput>('customers')
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Customer Entity Constants
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { DayOption } from './types'
|
|
6
|
+
|
|
7
|
+
// Day labels for display
|
|
8
|
+
export const DAY_LABELS: Record<DayOption, string> = {
|
|
9
|
+
lun: 'Lunes',
|
|
10
|
+
mar: 'Martes',
|
|
11
|
+
mie: 'Miércoles',
|
|
12
|
+
jue: 'Jueves',
|
|
13
|
+
vie: 'Viernes',
|
|
14
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TanStack Query mutations for Customers
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
|
6
|
+
import { customersApi } from './api'
|
|
7
|
+
import { CUSTOMERS_QUERY_KEY } from './constants.internal'
|
|
8
|
+
import type { CreateCustomerInput, UpdateCustomerInput } from './types'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Hook to create a new customer
|
|
12
|
+
*/
|
|
13
|
+
export function useCreateCustomer() {
|
|
14
|
+
const queryClient = useQueryClient()
|
|
15
|
+
|
|
16
|
+
return useMutation({
|
|
17
|
+
mutationFn: (data: CreateCustomerInput) => customersApi.create(data),
|
|
18
|
+
onSuccess: () => {
|
|
19
|
+
// Invalidate customers list to refetch
|
|
20
|
+
queryClient.invalidateQueries({ queryKey: CUSTOMERS_QUERY_KEY })
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Hook to update an existing customer
|
|
27
|
+
*/
|
|
28
|
+
export function useUpdateCustomer() {
|
|
29
|
+
const queryClient = useQueryClient()
|
|
30
|
+
|
|
31
|
+
return useMutation({
|
|
32
|
+
mutationFn: ({ id, data }: { id: string; data: UpdateCustomerInput }) =>
|
|
33
|
+
customersApi.update(id, data),
|
|
34
|
+
onSuccess: (response) => {
|
|
35
|
+
// Update the specific customer in cache
|
|
36
|
+
queryClient.setQueryData([...CUSTOMERS_QUERY_KEY, response.data.id], response)
|
|
37
|
+
// Invalidate list to refetch
|
|
38
|
+
queryClient.invalidateQueries({ queryKey: CUSTOMERS_QUERY_KEY })
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Hook to delete a customer
|
|
45
|
+
*/
|
|
46
|
+
export function useDeleteCustomer() {
|
|
47
|
+
const queryClient = useQueryClient()
|
|
48
|
+
|
|
49
|
+
return useMutation({
|
|
50
|
+
mutationFn: (id: string) => customersApi.delete(id),
|
|
51
|
+
onSuccess: (_, deletedId) => {
|
|
52
|
+
// Remove from cache
|
|
53
|
+
queryClient.removeQueries({ queryKey: [...CUSTOMERS_QUERY_KEY, deletedId] })
|
|
54
|
+
// Invalidate list to refetch
|
|
55
|
+
queryClient.invalidateQueries({ queryKey: CUSTOMERS_QUERY_KEY })
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TanStack Query hooks for Customers
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { useQuery } from '@tanstack/react-query'
|
|
6
|
+
import { customersApi } from './api'
|
|
7
|
+
import { CUSTOMERS_QUERY_KEY } from './constants.internal'
|
|
8
|
+
|
|
9
|
+
export { CUSTOMERS_QUERY_KEY }
|
|
10
|
+
|
|
11
|
+
interface UseCustomersOptions {
|
|
12
|
+
page?: number
|
|
13
|
+
limit?: number
|
|
14
|
+
search?: string
|
|
15
|
+
enabled?: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Hook to fetch paginated customers list
|
|
20
|
+
*/
|
|
21
|
+
export function useCustomers(options: UseCustomersOptions = {}) {
|
|
22
|
+
const { page = 1, limit = 20, search, enabled = true } = options
|
|
23
|
+
|
|
24
|
+
return useQuery({
|
|
25
|
+
queryKey: [...CUSTOMERS_QUERY_KEY, { page, limit, search }],
|
|
26
|
+
queryFn: () => customersApi.list({ page, limit, search }),
|
|
27
|
+
enabled,
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Hook to fetch a single customer by ID
|
|
33
|
+
*/
|
|
34
|
+
export function useCustomer(id: string | undefined) {
|
|
35
|
+
return useQuery({
|
|
36
|
+
queryKey: [...CUSTOMERS_QUERY_KEY, id],
|
|
37
|
+
queryFn: () => customersApi.get(id!),
|
|
38
|
+
enabled: !!id,
|
|
39
|
+
})
|
|
40
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Customer Entity Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Day options for visit/contact days
|
|
6
|
+
export type DayOption = 'lun' | 'mar' | 'mie' | 'jue' | 'vie'
|
|
7
|
+
|
|
8
|
+
// Customer entity
|
|
9
|
+
export interface Customer {
|
|
10
|
+
id: string
|
|
11
|
+
name: string
|
|
12
|
+
account: number
|
|
13
|
+
office: string
|
|
14
|
+
phone?: string | null
|
|
15
|
+
salesRep?: string | null
|
|
16
|
+
visitDays?: DayOption[] | null
|
|
17
|
+
contactDays?: DayOption[] | null
|
|
18
|
+
teamId: string
|
|
19
|
+
createdAt: string
|
|
20
|
+
updatedAt: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Create customer payload
|
|
24
|
+
export interface CreateCustomerInput {
|
|
25
|
+
name: string
|
|
26
|
+
account: number
|
|
27
|
+
office: string
|
|
28
|
+
phone?: string
|
|
29
|
+
salesRep?: string
|
|
30
|
+
visitDays?: DayOption[]
|
|
31
|
+
contactDays?: DayOption[]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Update customer payload
|
|
35
|
+
export interface UpdateCustomerInput {
|
|
36
|
+
name?: string
|
|
37
|
+
account?: number
|
|
38
|
+
office?: string
|
|
39
|
+
phone?: string | null
|
|
40
|
+
salesRep?: string | null
|
|
41
|
+
visitDays?: DayOption[] | null
|
|
42
|
+
contactDays?: DayOption[] | null
|
|
43
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tasks API Client
|
|
3
|
+
*
|
|
4
|
+
* Auto-generated entity API using createEntityApi factory.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createEntityApi } from '@nextsparkjs/mobile'
|
|
8
|
+
import type { Task, CreateTaskInput, UpdateTaskInput } from './types'
|
|
9
|
+
|
|
10
|
+
export const tasksApi = createEntityApi<Task, CreateTaskInput, UpdateTaskInput>('tasks')
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task Entity Constants
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { TaskStatus, TaskPriority } from './types'
|
|
6
|
+
|
|
7
|
+
// Status display labels
|
|
8
|
+
export const STATUS_LABELS: Record<TaskStatus, string> = {
|
|
9
|
+
'todo': 'To Do',
|
|
10
|
+
'in-progress': 'In Progress',
|
|
11
|
+
'review': 'In Review',
|
|
12
|
+
'done': 'Done',
|
|
13
|
+
'blocked': 'Blocked',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Priority display labels
|
|
17
|
+
export const PRIORITY_LABELS: Record<TaskPriority, string> = {
|
|
18
|
+
low: 'Low',
|
|
19
|
+
medium: 'Medium',
|
|
20
|
+
high: 'High',
|
|
21
|
+
urgent: 'Urgent',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Status colors
|
|
25
|
+
export const STATUS_COLORS: Record<TaskStatus, string> = {
|
|
26
|
+
'todo': '#6B7280',
|
|
27
|
+
'in-progress': '#3B82F6',
|
|
28
|
+
'review': '#F59E0B',
|
|
29
|
+
'done': '#10B981',
|
|
30
|
+
'blocked': '#EF4444',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Priority colors
|
|
34
|
+
export const PRIORITY_COLORS: Record<TaskPriority, string> = {
|
|
35
|
+
low: '#6B7280',
|
|
36
|
+
medium: '#3B82F6',
|
|
37
|
+
high: '#F59E0B',
|
|
38
|
+
urgent: '#EF4444',
|
|
39
|
+
}
|