feat: enhance soft delete functionality for fields to cascade delete items and their values if last field is removed

This commit is contained in:
2025-11-12 00:29:32 +01:00
parent a433dec4cf
commit c0ded946d8
2 changed files with 42 additions and 7 deletions
+28 -6
View File
@@ -54,16 +54,38 @@ router.put('/:id', async (req: Request, res: Response) => {
router.delete('/:id', async (req: Request, res: Response) => {
try {
const updatedAt = Date.now();
const result = await dbAdapter.execute(
// Soft delete the field first
const field = await dbAdapter.queryOne('SELECT * FROM fields WHERE id = ?', [req.params.id]);
if (!field) {
return res.status(404).json({ error: 'Field not found' });
}
await dbAdapter.execute(
'UPDATE fields SET isDeleted = 1, updatedAt = ? WHERE id = ?',
[updatedAt, req.params.id]
);
if (result.changes === 0) {
return res.status(404).json({ error: 'Field not found' });
// Remove any item_values referencing this field (already handled in sync route, but ensure consistency for direct REST usage)
await dbAdapter.execute('DELETE FROM item_values WHERE fieldId = ?', [req.params.id]);
// Determine if this was the last remaining (non-deleted) field in its list.
const listId = field.listId;
const remaining = await dbAdapter.queryOne(
'SELECT COUNT(*) as cnt FROM fields WHERE listId = ? AND isDeleted = 0',
[listId]
);
const remainingCount = remaining ? (remaining.cnt ?? remaining.CNT ?? remaining.count ?? 0) : 0;
if (remainingCount === 0) {
// Last field deleted -> soft delete all items and purge their values for this list
try {
await dbAdapter.execute('UPDATE items SET isDeleted = 1, updatedAt = ? WHERE listId = ?', [updatedAt, listId]);
await dbAdapter.execute('DELETE FROM item_values WHERE itemId IN (SELECT id FROM items WHERE listId = ?)', [listId]);
} catch (cascadeErr) {
console.warn('[FIELD DELETE] Cascade item cleanup failed:', cascadeErr);
}
}
res.json({ message: 'Field deleted successfully' });
res.json({ message: 'Field deleted successfully', cascadeItemsDeleted: remainingCount === 0 });
} catch (error) {
res.status(500).json({ error: 'Failed to delete field' });
}
+14 -1
View File
@@ -151,13 +151,26 @@ router.post('/sync', async (req: Request, res: Response) => {
field.updatedAt,
field.isDeleted ? 1 : 0
]);
// If a field was deleted, remove any item_values referencing it so clients converge
if (field.isDeleted) {
// Remove item_values for this field
try {
await tx.execute('DELETE FROM item_values WHERE fieldId = ?', [field.id]);
} catch (err) {
console.warn('[SYNC] Cascade delete for field failed:', String(err));
}
// Check if this was the last remaining non-deleted field for the list
try {
const remaining = await tx.queryOne('SELECT COUNT(*) as cnt FROM fields WHERE listId = ? AND isDeleted = 0', [field.listId]);
const remainingCount = remaining ? (remaining.cnt ?? remaining.CNT ?? remaining.count ?? 0) : 0;
if (remainingCount === 0) {
// Soft delete items in this list and purge their values
await tx.execute('UPDATE items SET isDeleted = 1, updatedAt = ? WHERE listId = ?', [field.updatedAt, field.listId]);
await tx.execute('DELETE FROM item_values WHERE itemId IN (SELECT id FROM items WHERE listId = ?)', [field.listId]);
console.log('[SYNC] Last field deleted; cascaded item+value cleanup for list', field.listId);
}
} catch (cascadeErr) {
console.warn('[SYNC] Cascade last-field cleanup failed:', cascadeErr);
}
}
}
}