@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,231 @@
1
+ /**
2
+ * Task Form Component for Create/Edit
3
+ * Migrated to NativeWind + UI primitives
4
+ */
5
+
6
+ import { useState, useEffect } from "react";
7
+ import { View, ScrollView } from "react-native";
8
+ import { Alert } from "@nextsparkjs/mobile";
9
+ import type {
10
+ Task,
11
+ TaskStatus,
12
+ TaskPriority,
13
+ CreateTaskInput,
14
+ UpdateTaskInput,
15
+ } from "../../../entities/tasks";
16
+ import { STATUS_LABELS, PRIORITY_LABELS } from "../../../entities/tasks";
17
+ import { Text, Input, Textarea, Button, PressableBadge, Card } from "../../ui";
18
+
19
+ type TaskFormProps =
20
+ | {
21
+ mode: "create";
22
+ initialData?: undefined;
23
+ onSubmit: (data: CreateTaskInput) => Promise<void>;
24
+ onDelete?: undefined;
25
+ isLoading?: boolean;
26
+ }
27
+ | {
28
+ mode: "edit";
29
+ initialData: Task;
30
+ onSubmit: (data: UpdateTaskInput) => Promise<void>;
31
+ onDelete: () => Promise<void>;
32
+ isLoading?: boolean;
33
+ };
34
+
35
+ const STATUS_OPTIONS: TaskStatus[] = [
36
+ "todo",
37
+ "in-progress",
38
+ "review",
39
+ "done",
40
+ "blocked",
41
+ ];
42
+ const PRIORITY_OPTIONS: TaskPriority[] = ["low", "medium", "high", "urgent"];
43
+
44
+ export function TaskForm({
45
+ initialData,
46
+ onSubmit,
47
+ onDelete,
48
+ isLoading = false,
49
+ mode,
50
+ }: TaskFormProps) {
51
+ const [title, setTitle] = useState(initialData?.title || "");
52
+ const [description, setDescription] = useState(
53
+ initialData?.description || ""
54
+ );
55
+ const [status, setStatus] = useState<TaskStatus>(
56
+ initialData?.status || "todo"
57
+ );
58
+ const [priority, setPriority] = useState<TaskPriority>(
59
+ initialData?.priority || "medium"
60
+ );
61
+ const [error, setError] = useState<string | null>(null);
62
+
63
+ useEffect(() => {
64
+ if (initialData) {
65
+ setTitle(initialData.title);
66
+ setDescription(initialData.description || "");
67
+ setStatus(initialData.status);
68
+ setPriority(initialData.priority);
69
+ }
70
+ }, [initialData]);
71
+
72
+ const handleSubmit = async () => {
73
+ setError(null);
74
+
75
+ // Validation
76
+ if (!title.trim()) {
77
+ setError("El título es requerido");
78
+ return;
79
+ }
80
+
81
+ const data = {
82
+ title: title.trim(),
83
+ description: description.trim() || undefined,
84
+ status,
85
+ priority,
86
+ };
87
+
88
+ try {
89
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
90
+ await onSubmit(data as any);
91
+ } catch (err) {
92
+ setError(err instanceof Error ? err.message : "Ocurrió un error");
93
+ }
94
+ };
95
+
96
+ const handleDelete = async () => {
97
+ const confirmed = await Alert.confirmDestructive(
98
+ "Eliminar Tarea",
99
+ "¿Estás seguro que deseas eliminar esta tarea? Esta acción no se puede deshacer.",
100
+ "Eliminar"
101
+ );
102
+
103
+ if (confirmed) {
104
+ try {
105
+ await onDelete?.();
106
+ } catch (err) {
107
+ setError(err instanceof Error ? err.message : "Error al eliminar");
108
+ }
109
+ }
110
+ };
111
+
112
+ // Map status to badge variant
113
+ const getStatusVariant = (s: TaskStatus) => {
114
+ const map: Record<TaskStatus, "todo" | "in-progress" | "review" | "done" | "blocked"> = {
115
+ todo: "todo",
116
+ "in-progress": "in-progress",
117
+ review: "review",
118
+ done: "done",
119
+ blocked: "blocked",
120
+ };
121
+ return map[s];
122
+ };
123
+
124
+ // Map priority to badge variant
125
+ const getPriorityVariant = (p: TaskPriority) => {
126
+ const map: Record<TaskPriority, "low" | "medium" | "high" | "urgent"> = {
127
+ low: "low",
128
+ medium: "medium",
129
+ high: "high",
130
+ urgent: "urgent",
131
+ };
132
+ return map[p];
133
+ };
134
+
135
+ return (
136
+ <ScrollView
137
+ className="flex-1 bg-secondary p-4"
138
+ keyboardShouldPersistTaps="handled"
139
+ >
140
+ {/* Error Display */}
141
+ {error && (
142
+ <Card className="mb-4 border-destructive bg-red-50">
143
+ <Text variant="error">{error}</Text>
144
+ </Card>
145
+ )}
146
+
147
+ {/* Title */}
148
+ <Input
149
+ label="Título"
150
+ required
151
+ value={title}
152
+ onChangeText={setTitle}
153
+ placeholder="Ingresa el título de la tarea..."
154
+ editable={!isLoading}
155
+ containerClassName="mb-5"
156
+ />
157
+
158
+ {/* Description */}
159
+ <Textarea
160
+ label="Descripción"
161
+ value={description}
162
+ onChangeText={setDescription}
163
+ placeholder="Describe la tarea..."
164
+ editable={!isLoading}
165
+ containerClassName="mb-5"
166
+ />
167
+
168
+ {/* Status */}
169
+ <View className="mb-5 gap-2">
170
+ <Text variant="label">Estado</Text>
171
+ <View className="flex-row flex-wrap gap-2">
172
+ {STATUS_OPTIONS.map((opt) => (
173
+ <PressableBadge
174
+ key={opt}
175
+ variant="outline"
176
+ selectedVariant={getStatusVariant(opt)}
177
+ selected={status === opt}
178
+ onPress={() => setStatus(opt)}
179
+ disabled={isLoading}
180
+ >
181
+ {STATUS_LABELS[opt]}
182
+ </PressableBadge>
183
+ ))}
184
+ </View>
185
+ </View>
186
+
187
+ {/* Priority */}
188
+ <View className="mb-5 gap-2">
189
+ <Text variant="label">Prioridad</Text>
190
+ <View className="flex-row flex-wrap gap-2">
191
+ {PRIORITY_OPTIONS.map((opt) => (
192
+ <PressableBadge
193
+ key={opt}
194
+ variant="outline"
195
+ selectedVariant={getPriorityVariant(opt)}
196
+ selected={priority === opt}
197
+ onPress={() => setPriority(opt)}
198
+ disabled={isLoading}
199
+ >
200
+ {PRIORITY_LABELS[opt]}
201
+ </PressableBadge>
202
+ ))}
203
+ </View>
204
+ </View>
205
+
206
+ {/* Submit Button */}
207
+ <Button
208
+ onPress={handleSubmit}
209
+ isLoading={isLoading}
210
+ className="mt-2"
211
+ >
212
+ {mode === "create" ? "Crear Tarea" : "Guardar Cambios"}
213
+ </Button>
214
+
215
+ {/* Delete Button (edit mode only) */}
216
+ {mode === "edit" && (
217
+ <Button
218
+ variant="outline-destructive"
219
+ onPress={handleDelete}
220
+ disabled={isLoading}
221
+ className="mt-3"
222
+ >
223
+ Eliminar Tarea
224
+ </Button>
225
+ )}
226
+
227
+ {/* Spacer */}
228
+ <View className="h-10" />
229
+ </ScrollView>
230
+ );
231
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Task entity components - barrel exports
3
+ */
4
+
5
+ export { TaskCard } from './TaskCard'
6
+ export { TaskForm } from './TaskForm'
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Feature components - barrel exports
3
+ * Placeholder for future feature-specific components
4
+ */
5
+
6
+ // TODO: Add feature components here as needed
@@ -0,0 +1,80 @@
1
+ /**
2
+ * BottomTabBar Component - NextSpark Mobile Style
3
+ * Inicio | Tareas | [Crear] | Clientes | Más
4
+ * Migrated to NativeWind + UI primitives + Lucide icons
5
+ */
6
+
7
+ import { View, Pressable } from "react-native";
8
+ import { Home, CheckSquare, Plus, Users, Menu, type LucideIcon } from "lucide-react-native";
9
+ import { Text } from "../ui";
10
+
11
+ export type TabKey = "home" | "tasks" | "create" | "customers" | "more";
12
+
13
+ interface BottomTabBarProps {
14
+ activeTab: TabKey;
15
+ onTabPress: (tab: TabKey) => void;
16
+ }
17
+
18
+ interface TabItem {
19
+ key: TabKey;
20
+ label: string;
21
+ Icon: LucideIcon;
22
+ }
23
+
24
+ const TABS: TabItem[] = [
25
+ { key: "home", label: "Inicio", Icon: Home },
26
+ { key: "tasks", label: "Tareas", Icon: CheckSquare },
27
+ { key: "create", label: "Crear", Icon: Plus },
28
+ { key: "customers", label: "Clientes", Icon: Users },
29
+ { key: "more", label: "Más", Icon: Menu },
30
+ ];
31
+
32
+ export function BottomTabBar({ activeTab, onTabPress }: BottomTabBarProps) {
33
+ return (
34
+ <View className="flex-row items-end justify-around border-t border-border bg-background pb-2 pt-2">
35
+ {TABS.map((tab) => {
36
+ const isActive = activeTab === tab.key;
37
+ const isCreate = tab.key === "create";
38
+ const IconComponent = tab.Icon;
39
+
40
+ if (isCreate) {
41
+ return (
42
+ <Pressable
43
+ key={tab.key}
44
+ className="-mt-6 flex-1 items-center"
45
+ onPress={() => onTabPress(tab.key)}
46
+ >
47
+ <View className="h-14 w-14 items-center justify-center rounded-full bg-primary shadow-lg">
48
+ <IconComponent size={28} color="#fafafa" strokeWidth={1.5} />
49
+ </View>
50
+ <Text className="mt-1 text-[11px] font-medium text-muted-foreground">
51
+ {tab.label}
52
+ </Text>
53
+ </Pressable>
54
+ );
55
+ }
56
+
57
+ return (
58
+ <Pressable
59
+ key={tab.key}
60
+ className="flex-1 items-center justify-center py-2"
61
+ onPress={() => onTabPress(tab.key)}
62
+ >
63
+ <IconComponent
64
+ size={24}
65
+ color={isActive ? "#171717" : "#737373"}
66
+ strokeWidth={isActive ? 2 : 1.5}
67
+ />
68
+ <Text
69
+ className={`mt-1 text-[11px] font-medium ${
70
+ isActive ? "text-foreground" : "text-muted-foreground"
71
+ }`}
72
+ >
73
+ {tab.label}
74
+ </Text>
75
+ </Pressable>
76
+ );
77
+ })}
78
+ </View>
79
+ );
80
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * CreateSheet Component - "Crear" bottom sheet for quick entity creation
3
+ * Migrated to NativeWind + Lucide icons
4
+ */
5
+
6
+ import { View, TouchableOpacity, Modal, Pressable } from "react-native";
7
+ import { Users, CheckSquare, X, type LucideIcon } from "lucide-react-native";
8
+ import { Text } from "../ui";
9
+
10
+ interface CreateSheetProps {
11
+ visible: boolean;
12
+ onClose: () => void;
13
+ onCreateEntity: (entity: string) => void;
14
+ }
15
+
16
+ interface CreateOption {
17
+ key: string;
18
+ label: string;
19
+ description: string;
20
+ Icon: LucideIcon;
21
+ }
22
+
23
+ const CREATE_OPTIONS: CreateOption[] = [
24
+ {
25
+ key: "customer",
26
+ label: "customer",
27
+ description: "Crear nuevo customer",
28
+ Icon: Users,
29
+ },
30
+ {
31
+ key: "task",
32
+ label: "task",
33
+ description: "Crear nuevo task",
34
+ Icon: CheckSquare,
35
+ },
36
+ ];
37
+
38
+ export function CreateSheet({
39
+ visible,
40
+ onClose,
41
+ onCreateEntity,
42
+ }: CreateSheetProps) {
43
+ const handleCreate = (option: CreateOption) => {
44
+ onCreateEntity(option.key);
45
+ onClose();
46
+ };
47
+
48
+ return (
49
+ <Modal
50
+ visible={visible}
51
+ transparent
52
+ animationType="slide"
53
+ onRequestClose={onClose}
54
+ >
55
+ <Pressable
56
+ className="flex-1 justify-end bg-black/50"
57
+ onPress={onClose}
58
+ >
59
+ <Pressable
60
+ className="rounded-t-[20px] bg-background px-6 pb-10 pt-3"
61
+ onPress={(e) => e.stopPropagation()}
62
+ >
63
+ {/* Handle bar */}
64
+ <View className="mb-4 h-1 w-10 self-center rounded-full bg-border" />
65
+
66
+ {/* Close button */}
67
+ <TouchableOpacity
68
+ className="absolute right-5 top-5 h-8 w-8 items-center justify-center rounded-full border border-border"
69
+ onPress={onClose}
70
+ >
71
+ <X size={16} color="#737373" />
72
+ </TouchableOpacity>
73
+
74
+ {/* Header */}
75
+ <Text className="mt-2 text-center text-xl font-semibold">Crear</Text>
76
+ <Text className="mb-6 mt-2 text-center text-sm text-muted-foreground">
77
+ Crea un nuevo elemento
78
+ </Text>
79
+
80
+ {/* Options */}
81
+ <View className="gap-2">
82
+ {CREATE_OPTIONS.map((option) => {
83
+ const IconComponent = option.Icon;
84
+ return (
85
+ <TouchableOpacity
86
+ key={option.key}
87
+ className="flex-row items-center gap-4 py-4"
88
+ onPress={() => handleCreate(option)}
89
+ activeOpacity={0.7}
90
+ >
91
+ <View className="w-8 items-center">
92
+ <IconComponent size={24} color="#171717" />
93
+ </View>
94
+ <View className="flex-1">
95
+ <Text className="text-base font-semibold">{option.label}</Text>
96
+ <Text className="mt-0.5 text-sm text-muted-foreground">
97
+ {option.description}
98
+ </Text>
99
+ </View>
100
+ </TouchableOpacity>
101
+ );
102
+ })}
103
+ </View>
104
+ </Pressable>
105
+ </Pressable>
106
+ </Modal>
107
+ );
108
+ }