feat(fields): мягкое удаление полей формы — архив удалённых с восстановлением и блок «Удалённые поля» в задаче

This commit is contained in:
2026-09-21 14:35:45 +03:00
parent d5f2925886
commit e3f0ebfb89
18 changed files with 296 additions and 48 deletions

View File

@@ -121,25 +121,8 @@ export function registerFormStructureRoutes(router: ReturnType<typeof import("ex
const incomingIds = fields.filter((f: Record<string, unknown>) => f.id).map((f: Record<string, unknown>) => f.id as number);
const fieldsToDelete = existingFields.filter(existing => !incomingIds.includes(existing.id));
if (fieldsToDelete.length > 0) {
const tasks = await storage.getTasksByForm(formId, req.organizationId!);
const usedFieldIds = new Set();
for (const task of tasks) {
const fieldValues = await storage.getTaskFieldValues(task.id, req.organizationId!);
for (const value of fieldValues) {
usedFieldIds.add(value.fieldId);
}
}
for (const field of fieldsToDelete) {
if (usedFieldIds.has(field.id)) {
return res.status(400).json({
success: false,
error: `Невозможно удалить поле "${field.name}" - оно используется в существующих задачах. Сначала удалите значения этого поля во всех задачах.`
});
}
}
}
// Мягкое удаление: значения в задачах сохраняются (поле уходит в архив),
// поэтому раньше существовавшая блокировка «поле используется в задачах» снята.
const fieldIdsToDelete = fieldsToDelete.map(f => f.id);
const normalizedFields = fields.map((f: Record<string, unknown>) => ({
...f,
@@ -171,4 +154,42 @@ export function registerFormStructureRoutes(router: ReturnType<typeof import("ex
}
}
);
// Архив удалённых (мягко) полей формы
router.get('/api/forms/:id/fields/deleted',
requireFormAdminOrPermission('forms.manage'),
async (req: AuthenticatedRequest, res) => {
try {
const formId = parseInt(req.params.id);
if (isNaN(formId)) {
return res.status(400).json({ success: false, error: 'Неверный ID формы' });
}
const fields = await storage.getDeletedFormFields(formId, req.organizationId!);
res.json({ success: true, fields });
} catch (error) {
console.error('Get deleted fields error:', error);
res.status(500).json({ success: false, error: 'Ошибка при загрузке удалённых полей' });
}
}
);
// Восстановление удалённого поля (значения в задачах сохранились и вернутся сами)
router.post('/api/forms/:id/fields/:fieldId/restore',
requireFormAdminOrPermission('forms.manage'),
async (req: AuthenticatedRequest, res) => {
try {
const formId = parseInt(req.params.id);
const fieldId = parseInt(req.params.fieldId);
if (isNaN(formId) || isNaN(fieldId)) {
return res.status(400).json({ success: false, error: 'Неверный ID формы или поля' });
}
const restored = await storage.restoreFormField(fieldId, formId, req.organizationId!);
formsCache.invalidatePrefix(`forms:${req.organizationId}`);
res.json({ success: true, field: restored });
} catch (error) {
console.error('Restore field error:', error);
res.status(400).json({ success: false, error: error instanceof Error ? error.message : 'Ошибка при восстановлении поля' });
}
}
);
}