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.
- package/lib/commonjs/NotificationManager/NotificationManagerTabs.js +188 -0
- package/lib/commonjs/NotificationManager/NotificationManagerTabs.js.map +1 -0
- package/lib/commonjs/NotificationManager/api/index.js +235 -6
- package/lib/commonjs/NotificationManager/api/index.js.map +1 -1
- package/lib/commonjs/NotificationManager/components/GroupManagement.js +1038 -0
- package/lib/commonjs/NotificationManager/components/GroupManagement.js.map +1 -0
- package/lib/commonjs/NotificationManager/components/JobManagement.js +783 -0
- package/lib/commonjs/NotificationManager/components/JobManagement.js.map +1 -0
- package/lib/commonjs/NotificationManager/components/ScheduleNotification.js +407 -0
- package/lib/commonjs/NotificationManager/components/ScheduleNotification.js.map +1 -0
- package/lib/commonjs/NotificationManager/components/index.js +56 -0
- package/lib/commonjs/NotificationManager/components/index.js.map +1 -0
- package/lib/commonjs/NotificationManager/components/shared/DateTimePicker.js +113 -0
- package/lib/commonjs/NotificationManager/components/shared/DateTimePicker.js.map +1 -0
- package/lib/commonjs/NotificationManager/components/shared/GroupSelector.js +191 -0
- package/lib/commonjs/NotificationManager/components/shared/GroupSelector.js.map +1 -0
- package/lib/commonjs/NotificationManager/components/shared/NotificationBuilderForm.js +509 -0
- package/lib/commonjs/NotificationManager/components/shared/NotificationBuilderForm.js.map +1 -0
- package/lib/commonjs/NotificationManager/components/shared/StatusBadge.js +69 -0
- package/lib/commonjs/NotificationManager/components/shared/StatusBadge.js.map +1 -0
- package/lib/commonjs/NotificationManager/index.js +38 -23
- package/lib/commonjs/NotificationManager/index.js.map +1 -1
- package/lib/commonjs/index.js +7 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/types.d.js +2 -0
- package/lib/commonjs/types.d.js.map +1 -1
- package/lib/module/NotificationManager/NotificationManagerTabs.js +180 -0
- package/lib/module/NotificationManager/NotificationManagerTabs.js.map +1 -0
- package/lib/module/NotificationManager/api/index.js +235 -6
- package/lib/module/NotificationManager/api/index.js.map +1 -1
- package/lib/module/NotificationManager/components/GroupManagement.js +1030 -0
- package/lib/module/NotificationManager/components/GroupManagement.js.map +1 -0
- package/lib/module/NotificationManager/components/JobManagement.js +775 -0
- package/lib/module/NotificationManager/components/JobManagement.js.map +1 -0
- package/lib/module/NotificationManager/components/ScheduleNotification.js +399 -0
- package/lib/module/NotificationManager/components/ScheduleNotification.js.map +1 -0
- package/lib/module/NotificationManager/components/index.js +8 -0
- package/lib/module/NotificationManager/components/index.js.map +1 -0
- package/lib/module/NotificationManager/components/shared/DateTimePicker.js +106 -0
- package/lib/module/NotificationManager/components/shared/DateTimePicker.js.map +1 -0
- package/lib/module/NotificationManager/components/shared/GroupSelector.js +184 -0
- package/lib/module/NotificationManager/components/shared/GroupSelector.js.map +1 -0
- package/lib/module/NotificationManager/components/shared/NotificationBuilderForm.js +501 -0
- package/lib/module/NotificationManager/components/shared/NotificationBuilderForm.js.map +1 -0
- package/lib/module/NotificationManager/components/shared/StatusBadge.js +62 -0
- package/lib/module/NotificationManager/components/shared/StatusBadge.js.map +1 -0
- package/lib/module/NotificationManager/index.js +32 -23
- package/lib/module/NotificationManager/index.js.map +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.d.js +2 -0
- package/lib/module/types.d.js.map +1 -1
- package/lib/typescript/lib/commonjs/NotificationManager/NotificationManagerTabs.d.ts +17 -0
- package/lib/typescript/lib/commonjs/NotificationManager/NotificationManagerTabs.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/NotificationManager/api/index.d.ts +17 -2
- package/lib/typescript/lib/commonjs/NotificationManager/api/index.d.ts.map +1 -1
- package/lib/typescript/lib/commonjs/NotificationManager/components/GroupManagement.d.ts +6 -0
- package/lib/typescript/lib/commonjs/NotificationManager/components/GroupManagement.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/NotificationManager/components/JobManagement.d.ts +7 -0
- package/lib/typescript/lib/commonjs/NotificationManager/components/JobManagement.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/NotificationManager/components/ScheduleNotification.d.ts +8 -0
- package/lib/typescript/lib/commonjs/NotificationManager/components/ScheduleNotification.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/NotificationManager/components/index.d.ts +9 -0
- package/lib/typescript/lib/commonjs/NotificationManager/components/index.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/NotificationManager/components/shared/DateTimePicker.d.ts +9 -0
- package/lib/typescript/lib/commonjs/NotificationManager/components/shared/DateTimePicker.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/NotificationManager/components/shared/GroupSelector.d.ts +13 -0
- package/lib/typescript/lib/commonjs/NotificationManager/components/shared/GroupSelector.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/NotificationManager/components/shared/NotificationBuilderForm.d.ts +10 -0
- package/lib/typescript/lib/commonjs/NotificationManager/components/shared/NotificationBuilderForm.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/NotificationManager/components/shared/StatusBadge.d.ts +6 -0
- package/lib/typescript/lib/commonjs/NotificationManager/components/shared/StatusBadge.d.ts.map +1 -0
- package/lib/typescript/lib/commonjs/NotificationManager/index.d.ts +18 -2
- package/lib/typescript/lib/commonjs/NotificationManager/index.d.ts.map +1 -1
- package/lib/typescript/lib/commonjs/index.d.ts +18 -2
- package/lib/typescript/lib/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/lib/module/NotificationManager/NotificationManagerTabs.d.ts +17 -0
- package/lib/typescript/lib/module/NotificationManager/NotificationManagerTabs.d.ts.map +1 -0
- package/lib/typescript/lib/module/NotificationManager/api/index.d.ts +17 -2
- package/lib/typescript/lib/module/NotificationManager/api/index.d.ts.map +1 -1
- package/lib/typescript/lib/module/NotificationManager/components/GroupManagement.d.ts +6 -0
- package/lib/typescript/lib/module/NotificationManager/components/GroupManagement.d.ts.map +1 -0
- package/lib/typescript/lib/module/NotificationManager/components/JobManagement.d.ts +7 -0
- package/lib/typescript/lib/module/NotificationManager/components/JobManagement.d.ts.map +1 -0
- package/lib/typescript/lib/module/NotificationManager/components/ScheduleNotification.d.ts +8 -0
- package/lib/typescript/lib/module/NotificationManager/components/ScheduleNotification.d.ts.map +1 -0
- package/lib/typescript/lib/module/NotificationManager/components/index.d.ts +8 -0
- package/lib/typescript/lib/module/NotificationManager/components/index.d.ts.map +1 -0
- package/lib/typescript/lib/module/NotificationManager/components/shared/DateTimePicker.d.ts +9 -0
- package/lib/typescript/lib/module/NotificationManager/components/shared/DateTimePicker.d.ts.map +1 -0
- package/lib/typescript/lib/module/NotificationManager/components/shared/GroupSelector.d.ts +13 -0
- package/lib/typescript/lib/module/NotificationManager/components/shared/GroupSelector.d.ts.map +1 -0
- package/lib/typescript/lib/module/NotificationManager/components/shared/NotificationBuilderForm.d.ts +10 -0
- package/lib/typescript/lib/module/NotificationManager/components/shared/NotificationBuilderForm.d.ts.map +1 -0
- package/lib/typescript/lib/module/NotificationManager/components/shared/StatusBadge.d.ts +6 -0
- package/lib/typescript/lib/module/NotificationManager/components/shared/StatusBadge.d.ts.map +1 -0
- package/lib/typescript/lib/module/NotificationManager/index.d.ts +1 -0
- package/lib/typescript/lib/module/NotificationManager/index.d.ts.map +1 -1
- package/lib/typescript/lib/module/index.d.ts +2 -1
- package/lib/typescript/lib/module/index.d.ts.map +1 -1
- package/lib/typescript/src/NotificationManager/NotificationManagerTabs.d.ts +20 -0
- package/lib/typescript/src/NotificationManager/NotificationManagerTabs.d.ts.map +1 -0
- package/lib/typescript/src/NotificationManager/api/index.d.ts +74 -3
- package/lib/typescript/src/NotificationManager/api/index.d.ts.map +1 -1
- package/lib/typescript/src/NotificationManager/components/GroupManagement.d.ts +8 -0
- package/lib/typescript/src/NotificationManager/components/GroupManagement.d.ts.map +1 -0
- package/lib/typescript/src/NotificationManager/components/JobManagement.d.ts +9 -0
- package/lib/typescript/src/NotificationManager/components/JobManagement.d.ts.map +1 -0
- package/lib/typescript/src/NotificationManager/components/ScheduleNotification.d.ts +10 -0
- package/lib/typescript/src/NotificationManager/components/ScheduleNotification.d.ts.map +1 -0
- package/lib/typescript/src/NotificationManager/components/index.d.ts +8 -0
- package/lib/typescript/src/NotificationManager/components/index.d.ts.map +1 -0
- package/lib/typescript/src/NotificationManager/components/shared/DateTimePicker.d.ts +12 -0
- package/lib/typescript/src/NotificationManager/components/shared/DateTimePicker.d.ts.map +1 -0
- package/lib/typescript/src/NotificationManager/components/shared/GroupSelector.d.ts +16 -0
- package/lib/typescript/src/NotificationManager/components/shared/GroupSelector.d.ts.map +1 -0
- package/lib/typescript/src/NotificationManager/components/shared/NotificationBuilderForm.d.ts +12 -0
- package/lib/typescript/src/NotificationManager/components/shared/NotificationBuilderForm.d.ts.map +1 -0
- package/lib/typescript/src/NotificationManager/components/shared/StatusBadge.d.ts +8 -0
- package/lib/typescript/src/NotificationManager/components/shared/StatusBadge.d.ts.map +1 -0
- package/lib/typescript/src/NotificationManager/index.d.ts +1 -0
- package/lib/typescript/src/NotificationManager/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/NotificationManager/NotificationManagerTabs.tsx +178 -0
- package/src/NotificationManager/api/index.ts +239 -6
- package/src/NotificationManager/components/GroupManagement.tsx +854 -0
- package/src/NotificationManager/components/JobManagement.tsx +569 -0
- package/src/NotificationManager/components/ScheduleNotification.tsx +388 -0
- package/src/NotificationManager/components/index.ts +7 -0
- package/src/NotificationManager/components/shared/DateTimePicker.tsx +94 -0
- package/src/NotificationManager/components/shared/GroupSelector.tsx +130 -0
- package/src/NotificationManager/components/shared/NotificationBuilderForm.tsx +364 -0
- package/src/NotificationManager/components/shared/StatusBadge.tsx +72 -0
- package/src/NotificationManager/index.tsx +43 -24
- package/src/index.tsx +2 -0
- 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;
|