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,569 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { FlatList, TouchableOpacity, ActivityIndicator } from 'react-native';
3
+ import { View, Text, Button, TextInput } from '../../Components/Themed';
4
+ import { useColors } from '../../constants/useColors';
5
+ import { NotificationApi } from '../api';
6
+ import { showConfirmAlert } from '../../Components/ConfirmAlert';
7
+ import { Icons } from '../../Components';
8
+ import StatusBadge from './shared/StatusBadge';
9
+ import type { NotificationJobProps, NotificationJobStatus, NotificationGroupProps, FocusPositionProps } from '../../types';
10
+ import DropDown from '../../Components/Dropdown';
11
+ import Pagination from '../../Components/Pagination';
12
+
13
+ interface JobManagementProps {
14
+ onEditJob?: (job: NotificationJobProps) => void;
15
+ onFocusPosition?:(pos:FocusPositionProps) => void
16
+ }
17
+
18
+ const STATUS_FILTERS: { label: string; value: NotificationJobStatus | 'all' }[] = [
19
+ { label: 'All Jobs', value: 'all' },
20
+ { label: 'Drafts', value: 'pending' },
21
+ { label: 'Scheduled', value: 'ready' },
22
+ { label: 'Sending', value: 'processing' },
23
+ { label: 'Sent', value: 'sent' },
24
+ { label: 'Failed', value: 'failed' },
25
+ { label: 'Cancelled', value: 'cancelled' },
26
+ ];
27
+
28
+ type ListItemType =
29
+ | { type: 'header' }
30
+ | { type: 'search' }
31
+ | { type: 'filter' }
32
+ | { type: 'results-count' }
33
+ | { type: 'job'; data: NotificationJobProps }
34
+ | { type: 'empty' }
35
+ | { type: 'loading' }
36
+ | { type: 'pagination' };
37
+
38
+ const JobManagement = ({ onEditJob, onFocusPosition }: JobManagementProps) => {
39
+ const Colors = useColors();
40
+ const [loading, setLoading] = useState(false);
41
+ const [jobs, setJobs] = useState<NotificationJobProps[]>([]);
42
+ const [groups, setGroups] = useState<NotificationGroupProps[]>([]);
43
+ const [statusFilter, setStatusFilter] = useState<NotificationJobStatus | 'all'>('all');
44
+ const [selectedJob, setSelectedJob] = useState<NotificationJobProps | null>(null);
45
+ const [showDetailsModal, setShowDetailsModal] = useState(false);
46
+ const [autoRefreshEnabled, setAutoRefreshEnabled] = useState(true);
47
+
48
+ // Pagination state
49
+ const [currentPage, setCurrentPage] = useState(0);
50
+ const pageSize = 20;
51
+ const [hasMore, setHasMore] = useState(false);
52
+
53
+ // Search state
54
+ const [searchQuery, setSearchQuery] = useState('');
55
+
56
+ useEffect(() => {
57
+ loadJobs();
58
+ loadGroups();
59
+ }, []);
60
+
61
+ useEffect(() => {
62
+ // Reload jobs when status filter or current page changes
63
+ loadJobs();
64
+ }, [statusFilter, currentPage]);
65
+
66
+ useEffect(() => {
67
+ // Auto-refresh every 30 seconds
68
+ if (!autoRefreshEnabled) return;
69
+
70
+ const interval = setInterval(() => {
71
+ loadJobs();
72
+ }, 30000);
73
+
74
+ return () => clearInterval(interval);
75
+ }, [autoRefreshEnabled, statusFilter, currentPage]);
76
+
77
+ const loadJobs = async () => {
78
+ setLoading(true);
79
+ const offset = currentPage * pageSize;
80
+
81
+ let fetchedJobs: NotificationJobProps[] = [];
82
+ if (statusFilter === 'all') {
83
+ fetchedJobs = await NotificationApi.getAllNotificationJobs(pageSize, offset);
84
+ } else {
85
+ fetchedJobs = await NotificationApi.getNotificationJobsByStatus(statusFilter, pageSize, offset);
86
+ }
87
+
88
+ // Sort by scheduled_time descending (most recent first)
89
+ fetchedJobs.sort((a, b) => new Date(b.scheduled_time).getTime() - new Date(a.scheduled_time).getTime());
90
+
91
+ // Check if there are more results (if we got the full page size, there might be more)
92
+ setHasMore(fetchedJobs.length === pageSize);
93
+
94
+ setJobs(fetchedJobs);
95
+ setLoading(false);
96
+ };
97
+
98
+ const loadGroups = async () => {
99
+ const allGroups = await NotificationApi.getAllNotificationGroups();
100
+ setGroups(allGroups);
101
+ };
102
+
103
+ const getGroupName = (groupId: string) => {
104
+ const group = groups.find(g => g.notification_group_id === groupId);
105
+ return group?.name || 'Unknown Group';
106
+ };
107
+
108
+ const handleStatusFilterChange = (newStatus: NotificationJobStatus | 'all') => {
109
+ setStatusFilter(newStatus);
110
+ setCurrentPage(0); // Reset to first page when filter changes
111
+ };
112
+
113
+ const handlePreviousPage = () => {
114
+ if (currentPage > 0) {
115
+ setCurrentPage(currentPage - 1);
116
+ }
117
+ };
118
+
119
+ const handleNextPage = () => {
120
+ if (hasMore) {
121
+ setCurrentPage(currentPage + 1);
122
+ }
123
+ };
124
+
125
+ const handleSelectPage = (page: number) => {
126
+ setCurrentPage(page);
127
+ };
128
+
129
+ // Client-side search filtering
130
+ const filteredJobs = React.useMemo(() => {
131
+ if (!searchQuery.trim()) {
132
+ return jobs;
133
+ }
134
+ const query = searchQuery.toLowerCase();
135
+ return jobs.filter(job => {
136
+ const title = (job.notification.title || '').toLowerCase();
137
+ const body = (job.notification.body || '').toLowerCase();
138
+ return title.includes(query) || body.includes(query);
139
+ });
140
+ }, [jobs, searchQuery]);
141
+
142
+ // Build list data for FlatList
143
+ const listData: ListItemType[] = React.useMemo(() => {
144
+ const data: ListItemType[] = [];
145
+
146
+ // Always add header, search, and filter sections
147
+ data.push({ type: 'header' });
148
+ data.push({ type: 'search' });
149
+ data.push({ type: 'filter' });
150
+
151
+ // If loading, show loading indicator
152
+ if (loading) {
153
+ data.push({ type: 'loading' });
154
+ return data;
155
+ }
156
+
157
+ // If no jobs, show empty state
158
+ if (filteredJobs.length === 0) {
159
+ data.push({ type: 'empty' });
160
+ return data;
161
+ }
162
+
163
+ // Show results count
164
+ data.push({ type: 'results-count' });
165
+
166
+ // Add all job items
167
+ filteredJobs.forEach(job => {
168
+ data.push({ type: 'job', data: job });
169
+ });
170
+
171
+ // Add pagination
172
+ data.push({ type: 'pagination' });
173
+
174
+ return data;
175
+ }, [loading, filteredJobs, currentPage, searchQuery]);
176
+
177
+ const handleCancel = (job: NotificationJobProps) => {
178
+ if (job.status !== 'pending' && job.status !== 'ready') {
179
+ alert('Only pending or scheduled jobs can be cancelled.');
180
+ return;
181
+ }
182
+
183
+ showConfirmAlert(
184
+ 'Cancel Job',
185
+ `Are you sure you want to cancel this scheduled notification?`,
186
+ async () => {
187
+ try {
188
+ setLoading(true);
189
+ await NotificationApi.cancelNotificationJob(job.notification_job_id);
190
+ await loadJobs();
191
+ } catch (error) {
192
+ console.error('Error cancelling job:', error);
193
+ alert('Failed to cancel job. Please try again.');
194
+ } finally {
195
+ setLoading(false);
196
+ }
197
+ }
198
+ );
199
+ };
200
+
201
+ const handleRetry = (job: NotificationJobProps) => {
202
+ if (job.status !== 'failed') {
203
+ alert('Only failed jobs can be retried.');
204
+ return;
205
+ }
206
+
207
+ showConfirmAlert(
208
+ 'Retry Failed Job',
209
+ `This will create a new job with the same notification. The original failed job will remain in history.`,
210
+ async () => {
211
+ try {
212
+ setLoading(true);
213
+ // Create a new job with same notification
214
+ await NotificationApi.createNotificationJob({
215
+ notification_group_id: job.notification_group_id,
216
+ notification: job.notification,
217
+ scheduled_time: new Date(),
218
+ status: 'pending'
219
+ });
220
+ await loadJobs();
221
+ alert('New job created! Edit and finalize it when ready.');
222
+ } catch (error) {
223
+ console.error('Error retrying job:', error);
224
+ alert('Failed to create retry job. Please try again.');
225
+ } finally {
226
+ setLoading(false);
227
+ }
228
+ }
229
+ );
230
+ };
231
+
232
+ const handleViewDetails = (job: NotificationJobProps) => {
233
+ setSelectedJob(job);
234
+ setShowDetailsModal(true);
235
+ };
236
+
237
+ const formatDateTime = (dateTime: any) => {
238
+ const date = new Date(dateTime);
239
+ return date.toLocaleString();
240
+ };
241
+
242
+ // Render functions for each section type
243
+ const renderHeader = () => (
244
+ <View type='header' style={{ flexDirection: 'row', alignItems: 'center', padding: 10 }}>
245
+ <View transparent style={{ flex: 1 }}>
246
+ <Text theme='h1'>Manage Jobs</Text>
247
+ <Text theme='description' style={{ marginTop: 3 }}>
248
+ View and manage scheduled notifications
249
+ </Text>
250
+ </View>
251
+ <View transparent style={{ flexDirection: 'row', alignItems: 'center' }}>
252
+ <TouchableOpacity
253
+ onPress={() => setAutoRefreshEnabled(!autoRefreshEnabled)}
254
+ style={{ padding: 8, marginRight: 5 }}
255
+ >
256
+ <View transparent style={{ flexDirection: 'row', alignItems: 'center' }}>
257
+ <Icons.RefreshIcon
258
+ size={16}
259
+ color={autoRefreshEnabled ? Colors.text.success : Colors.text.h2}
260
+ />
261
+ <Text
262
+ theme="description"
263
+ color={autoRefreshEnabled ? Colors.text.success : Colors.text.h2}
264
+ style={{ marginLeft: 4, fontSize: 11 }}
265
+ >
266
+ Auto
267
+ </Text>
268
+ </View>
269
+ </TouchableOpacity>
270
+ <Button type="action" onPress={loadJobs} style={{ padding: 10, marginLeft: 5, flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}>
271
+ <Icons.RefreshIcon size={14} color={Colors.text.white} />
272
+ <Text theme="h2" color={Colors.text.white} style={{ marginLeft: 6 }}>
273
+ Refresh
274
+ </Text>
275
+ </Button>
276
+ </View>
277
+ </View>
278
+ );
279
+
280
+ const renderSearch = () => (
281
+ <View type='row' style={{ padding: 10, margin: 10, marginBottom: 5 }}>
282
+ <View transparent style={{ flex: 1, flexDirection: 'row', alignItems: 'center' }}>
283
+ <Icons.SearchIcon size={16} color={Colors.text.h2} />
284
+ <TextInput
285
+ placeholder="Search by title or message..."
286
+ value={searchQuery}
287
+ onChangeText={setSearchQuery}
288
+ onFocusPosition={onFocusPosition}
289
+ style={{ flex: 1, padding: 10, fontSize: 14, marginLeft:5 }}
290
+ />
291
+ {searchQuery.length > 0 && (
292
+ <TouchableOpacity onPress={() => setSearchQuery('')} style={{ padding: 8 }}>
293
+ <Icons.CloseIcon size={14} color={Colors.text.h2} />
294
+ </TouchableOpacity>
295
+ )}
296
+ </View>
297
+ </View>
298
+ );
299
+
300
+ const renderFilter = () => (
301
+ <View type='row' style={{ padding: 10, margin: 10, marginTop: 5 }}>
302
+ <Text theme="h2" style={{ flex: 1 }}>Filter by Status</Text>
303
+ <DropDown
304
+ selected_value={STATUS_FILTERS.find((f) => f.value === statusFilter)?.label ?? 'All Jobs'}
305
+ dropdown_options={[
306
+ { value: 'status', eligible_options: STATUS_FILTERS.map((f) => f.label) },
307
+ ]}
308
+ onOptionSelect={(selected) => {
309
+ const filter = STATUS_FILTERS.find((f) => f.label === selected);
310
+ if (filter) {
311
+ handleStatusFilterChange(filter.value);
312
+ }
313
+ }}
314
+ />
315
+ </View>
316
+ );
317
+
318
+ const renderResultsCount = () => (
319
+ <View transparent style={{ paddingHorizontal: 20, paddingVertical: 5 }}>
320
+ <Text theme="description" style={{ fontSize: 12 }}>
321
+ Showing {filteredJobs.length} result{filteredJobs.length !== 1 ? 's' : ''} on page {currentPage + 1}
322
+ {searchQuery && ` (filtered by search)`}
323
+ </Text>
324
+ </View>
325
+ );
326
+
327
+ const renderJobCard = (job: NotificationJobProps) => {
328
+ const canCancel = job.status === 'pending' || job.status === 'ready';
329
+ const canEdit = job.status === 'pending';
330
+ const canRetry = job.status === 'failed';
331
+
332
+ return (
333
+ <View
334
+ float
335
+ style={{
336
+ padding: 15,
337
+ margin: 10,
338
+ borderRadius: 8,
339
+ }}
340
+ >
341
+ <View transparent style={{ flexDirection: 'row', justifyContent: 'space-between', marginBottom: 10 }}>
342
+ <View transparent style={{ flex: 1 }}>
343
+ <View transparent style={{ flexDirection: 'row', alignItems: 'center', marginBottom: 6 }}>
344
+ <StatusBadge status={job.status} />
345
+ <Text theme="h2" style={{ marginLeft: 8, flex: 1 }}>
346
+ {getGroupName(job.notification_group_id)}
347
+ </Text>
348
+ </View>
349
+ <Text theme="h1" style={{ marginBottom: 4 }}>
350
+ {job.notification.title || 'No Title'}
351
+ </Text>
352
+ <Text theme="description" numberOfLines={2}>
353
+ {job.notification.body || 'No message'}
354
+ </Text>
355
+ <Text theme="description" style={{ marginTop: 6, fontSize: 11 }}>
356
+ Scheduled: {formatDateTime(job.scheduled_time)}
357
+ </Text>
358
+ {job.sent_datetime && (
359
+ <Text theme="description" style={{ fontSize: 11, color: Colors.text.success }}>
360
+ Sent: {formatDateTime(job.sent_datetime)}
361
+ </Text>
362
+ )}
363
+ {job.error_message && (
364
+ <Text theme="description" style={{ fontSize: 11, color: Colors.text.error, marginTop: 4 }}>
365
+ Error: {job.error_message}
366
+ </Text>
367
+ )}
368
+ </View>
369
+ </View>
370
+ <View transparent style={{ flexDirection: 'row', flexWrap: 'wrap' }}>
371
+ <Button
372
+ type="action"
373
+ onPress={() => handleViewDetails(job)}
374
+ style={{ flex: 1, minWidth: 100, padding: 10, marginRight: 4, flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}
375
+ >
376
+ <Icons.EyeOnIcon size={14} color={Colors.text.white} />
377
+ <Text theme="h2" color={Colors.text.white} style={{ marginLeft: 6 }}>
378
+ Details
379
+ </Text>
380
+ </Button>
381
+ {canEdit && onEditJob && (
382
+ <Button
383
+ type="success"
384
+ onPress={() => onEditJob(job)}
385
+ style={{ flex: 1, minWidth: 100, padding: 10, marginHorizontal: 4, flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}
386
+ >
387
+ <Icons.EditIcon size={14} color={Colors.text.white} />
388
+ <Text theme="h2" color={Colors.text.white} style={{ marginLeft: 6 }}>
389
+ Edit
390
+ </Text>
391
+ </Button>
392
+ )}
393
+ {canRetry && (
394
+ <Button
395
+ type="warning"
396
+ onPress={() => handleRetry(job)}
397
+ style={{ flex: 1, minWidth: 100, padding: 10, marginHorizontal: 4, flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}
398
+ >
399
+ <Icons.RefreshIcon size={14} color={Colors.text.white} />
400
+ <Text theme="h2" color={Colors.text.white} style={{ marginLeft: 6 }}>
401
+ Retry
402
+ </Text>
403
+ </Button>
404
+ )}
405
+ {canCancel && (
406
+ <Button
407
+ type="error"
408
+ onPress={() => handleCancel(job)}
409
+ style={{ flex: 1, minWidth: 100, padding: 10, marginLeft: 4, flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}
410
+ >
411
+ <Icons.CloseIcon size={14} color={Colors.text.white} />
412
+ <Text theme="h2" color={Colors.text.white} style={{ marginLeft: 6 }}>
413
+ Cancel
414
+ </Text>
415
+ </Button>
416
+ )}
417
+ </View>
418
+ </View>
419
+ );
420
+ };
421
+
422
+ const renderEmpty = () => (
423
+ <View style={{ padding: 40, alignItems: 'center' }}>
424
+ <Text theme="description">
425
+ {searchQuery
426
+ ? 'No jobs match your search query.'
427
+ : statusFilter === 'all'
428
+ ? 'No jobs found. Schedule your first notification!'
429
+ : `No ${STATUS_FILTERS.find((f) => f.value === statusFilter)?.label.toLowerCase()} found.`}
430
+ </Text>
431
+ </View>
432
+ );
433
+
434
+ const renderLoading = () => (
435
+ <View style={{ padding: 40, justifyContent: 'center', alignItems: 'center' }}>
436
+ <ActivityIndicator size="large" color={Colors.text.action} />
437
+ </View>
438
+ );
439
+
440
+ const renderPagination = () => (
441
+ <View transparent style={{ padding: 15, paddingBottom: 20 }}>
442
+ <Pagination
443
+ offset={currentPage}
444
+ pages={hasMore ? currentPage + 2 : currentPage + 1}
445
+ onPrevious={handlePreviousPage}
446
+ onNext={handleNextPage}
447
+ onSelectPage={handleSelectPage}
448
+ />
449
+ </View>
450
+ );
451
+
452
+ // Main render item function with switch statement
453
+ const renderItem = ({ item }: { item: ListItemType }) => {
454
+ switch (item.type) {
455
+ case 'header':
456
+ return renderHeader();
457
+ case 'search':
458
+ return renderSearch();
459
+ case 'filter':
460
+ return renderFilter();
461
+ case 'results-count':
462
+ return renderResultsCount();
463
+ case 'job':
464
+ return renderJobCard(item.data);
465
+ case 'empty':
466
+ return renderEmpty();
467
+ case 'loading':
468
+ return renderLoading();
469
+ case 'pagination':
470
+ return renderPagination();
471
+ default:
472
+ return null;
473
+ }
474
+ };
475
+
476
+ const getItemKey = (item: ListItemType, index: number) => {
477
+ if (item.type === 'job') {
478
+ return item.data.notification_job_id;
479
+ }
480
+ return `${item.type}-${index}`;
481
+ };
482
+
483
+ return (
484
+ <View style={{ flex: 1 }}>
485
+ {/* Single FlatList for entire component */}
486
+ <FlatList
487
+ data={listData}
488
+ keyExtractor={getItemKey}
489
+ renderItem={renderItem}
490
+ showsVerticalScrollIndicator={true}
491
+ />
492
+
493
+ {/* Details Modal */}
494
+ {showDetailsModal && selectedJob && (
495
+ <View type='blur' style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, padding: 20 }}>
496
+ <View float style={{ maxWidth: 600, alignSelf: 'center', width: '100%', maxHeight: '80%' }}>
497
+ <View type='header' style={{ flexDirection: 'row', alignItems: 'center', padding: 10, borderTopRightRadius: 8, borderTopLeftRadius: 8 }}>
498
+ <View transparent style={{ flex: 1 }}>
499
+ <Text theme='h1'>Job Details</Text>
500
+ <Text theme='description' style={{ marginTop: 3 }}>
501
+ Full notification information
502
+ </Text>
503
+ </View>
504
+ <StatusBadge status={selectedJob.status} />
505
+ </View>
506
+ <View style={{ flex: 1, padding: 15 }}>
507
+ <View transparent style={{ marginBottom: 15 }}>
508
+ <Text theme="h2" style={{ marginBottom: 4 }}>Group</Text>
509
+ <Text theme="description">{getGroupName(selectedJob.notification_group_id)}</Text>
510
+ </View>
511
+ <View transparent style={{ marginBottom: 15 }}>
512
+ <Text theme="h2" style={{ marginBottom: 4 }}>Title</Text>
513
+ <Text theme="h1">{selectedJob.notification.title || 'No Title'}</Text>
514
+ </View>
515
+ <View transparent style={{ marginBottom: 15 }}>
516
+ <Text theme="h2" style={{ marginBottom: 4 }}>Message</Text>
517
+ <Text theme="description">{selectedJob.notification.body || 'No message'}</Text>
518
+ </View>
519
+ <View transparent style={{ marginBottom: 15 }}>
520
+ <Text theme="h2" style={{ marginBottom: 4 }}>Notification Type</Text>
521
+ <Text theme="description">{selectedJob.notification.type}</Text>
522
+ </View>
523
+ {selectedJob.notification.options?.data?.path_name && (
524
+ <View transparent style={{ marginBottom: 15 }}>
525
+ <Text theme="h2" style={{ marginBottom: 4 }}>Deep Link</Text>
526
+ <Text theme="description">{selectedJob.notification.options.data.path_name}</Text>
527
+ </View>
528
+ )}
529
+ <View transparent style={{ marginBottom: 15 }}>
530
+ <Text theme="h2" style={{ marginBottom: 4 }}>Scheduled Time</Text>
531
+ <Text theme="description">{formatDateTime(selectedJob.scheduled_time)}</Text>
532
+ </View>
533
+ {selectedJob.sent_datetime && (
534
+ <View transparent style={{ marginBottom: 15 }}>
535
+ <Text theme="h2" style={{ marginBottom: 4 }}>Sent Time</Text>
536
+ <Text theme="description" color={Colors.text.success}>
537
+ {formatDateTime(selectedJob.sent_datetime)}
538
+ </Text>
539
+ </View>
540
+ )}
541
+ {selectedJob.error_message && (
542
+ <View transparent style={{ marginBottom: 15 }}>
543
+ <Text theme="h2" style={{ marginBottom: 4, color: Colors.text.error }}>Error Message</Text>
544
+ <Text theme="description" color={Colors.text.error}>
545
+ {selectedJob.error_message}
546
+ </Text>
547
+ </View>
548
+ )}
549
+ <View transparent style={{ marginBottom: 15 }}>
550
+ <Text theme="h2" style={{ marginBottom: 4 }}>Created</Text>
551
+ <Text theme="description">{formatDateTime(selectedJob.create_datetime)}</Text>
552
+ </View>
553
+ </View>
554
+ <View type='footer' style={{ flexDirection: 'row', alignItems: 'center', padding: 10, borderBottomRightRadius: 8, borderBottomLeftRadius: 8 }}>
555
+ <Button
556
+ style={{ flex: 1 }}
557
+ type='close'
558
+ title='CLOSE'
559
+ onPress={() => setShowDetailsModal(false)}
560
+ />
561
+ </View>
562
+ </View>
563
+ </View>
564
+ )}
565
+ </View>
566
+ );
567
+ };
568
+
569
+ export default JobManagement;