fix(number-fields): избегаем потери точности длинных чисел
- number-поля теперь рендерятся как text + inputMode=numeric, чтобы браузер не округлял значения через input type=number - пробелы при вставке в number-поля удаляются - бэкенд нормализует значения number-полей в строку перед сохранением - добавлен хелпер normalizeFieldValueForStorage Closes: искажение расчётного счёта и других длинных числовых полей
This commit is contained in:
56
server/utils/pushTaskUpdated.ts
Normal file
56
server/utils/pushTaskUpdated.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { storage } from '../storage';
|
||||
import { eventBus } from '../routes/shared';
|
||||
import { webPushService } from '../services/web-push.service';
|
||||
|
||||
/**
|
||||
* Sends web-push notifications about a task update to all task participants
|
||||
* who do NOT have an active SSE connection (i.e. are offline).
|
||||
*
|
||||
* Recipients = assignedTo + createdBy + all active taskUserAccess members,
|
||||
* minus the actor who triggered the event.
|
||||
*/
|
||||
export async function pushTaskUpdated(opts: {
|
||||
taskId: number;
|
||||
organizationId: number;
|
||||
formId: number;
|
||||
taskTitle: string;
|
||||
actorId: number;
|
||||
}): Promise<void> {
|
||||
const { taskId, organizationId, formId, taskTitle, actorId } = opts;
|
||||
|
||||
try {
|
||||
const candidates = new Set<number>();
|
||||
|
||||
// Core fields: assignedTo / createdBy
|
||||
const task = await storage.getTask(taskId, organizationId);
|
||||
if (task) {
|
||||
if (task.assignedTo) candidates.add(task.assignedTo);
|
||||
if (task.createdBy) candidates.add(task.createdBy);
|
||||
}
|
||||
|
||||
// All users who have interacted with the task (non-revoked access records)
|
||||
const accessList = await storage.getTaskUserAccess(taskId, organizationId);
|
||||
for (const entry of accessList) {
|
||||
if (!entry.revokedAt) {
|
||||
candidates.add(entry.userId);
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude the actor who triggered the event — they see it instantly
|
||||
candidates.delete(actorId);
|
||||
|
||||
for (const uid of candidates) {
|
||||
if (!eventBus.isUserConnected(uid, organizationId)) {
|
||||
await webPushService.sendToUser(uid, organizationId, {
|
||||
title: 'Задача обновлена',
|
||||
body: taskTitle,
|
||||
icon: '/favicon.svg',
|
||||
url: `/forms/${formId}/tasks/${taskId}`,
|
||||
tag: `task-updated-${taskId}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[WebPush] pushTaskUpdated error:', err);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user