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,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;
|