be-components 7.6.3 → 7.6.4

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 (138) hide show
  1. package/lib/commonjs/NotificationManager/NotificationManagerTabs.js +188 -0
  2. package/lib/commonjs/NotificationManager/NotificationManagerTabs.js.map +1 -0
  3. package/lib/commonjs/NotificationManager/api/index.js +235 -6
  4. package/lib/commonjs/NotificationManager/api/index.js.map +1 -1
  5. package/lib/commonjs/NotificationManager/components/GroupManagement.js +1038 -0
  6. package/lib/commonjs/NotificationManager/components/GroupManagement.js.map +1 -0
  7. package/lib/commonjs/NotificationManager/components/JobManagement.js +783 -0
  8. package/lib/commonjs/NotificationManager/components/JobManagement.js.map +1 -0
  9. package/lib/commonjs/NotificationManager/components/ScheduleNotification.js +407 -0
  10. package/lib/commonjs/NotificationManager/components/ScheduleNotification.js.map +1 -0
  11. package/lib/commonjs/NotificationManager/components/index.js +56 -0
  12. package/lib/commonjs/NotificationManager/components/index.js.map +1 -0
  13. package/lib/commonjs/NotificationManager/components/shared/DateTimePicker.js +113 -0
  14. package/lib/commonjs/NotificationManager/components/shared/DateTimePicker.js.map +1 -0
  15. package/lib/commonjs/NotificationManager/components/shared/GroupSelector.js +191 -0
  16. package/lib/commonjs/NotificationManager/components/shared/GroupSelector.js.map +1 -0
  17. package/lib/commonjs/NotificationManager/components/shared/NotificationBuilderForm.js +509 -0
  18. package/lib/commonjs/NotificationManager/components/shared/NotificationBuilderForm.js.map +1 -0
  19. package/lib/commonjs/NotificationManager/components/shared/StatusBadge.js +69 -0
  20. package/lib/commonjs/NotificationManager/components/shared/StatusBadge.js.map +1 -0
  21. package/lib/commonjs/NotificationManager/index.js +38 -23
  22. package/lib/commonjs/NotificationManager/index.js.map +1 -1
  23. package/lib/commonjs/index.js +7 -0
  24. package/lib/commonjs/index.js.map +1 -1
  25. package/lib/commonjs/types.d.js +2 -0
  26. package/lib/commonjs/types.d.js.map +1 -1
  27. package/lib/module/NotificationManager/NotificationManagerTabs.js +180 -0
  28. package/lib/module/NotificationManager/NotificationManagerTabs.js.map +1 -0
  29. package/lib/module/NotificationManager/api/index.js +235 -6
  30. package/lib/module/NotificationManager/api/index.js.map +1 -1
  31. package/lib/module/NotificationManager/components/GroupManagement.js +1030 -0
  32. package/lib/module/NotificationManager/components/GroupManagement.js.map +1 -0
  33. package/lib/module/NotificationManager/components/JobManagement.js +775 -0
  34. package/lib/module/NotificationManager/components/JobManagement.js.map +1 -0
  35. package/lib/module/NotificationManager/components/ScheduleNotification.js +399 -0
  36. package/lib/module/NotificationManager/components/ScheduleNotification.js.map +1 -0
  37. package/lib/module/NotificationManager/components/index.js +8 -0
  38. package/lib/module/NotificationManager/components/index.js.map +1 -0
  39. package/lib/module/NotificationManager/components/shared/DateTimePicker.js +106 -0
  40. package/lib/module/NotificationManager/components/shared/DateTimePicker.js.map +1 -0
  41. package/lib/module/NotificationManager/components/shared/GroupSelector.js +184 -0
  42. package/lib/module/NotificationManager/components/shared/GroupSelector.js.map +1 -0
  43. package/lib/module/NotificationManager/components/shared/NotificationBuilderForm.js +501 -0
  44. package/lib/module/NotificationManager/components/shared/NotificationBuilderForm.js.map +1 -0
  45. package/lib/module/NotificationManager/components/shared/StatusBadge.js +62 -0
  46. package/lib/module/NotificationManager/components/shared/StatusBadge.js.map +1 -0
  47. package/lib/module/NotificationManager/index.js +32 -23
  48. package/lib/module/NotificationManager/index.js.map +1 -1
  49. package/lib/module/index.js +2 -1
  50. package/lib/module/index.js.map +1 -1
  51. package/lib/module/types.d.js +2 -0
  52. package/lib/module/types.d.js.map +1 -1
  53. package/lib/typescript/lib/commonjs/NotificationManager/NotificationManagerTabs.d.ts +17 -0
  54. package/lib/typescript/lib/commonjs/NotificationManager/NotificationManagerTabs.d.ts.map +1 -0
  55. package/lib/typescript/lib/commonjs/NotificationManager/api/index.d.ts +17 -2
  56. package/lib/typescript/lib/commonjs/NotificationManager/api/index.d.ts.map +1 -1
  57. package/lib/typescript/lib/commonjs/NotificationManager/components/GroupManagement.d.ts +6 -0
  58. package/lib/typescript/lib/commonjs/NotificationManager/components/GroupManagement.d.ts.map +1 -0
  59. package/lib/typescript/lib/commonjs/NotificationManager/components/JobManagement.d.ts +7 -0
  60. package/lib/typescript/lib/commonjs/NotificationManager/components/JobManagement.d.ts.map +1 -0
  61. package/lib/typescript/lib/commonjs/NotificationManager/components/ScheduleNotification.d.ts +8 -0
  62. package/lib/typescript/lib/commonjs/NotificationManager/components/ScheduleNotification.d.ts.map +1 -0
  63. package/lib/typescript/lib/commonjs/NotificationManager/components/index.d.ts +9 -0
  64. package/lib/typescript/lib/commonjs/NotificationManager/components/index.d.ts.map +1 -0
  65. package/lib/typescript/lib/commonjs/NotificationManager/components/shared/DateTimePicker.d.ts +9 -0
  66. package/lib/typescript/lib/commonjs/NotificationManager/components/shared/DateTimePicker.d.ts.map +1 -0
  67. package/lib/typescript/lib/commonjs/NotificationManager/components/shared/GroupSelector.d.ts +13 -0
  68. package/lib/typescript/lib/commonjs/NotificationManager/components/shared/GroupSelector.d.ts.map +1 -0
  69. package/lib/typescript/lib/commonjs/NotificationManager/components/shared/NotificationBuilderForm.d.ts +10 -0
  70. package/lib/typescript/lib/commonjs/NotificationManager/components/shared/NotificationBuilderForm.d.ts.map +1 -0
  71. package/lib/typescript/lib/commonjs/NotificationManager/components/shared/StatusBadge.d.ts +6 -0
  72. package/lib/typescript/lib/commonjs/NotificationManager/components/shared/StatusBadge.d.ts.map +1 -0
  73. package/lib/typescript/lib/commonjs/NotificationManager/index.d.ts +18 -2
  74. package/lib/typescript/lib/commonjs/NotificationManager/index.d.ts.map +1 -1
  75. package/lib/typescript/lib/commonjs/index.d.ts +18 -2
  76. package/lib/typescript/lib/commonjs/index.d.ts.map +1 -1
  77. package/lib/typescript/lib/module/NotificationManager/NotificationManagerTabs.d.ts +17 -0
  78. package/lib/typescript/lib/module/NotificationManager/NotificationManagerTabs.d.ts.map +1 -0
  79. package/lib/typescript/lib/module/NotificationManager/api/index.d.ts +17 -2
  80. package/lib/typescript/lib/module/NotificationManager/api/index.d.ts.map +1 -1
  81. package/lib/typescript/lib/module/NotificationManager/components/GroupManagement.d.ts +6 -0
  82. package/lib/typescript/lib/module/NotificationManager/components/GroupManagement.d.ts.map +1 -0
  83. package/lib/typescript/lib/module/NotificationManager/components/JobManagement.d.ts +7 -0
  84. package/lib/typescript/lib/module/NotificationManager/components/JobManagement.d.ts.map +1 -0
  85. package/lib/typescript/lib/module/NotificationManager/components/ScheduleNotification.d.ts +8 -0
  86. package/lib/typescript/lib/module/NotificationManager/components/ScheduleNotification.d.ts.map +1 -0
  87. package/lib/typescript/lib/module/NotificationManager/components/index.d.ts +8 -0
  88. package/lib/typescript/lib/module/NotificationManager/components/index.d.ts.map +1 -0
  89. package/lib/typescript/lib/module/NotificationManager/components/shared/DateTimePicker.d.ts +9 -0
  90. package/lib/typescript/lib/module/NotificationManager/components/shared/DateTimePicker.d.ts.map +1 -0
  91. package/lib/typescript/lib/module/NotificationManager/components/shared/GroupSelector.d.ts +13 -0
  92. package/lib/typescript/lib/module/NotificationManager/components/shared/GroupSelector.d.ts.map +1 -0
  93. package/lib/typescript/lib/module/NotificationManager/components/shared/NotificationBuilderForm.d.ts +10 -0
  94. package/lib/typescript/lib/module/NotificationManager/components/shared/NotificationBuilderForm.d.ts.map +1 -0
  95. package/lib/typescript/lib/module/NotificationManager/components/shared/StatusBadge.d.ts +6 -0
  96. package/lib/typescript/lib/module/NotificationManager/components/shared/StatusBadge.d.ts.map +1 -0
  97. package/lib/typescript/lib/module/NotificationManager/index.d.ts +1 -0
  98. package/lib/typescript/lib/module/NotificationManager/index.d.ts.map +1 -1
  99. package/lib/typescript/lib/module/index.d.ts +2 -1
  100. package/lib/typescript/lib/module/index.d.ts.map +1 -1
  101. package/lib/typescript/src/NotificationManager/NotificationManagerTabs.d.ts +20 -0
  102. package/lib/typescript/src/NotificationManager/NotificationManagerTabs.d.ts.map +1 -0
  103. package/lib/typescript/src/NotificationManager/api/index.d.ts +74 -3
  104. package/lib/typescript/src/NotificationManager/api/index.d.ts.map +1 -1
  105. package/lib/typescript/src/NotificationManager/components/GroupManagement.d.ts +8 -0
  106. package/lib/typescript/src/NotificationManager/components/GroupManagement.d.ts.map +1 -0
  107. package/lib/typescript/src/NotificationManager/components/JobManagement.d.ts +9 -0
  108. package/lib/typescript/src/NotificationManager/components/JobManagement.d.ts.map +1 -0
  109. package/lib/typescript/src/NotificationManager/components/ScheduleNotification.d.ts +10 -0
  110. package/lib/typescript/src/NotificationManager/components/ScheduleNotification.d.ts.map +1 -0
  111. package/lib/typescript/src/NotificationManager/components/index.d.ts +8 -0
  112. package/lib/typescript/src/NotificationManager/components/index.d.ts.map +1 -0
  113. package/lib/typescript/src/NotificationManager/components/shared/DateTimePicker.d.ts +12 -0
  114. package/lib/typescript/src/NotificationManager/components/shared/DateTimePicker.d.ts.map +1 -0
  115. package/lib/typescript/src/NotificationManager/components/shared/GroupSelector.d.ts +16 -0
  116. package/lib/typescript/src/NotificationManager/components/shared/GroupSelector.d.ts.map +1 -0
  117. package/lib/typescript/src/NotificationManager/components/shared/NotificationBuilderForm.d.ts +12 -0
  118. package/lib/typescript/src/NotificationManager/components/shared/NotificationBuilderForm.d.ts.map +1 -0
  119. package/lib/typescript/src/NotificationManager/components/shared/StatusBadge.d.ts +8 -0
  120. package/lib/typescript/src/NotificationManager/components/shared/StatusBadge.d.ts.map +1 -0
  121. package/lib/typescript/src/NotificationManager/index.d.ts +1 -0
  122. package/lib/typescript/src/NotificationManager/index.d.ts.map +1 -1
  123. package/lib/typescript/src/index.d.ts +2 -1
  124. package/lib/typescript/src/index.d.ts.map +1 -1
  125. package/package.json +1 -1
  126. package/src/NotificationManager/NotificationManagerTabs.tsx +178 -0
  127. package/src/NotificationManager/api/index.ts +239 -6
  128. package/src/NotificationManager/components/GroupManagement.tsx +854 -0
  129. package/src/NotificationManager/components/JobManagement.tsx +569 -0
  130. package/src/NotificationManager/components/ScheduleNotification.tsx +388 -0
  131. package/src/NotificationManager/components/index.ts +7 -0
  132. package/src/NotificationManager/components/shared/DateTimePicker.tsx +94 -0
  133. package/src/NotificationManager/components/shared/GroupSelector.tsx +130 -0
  134. package/src/NotificationManager/components/shared/NotificationBuilderForm.tsx +364 -0
  135. package/src/NotificationManager/components/shared/StatusBadge.tsx +72 -0
  136. package/src/NotificationManager/index.tsx +43 -24
  137. package/src/index.tsx +2 -0
  138. package/src/types.d.ts +38 -3
@@ -0,0 +1,388 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { ScrollView, ActivityIndicator } from 'react-native';
3
+ import { View, Text, Button } from '../../Components/Themed';
4
+ import { useColors } from '../../constants/useColors';
5
+ import { NotificationApi, NotificationHelpers } from '../api';
6
+ import { showConfirmAlert } from '../../Components/ConfirmAlert';
7
+ import { Icons } from '../../Components';
8
+ import NotificationBuilderForm from './shared/NotificationBuilderForm';
9
+ import GroupSelector from './shared/GroupSelector';
10
+ import BETimePicker from '../../Components/BETimePicker';
11
+ import moment from 'moment-mini';
12
+ import type { PlayerNotificationProps, NotificationGroupProps, NotificationJobProps, FocusPositionProps } from '../../types';
13
+
14
+ interface ScheduleNotificationProps {
15
+ onFocusPosition?: (pos: FocusPositionProps) => void;
16
+ editingJob?: NotificationJobProps | null;
17
+ onJobSaved?: () => void;
18
+ }
19
+
20
+ const ScheduleNotification = ({ onFocusPosition, editingJob, onJobSaved }: ScheduleNotificationProps) => {
21
+ const Colors = useColors();
22
+ const [loading, setLoading] = useState(false);
23
+ const [loadingGroups, setLoadingGroups] = useState(false);
24
+ const [groups, setGroups] = useState<NotificationGroupProps[]>([]);
25
+ const [selectedGroup, setSelectedGroup] = useState<NotificationGroupProps | null>(null);
26
+ const [showGroupSelector, setShowGroupSelector] = useState(false);
27
+ const [scheduledTime, setScheduledTime] = useState(moment().add(1, 'hour')); // 1 hour from now
28
+ const [notification, setNotification] = useState<Partial<PlayerNotificationProps>>({
29
+ type: 'order_notifications',
30
+ notify_body_type: 'custom',
31
+ title: '',
32
+ body: '',
33
+ status: 'unread',
34
+ expire_time: 0,
35
+ options: {
36
+ id: '',
37
+ body: '',
38
+ icon: '',
39
+ type: '',
40
+ data: {
41
+ id: '',
42
+ player_id: '',
43
+ pageStack: 'MainStack',
44
+ page: '',
45
+ pageParams: {},
46
+ path_name: '',
47
+ params: {}
48
+ }
49
+ }
50
+ });
51
+
52
+ useEffect(() => {
53
+ loadGroups();
54
+
55
+ // If editing a job, populate the form
56
+ if (editingJob) {
57
+ setNotification(editingJob.notification);
58
+ setScheduledTime(moment(editingJob.scheduled_time));
59
+ loadGroupForJob(editingJob.notification_group_id);
60
+ }
61
+ }, [editingJob]);
62
+
63
+ const loadGroups = async () => {
64
+ setLoadingGroups(true);
65
+ const activeGroups = await NotificationApi.getActiveNotificationGroups();
66
+ setGroups(activeGroups);
67
+ setLoadingGroups(false);
68
+ };
69
+
70
+ const loadGroupForJob = async (groupId: string) => {
71
+ const group = await NotificationApi.getNotificationGroupById(groupId);
72
+ if (group) {
73
+ setSelectedGroup(group);
74
+ }
75
+ };
76
+
77
+ const handleSelectGroup = (group: NotificationGroupProps) => {
78
+ setSelectedGroup(group);
79
+ };
80
+
81
+ const validateNotification = (): { valid: boolean; errors: string[] } => {
82
+ const errors: string[] = [];
83
+
84
+ if (!notification.title?.trim()) {
85
+ errors.push('Notification title is required');
86
+ }
87
+
88
+ if (!notification.body?.trim()) {
89
+ errors.push('Notification body is required');
90
+ }
91
+
92
+ if (!selectedGroup) {
93
+ errors.push('Please select a recipient group');
94
+ }
95
+
96
+ if (scheduledTime.isSameOrBefore(moment())) {
97
+ errors.push('Scheduled time must be in the future');
98
+ }
99
+
100
+ // Check required path parameters
101
+ if (notification.options?.data?.path_name) {
102
+ const path = NotificationHelpers.app_paths.find(p => p.path_name === notification.options?.data?.path_name);
103
+ if (path?.requiredParams) {
104
+ const params = notification.options.data.params || {};
105
+ const missingParams = path.requiredParams.filter(param => !params[param]);
106
+ if (missingParams.length > 0) {
107
+ errors.push(`Missing required parameters: ${missingParams.join(', ')}`);
108
+ }
109
+ }
110
+ }
111
+
112
+ return {
113
+ valid: errors.length === 0,
114
+ errors
115
+ };
116
+ };
117
+
118
+ const handleSaveDraft = async () => {
119
+ const validation = validateNotification();
120
+ if (!validation.valid) {
121
+ alert(`Cannot save:\n\n${validation.errors.join('\n')}`);
122
+ return;
123
+ }
124
+
125
+ try {
126
+ setLoading(true);
127
+
128
+ const jobData = {
129
+ notification_group_id: selectedGroup!.notification_group_id,
130
+ notification: notification as PlayerNotificationProps,
131
+ scheduled_time: scheduledTime.toISOString(),
132
+ status: 'pending' as const
133
+ };
134
+
135
+ if (editingJob) {
136
+ // Update existing job
137
+ await NotificationApi.updateNotificationJob({
138
+ ...editingJob,
139
+ ...jobData
140
+ });
141
+ alert('Job updated and saved as draft!');
142
+ } else {
143
+ // Create new job
144
+ await NotificationApi.createNotificationJob(jobData);
145
+ alert('Job saved as draft! Finalize it when ready to schedule.');
146
+ }
147
+
148
+ // Reset form
149
+ resetForm();
150
+ onJobSaved?.();
151
+ } catch (error) {
152
+ console.error('Error saving draft:', error);
153
+ alert('Failed to save draft. Please try again.');
154
+ } finally {
155
+ setLoading(false);
156
+ }
157
+ };
158
+
159
+ const handleSchedule = () => {
160
+ const validation = validateNotification();
161
+ if (!validation.valid) {
162
+ alert(`Cannot schedule:\n\n${validation.errors.join('\n')}`);
163
+ return;
164
+ }
165
+
166
+ showConfirmAlert(
167
+ 'Schedule Notification',
168
+ `This will schedule the notification to be sent to ${selectedGroup?.name} (${selectedGroup?.player_count} users) at ${scheduledTime.format('MM/DD/YYYY hh:mm a')}. Continue?`,
169
+ async () => {
170
+ try {
171
+ setLoading(true);
172
+
173
+ const jobData = {
174
+ notification_group_id: selectedGroup!.notification_group_id,
175
+ notification: notification as PlayerNotificationProps,
176
+ scheduled_time: scheduledTime.toISOString(),
177
+ status: 'pending' as const
178
+ };
179
+
180
+ let jobId: string;
181
+
182
+ if (editingJob) {
183
+ // Update existing job
184
+ await NotificationApi.updateNotificationJob({
185
+ ...editingJob,
186
+ ...jobData
187
+ });
188
+ jobId = editingJob.notification_job_id;
189
+ } else {
190
+ // Create new job
191
+ const newJob = await NotificationApi.createNotificationJob(jobData);
192
+ jobId = newJob!.notification_job_id;
193
+ }
194
+
195
+ // Finalize the job to mark it as ready
196
+ await NotificationApi.finalizeNotificationJob(jobId);
197
+
198
+ alert('Notification scheduled successfully!');
199
+
200
+ // Reset form
201
+ resetForm();
202
+ onJobSaved?.();
203
+ } catch (error) {
204
+ console.error('Error scheduling notification:', error);
205
+ alert('Failed to schedule notification. Please try again.');
206
+ } finally {
207
+ setLoading(false);
208
+ }
209
+ }
210
+ );
211
+ };
212
+
213
+ const resetForm = () => {
214
+ setNotification({
215
+ type: 'order_notifications',
216
+ notify_body_type: 'custom',
217
+ title: '',
218
+ body: '',
219
+ status: 'unread',
220
+ expire_time: 0,
221
+ options: {
222
+ id: '',
223
+ body: '',
224
+ icon: '',
225
+ type: '',
226
+ data: {
227
+ id: '',
228
+ player_id: '',
229
+ pageStack: 'MainStack',
230
+ page: '',
231
+ pageParams: {},
232
+ path_name: '',
233
+ params: {}
234
+ }
235
+ }
236
+ });
237
+ setSelectedGroup(null);
238
+ setScheduledTime(moment().add(1, 'hour'));
239
+ };
240
+
241
+ return (
242
+ <View style={{ flex: 1 }}>
243
+ {/* Header */}
244
+ <View type='header' style={{ flexDirection: 'row', alignItems: 'center', padding: 10 }}>
245
+ <View transparent style={{ flex: 1 }}>
246
+ <Text theme='h1'>{editingJob ? 'Edit Scheduled Notification' : 'Schedule Notification'}</Text>
247
+ <Text theme='description' style={{ marginTop: 3 }}>
248
+ {editingJob ? 'Update notification details and reschedule' : 'Create and schedule a notification for later'}
249
+ </Text>
250
+ </View>
251
+ </View>
252
+
253
+ <ScrollView style={{ flex: 1 }}>
254
+ {/* Group Selection */}
255
+ <View float style={{ margin: 10, padding: 15 }}>
256
+ <View transparent style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
257
+ <View transparent style={{ flex: 1 }}>
258
+ <Text theme="h2">Recipients</Text>
259
+ <Text theme="description" style={{ marginTop: 4 }}>
260
+ {selectedGroup ? `${selectedGroup.player_count} users in group` : 'No group selected'}
261
+ </Text>
262
+ </View>
263
+ <Button
264
+ type="action"
265
+ onPress={() => {
266
+ loadGroups();
267
+ setShowGroupSelector(true);
268
+ }}
269
+ style={{ padding: 8 }}
270
+ >
271
+ <Text theme="h1" color={Colors.text.white}>
272
+ Select Group
273
+ </Text>
274
+ </Button>
275
+ </View>
276
+
277
+ {selectedGroup && (
278
+ <View
279
+ float
280
+ style={{
281
+ flexDirection: 'row',
282
+ alignItems: 'center',
283
+ justifyContent: 'space-between',
284
+ padding: 12,
285
+ borderRadius: 8,
286
+ marginTop: 5
287
+ }}
288
+ >
289
+ <View transparent style={{ flex: 1 }}>
290
+ <Text theme="h1">{selectedGroup.name}</Text>
291
+ <Text theme="description" style={{ marginTop: 2 }}>
292
+ {selectedGroup.description}
293
+ </Text>
294
+ </View>
295
+ <Button
296
+ type="error"
297
+ onPress={() => setSelectedGroup(null)}
298
+ style={{ padding: 8 }}
299
+ >
300
+ <Icons.CloseIcon size={16} color={Colors.text.white} />
301
+ </Button>
302
+ </View>
303
+ )}
304
+ </View>
305
+
306
+ {/* Scheduled Time */}
307
+ <View float style={{ margin: 10, padding: 15 }}>
308
+ <Text theme="h2" style={{ marginBottom: 8 }}>Scheduled Send Time</Text>
309
+ <BETimePicker
310
+ selected_time={scheduledTime}
311
+ onSelectTime={setScheduledTime}
312
+ />
313
+ </View>
314
+
315
+ {/* Notification Builder */}
316
+ <NotificationBuilderForm
317
+ notification={notification}
318
+ onChange={setNotification}
319
+ onFocusPosition={onFocusPosition}
320
+ showPreview={true}
321
+ showType={true}
322
+ />
323
+ </ScrollView>
324
+
325
+ {/* Footer Actions */}
326
+ <View type='footer' style={{ flexDirection: 'row', padding: 10 }}>
327
+ <Button
328
+ type="close"
329
+ onPress={resetForm}
330
+ style={{ flex: 1, padding: 12, marginRight: 5 }}
331
+ disabled={loading}
332
+ >
333
+ <Text theme="h1" color={Colors.text.white}>
334
+ Clear
335
+ </Text>
336
+ </Button>
337
+ <Button
338
+ type="success"
339
+ onPress={handleSaveDraft}
340
+ style={{ flex: 1, padding: 12, marginHorizontal: 5 }}
341
+ disabled={loading}
342
+ >
343
+ {loading ? (
344
+ <ActivityIndicator size="small" color={Colors.text.white} />
345
+ ) : (
346
+ <>
347
+ <Icons.CheckIcon size={14} color={Colors.text.white} />
348
+ <Text theme="h1" color={Colors.text.white} style={{ marginLeft: 6 }}>
349
+ Save Draft
350
+ </Text>
351
+ </>
352
+ )}
353
+ </Button>
354
+ <Button
355
+ type="action"
356
+ onPress={handleSchedule}
357
+ style={{ flex: 1, padding: 12, marginLeft: 5 }}
358
+ disabled={loading}
359
+ >
360
+ {loading ? (
361
+ <ActivityIndicator size="small" color={Colors.text.white} />
362
+ ) : (
363
+ <>
364
+ <Icons.SendIcon size={14} color={Colors.text.white} />
365
+ <Text theme="h1" color={Colors.text.white} style={{ marginLeft: 6 }}>
366
+ Schedule
367
+ </Text>
368
+ </>
369
+ )}
370
+ </Button>
371
+ </View>
372
+
373
+ {/* Group Selector Modal */}
374
+ <GroupSelector
375
+ visible={showGroupSelector}
376
+ onClose={() => setShowGroupSelector(false)}
377
+ onSelectGroup={handleSelectGroup}
378
+ groups={groups}
379
+ loading={loadingGroups}
380
+ selectedGroup={selectedGroup}
381
+ showCustomOption={false}
382
+ onFocusPosition={onFocusPosition}
383
+ />
384
+ </View>
385
+ );
386
+ };
387
+
388
+ export default ScheduleNotification;
@@ -0,0 +1,7 @@
1
+ export { default as GroupManagement } from './GroupManagement';
2
+ export { default as ScheduleNotification } from './ScheduleNotification';
3
+ export { default as JobManagement } from './JobManagement';
4
+ export { default as GroupSelector } from './shared/GroupSelector';
5
+ export { default as StatusBadge } from './shared/StatusBadge';
6
+ export { default as DateTimePicker } from './shared/DateTimePicker';
7
+ export { default as NotificationBuilderForm } from './shared/NotificationBuilderForm';
@@ -0,0 +1,94 @@
1
+ import React from 'react';
2
+ import { View, Text, TextInput } from '../../../Components/Themed';
3
+ import type { FocusPositionProps } from '../../../types';
4
+
5
+ interface DateTimePickerProps {
6
+ value: Date;
7
+ onChange: (date: Date) => void;
8
+ label?: string;
9
+ minDate?: Date;
10
+ onFocusPosition?: (pos: FocusPositionProps) => void;
11
+ }
12
+
13
+ const DateTimePicker = ({ value, onChange, label, onFocusPosition }: DateTimePickerProps) => {
14
+
15
+ const formatDateForInput = (date: Date) => {
16
+ const year = date.getFullYear();
17
+ const month = String(date.getMonth() + 1).padStart(2, '0');
18
+ const day = String(date.getDate()).padStart(2, '0');
19
+ return `${year}-${month}-${day}`;
20
+ };
21
+
22
+ const formatTimeForInput = (date: Date) => {
23
+ const hours = String(date.getHours()).padStart(2, '0');
24
+ const minutes = String(date.getMinutes()).padStart(2, '0');
25
+ return `${hours}:${minutes}`;
26
+ };
27
+
28
+ const handleDateChange = (dateString: string) => {
29
+ if (!dateString) return;
30
+ const parts = dateString.split('-').map(Number);
31
+ if (parts.length !== 3) return;
32
+ const [year, month, day] = parts;
33
+ if (year === undefined || month === undefined || day === undefined) return;
34
+ const newDate = new Date(value);
35
+ newDate.setFullYear(year);
36
+ newDate.setMonth(month - 1);
37
+ newDate.setDate(day);
38
+ onChange(newDate);
39
+ };
40
+
41
+ const handleTimeChange = (timeString: string) => {
42
+ if (!timeString) return;
43
+ const parts = timeString.split(':').map(Number);
44
+ if (parts.length !== 2) return;
45
+ const [hours, minutes] = parts;
46
+ if (hours === undefined || minutes === undefined) return;
47
+ const newDate = new Date(value);
48
+ newDate.setHours(hours);
49
+ newDate.setMinutes(minutes);
50
+ onChange(newDate);
51
+ };
52
+
53
+
54
+ return (
55
+ <View transparent>
56
+ {label && (
57
+ <Text theme="h2" style={{ marginBottom: 8 }}>
58
+ {label}
59
+ </Text>
60
+ )}
61
+ <View transparent style={{ flexDirection: 'row' }}>
62
+ <View transparent style={{ flex: 1, marginRight: 5 }}>
63
+ <Text theme="description" style={{ marginBottom: 4, fontSize: 12 }}>
64
+ Date
65
+ </Text>
66
+ <TextInput
67
+ value={formatDateForInput(value)}
68
+ onFocusPosition={onFocusPosition}
69
+ placeholder="YYYY-MM-DD"
70
+ onChangeText={handleDateChange}
71
+ style={{ padding: 12, borderRadius: 8 }}
72
+ />
73
+ </View>
74
+ <View transparent style={{ flex: 1, marginLeft: 5 }}>
75
+ <Text theme="description" style={{ marginBottom: 4, fontSize: 12 }}>
76
+ Time
77
+ </Text>
78
+ <TextInput
79
+ value={formatTimeForInput(value)}
80
+ onFocusPosition={onFocusPosition}
81
+ placeholder="HH:MM"
82
+ onChangeText={handleTimeChange}
83
+ style={{ padding: 12, borderRadius: 8 }}
84
+ />
85
+ </View>
86
+ </View>
87
+ <Text theme="description" style={{ marginTop: 4, fontSize: 11 }}>
88
+ Selected: {value.toLocaleString()}
89
+ </Text>
90
+ </View>
91
+ );
92
+ };
93
+
94
+ export default DateTimePicker;
@@ -0,0 +1,130 @@
1
+ import React from 'react';
2
+ import { FlatList, TouchableOpacity, ActivityIndicator } from 'react-native';
3
+ import { View, Text, Button } from '../../../Components/Themed';
4
+ import { useColors } from '../../../constants/useColors';
5
+ import type { NotificationGroupProps, FocusPositionProps } from '../../../types';
6
+
7
+ interface GroupSelectorProps {
8
+ visible: boolean;
9
+ onClose: () => void;
10
+ onSelectGroup: (group: NotificationGroupProps) => void;
11
+ groups: NotificationGroupProps[];
12
+ loading?: boolean;
13
+ selectedGroup?: NotificationGroupProps | null;
14
+ showCustomOption?: boolean;
15
+ customGroup?: NotificationGroupProps;
16
+ onFocusPosition?: (pos: FocusPositionProps) => void;
17
+ }
18
+
19
+ const GroupSelector = ({
20
+ visible,
21
+ onClose,
22
+ onSelectGroup,
23
+ groups,
24
+ loading = false,
25
+ selectedGroup,
26
+ showCustomOption = true,
27
+ customGroup
28
+ }: GroupSelectorProps) => {
29
+ const Colors = useColors();
30
+
31
+ if (!visible) return null;
32
+
33
+ // Combine custom group with regular groups if enabled
34
+ const allGroups = showCustomOption && customGroup
35
+ ? [customGroup, ...groups]
36
+ : groups;
37
+
38
+ return (
39
+ <View type='blur' style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, padding: 20 }}>
40
+ <View float style={{ flex: 1 }}>
41
+ <View type='header' style={{ flexDirection: 'row', alignItems: 'center', padding: 10, borderTopRightRadius: 8, borderTopLeftRadius: 8 }}>
42
+ <View transparent style={{ flex: 1 }}>
43
+ <Text theme='h1'>Notification Groups</Text>
44
+ <Text theme='description' style={{ marginTop: 3 }}>Select a group to send to</Text>
45
+ </View>
46
+ </View>
47
+ <View style={{ flex: 1 }}>
48
+ {loading ? (
49
+ <View style={{ padding: 40, alignItems: 'center' }}>
50
+ <ActivityIndicator size="large" color={Colors.text.action} />
51
+ </View>
52
+ ) : (
53
+ <FlatList
54
+ data={allGroups}
55
+ keyExtractor={(item) => item.notification_group_id}
56
+ renderItem={({ item }) => {
57
+ const isSelected = selectedGroup?.notification_group_id === item.notification_group_id;
58
+ const isCustom = item.notification_group_id === 'custom_group';
59
+
60
+ return (
61
+ <TouchableOpacity
62
+ onPress={() => {
63
+ onSelectGroup(item);
64
+ onClose();
65
+ }}
66
+ style={{
67
+ padding: 15,
68
+ borderBottomWidth: 1,
69
+ borderColor: Colors.borders.light,
70
+ backgroundColor: isSelected ? Colors.views.header : 'transparent'
71
+ }}
72
+ >
73
+ <View transparent>
74
+ <View transparent style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
75
+ <View transparent style={{ flexDirection: 'row', alignItems: 'center', flex: 1 }}>
76
+ <Text theme="h1">{item.name}</Text>
77
+ {isCustom && (
78
+ <View transparent style={{ marginLeft: 8, paddingHorizontal: 6, paddingVertical: 2, backgroundColor: Colors.buttons.background.action, borderRadius: 4 }}>
79
+ <Text theme="description" color={Colors.text.white} style={{ fontSize: 10 }}>
80
+ CUSTOM
81
+ </Text>
82
+ </View>
83
+ )}
84
+ {isSelected && (
85
+ <View transparent style={{ marginLeft: 8, paddingHorizontal: 6, paddingVertical: 2, backgroundColor: Colors.text.success, borderRadius: 4 }}>
86
+ <Text theme="description" color={Colors.text.white} style={{ fontSize: 10 }}>
87
+ SELECTED
88
+ </Text>
89
+ </View>
90
+ )}
91
+ </View>
92
+ {item.player_count !== undefined && (
93
+ <View transparent style={{ marginLeft: 8, paddingHorizontal: 8, paddingVertical: 4, backgroundColor: Colors.views.header, borderRadius: 4 }}>
94
+ <Text theme="description" style={{ fontSize: 12, fontWeight: '600' }}>
95
+ {item.player_count.toLocaleString()} users
96
+ </Text>
97
+ </View>
98
+ )}
99
+ </View>
100
+ {item.description && (
101
+ <Text theme="description" style={{ marginTop: 4 }}>
102
+ {item.description}
103
+ </Text>
104
+ )}
105
+ </View>
106
+ </TouchableOpacity>
107
+ );
108
+ }}
109
+ ListEmptyComponent={
110
+ <View style={{ padding: 40, alignItems: 'center' }}>
111
+ <Text theme="description">No groups found</Text>
112
+ </View>
113
+ }
114
+ />
115
+ )}
116
+ </View>
117
+ <View type='footer' style={{ flexDirection: 'row', alignItems: 'center', padding: 10, borderBottomRightRadius: 8, borderBottomLeftRadius: 8 }}>
118
+ <Button
119
+ style={{ flex: 1 }}
120
+ type='close'
121
+ title='CLOSE'
122
+ onPress={onClose}
123
+ />
124
+ </View>
125
+ </View>
126
+ </View>
127
+ );
128
+ };
129
+
130
+ export default GroupSelector;