feat: Implement alert creation service with FCM notifications
- Add AlertCreationService to handle alert creation and notifications. - Implement idempotency key support to prevent duplicate alerts. - Create alert notification service for sending FCM messages. - Add tests for alert creation and notification services. - Document FCM notification flow and alert creation process.
This commit is contained in:
@@ -66,12 +66,12 @@ export default function AlertsPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleFormSubmit = async (data) => {
|
||||
const handleFormSubmit = async (data, idempotencyKey) => {
|
||||
try {
|
||||
if (editingAlert) {
|
||||
await alertsApi.update(editingAlert.id, data);
|
||||
} else {
|
||||
await alertsApi.create(data);
|
||||
await alertsApi.create(data, idempotencyKey);
|
||||
}
|
||||
setShowForm(false);
|
||||
loadData();
|
||||
@@ -390,7 +390,17 @@ function AlertCard({ alert, index, onEdit, onDelete }) {
|
||||
);
|
||||
}
|
||||
|
||||
function createAlertIdempotencyKey() {
|
||||
if (globalThis.crypto?.randomUUID) {
|
||||
return globalThis.crypto.randomUUID();
|
||||
}
|
||||
|
||||
return `alert-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
||||
}
|
||||
|
||||
function AlertForm({ alert, routes, onSubmit, onCancel }) {
|
||||
const [idempotencyKey] = useState(createAlertIdempotencyKey);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState({
|
||||
route_id: alert?.route_id || '',
|
||||
alert_type: alert?.alert_type || 'info',
|
||||
@@ -401,7 +411,7 @@ function AlertForm({ alert, routes, onSubmit, onCancel }) {
|
||||
is_active: alert?.is_active !== false
|
||||
});
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const data = {
|
||||
@@ -410,7 +420,12 @@ function AlertForm({ alert, routes, onSubmit, onCancel }) {
|
||||
end_time: formData.end_time || null
|
||||
};
|
||||
|
||||
onSubmit(data);
|
||||
setSubmitting(true);
|
||||
try {
|
||||
await onSubmit(data, alert ? null : idempotencyKey);
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
@@ -546,15 +561,24 @@ function AlertForm({ alert, routes, onSubmit, onCancel }) {
|
||||
</div>
|
||||
|
||||
<div className="p-4 sm:p-6 border-t border-gray-200 flex flex-col-reverse sm:flex-row sm:justify-end gap-2 sm:gap-3 safe-bottom">
|
||||
<button type="button" onClick={onCancel} className="btn-secondary w-full sm:w-auto">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onCancel}
|
||||
className="btn-secondary w-full sm:w-auto"
|
||||
disabled={submitting}
|
||||
>
|
||||
Отмена
|
||||
</button>
|
||||
<button type="submit" className="btn-primary w-full sm:w-auto">
|
||||
Сохранить
|
||||
<button
|
||||
type="submit"
|
||||
className="btn-primary w-full sm:w-auto"
|
||||
disabled={submitting}
|
||||
>
|
||||
{submitting ? 'Сохранение…' : 'Сохранить'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,9 @@ export const alertsApi = {
|
||||
getAll: (params) => api.get('/alerts', { params }),
|
||||
getActive: (routeId) => api.get('/alerts/active', { params: { route_id: routeId } }),
|
||||
getById: (id) => api.get(`/alerts/${id}`),
|
||||
create: (data) => api.post('/alerts', data),
|
||||
create: (data, idempotencyKey) => api.post('/alerts', data, {
|
||||
headers: idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : undefined
|
||||
}),
|
||||
update: (id, data) => api.put(`/alerts/${id}`, data),
|
||||
delete: (id) => api.delete(`/alerts/${id}`),
|
||||
cleanup: () => api.post('/alerts/cleanup')
|
||||
|
||||
Reference in New Issue
Block a user