feat(folders): soft-delete folders and show in Recently Deleted#4001
feat(folders): soft-delete folders and show in Recently Deleted#4001waleedlatif1 merged 12 commits intostagingfrom
Conversation
Folders are now soft-deleted (archived) instead of permanently removed, matching the existing pattern for workflows, tables, and knowledge bases. Users can restore folders from Settings > Recently Deleted. - Add `archivedAt` column to `workflowFolder` schema with index - Change folder deletion to set `archivedAt` instead of hard-delete - Add folder restore endpoint (POST /api/folders/[id]/restore) - Batch-restore all workflows inside restored folders in one transaction - Add scope filter to GET /api/folders (active/archived) - Add Folders tab to Recently Deleted settings page - Update delete modal messaging for restorable items - Change "This action cannot be undone" styling to muted text Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Adds end-to-end folder restore support: a new Updates the UI and client queries so Recently Deleted can show/restore folders, adjusts folder query keys/invalidation for scoped lists, and tweaks confirmation dialogs to reflect archiving/restorability (with muted styling for "This action cannot be undone"). Reviewed by Cursor Bugbot for commit a80cc8b. Configure here. |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Greptile SummaryThis PR adds soft-delete (archive/restore) support for workflow folders, matching the existing pattern already in place for workflows, tables, knowledge bases, and files. Deleted folders and their contents are now moved to an Key implementation highlights:
Confidence Score: 5/5Safe to merge — no P0/P1 issues found; implementation is correct and fully atomic with proper transaction wrapping and exact-timestamp restore scoping. All remaining findings are P2 (a redundant
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant API as folders API
participant LC as folder-lifecycle.ts
participant DB
Note over User,DB: Soft-Delete (folder + contents)
User->>API: DELETE /api/folders/{id}
API->>LC: performDeleteFolder()
LC->>DB: countWorkflowsInFolderRecursively()
LC->>LC: deleteFolderRecursively(id, ws, timestamp T)
loop Each non-archived child folder
LC->>LC: deleteFolderRecursively(childId, ws, T)
end
LC->>DB: archiveWorkflowsByIdsInWorkspace(ids, archivedAt=T)
LC->>DB: UPDATE workflowFolder SET archivedAt=T
LC->>DB: recordAudit(FOLDER_DELETED)
API->>User: 200 OK
Note over User,DB: Restore flow
User->>API: POST /api/folders/{id}/restore
API->>LC: performRestoreFolder()
LC->>DB: SELECT folder WHERE id AND workspaceId
LC->>DB: BEGIN TRANSACTION
alt parent folder is archived
LC->>DB: UPDATE workflowFolder SET parentId=null
end
LC->>LC: restoreFolderRecursively(id, ws, folderArchivedAt=T, tx)
LC->>DB: UPDATE workflowFolder SET archivedAt=null
LC->>DB: SELECT workflows WHERE archivedAt=T (exact match)
LC->>DB: UPDATE workflow+schedule+webhook+chat+form+mcp+a2a SET archivedAt=null
loop Each child folder WHERE archivedAt=T
LC->>LC: restoreFolderRecursively(childId, ws, T, tx)
end
LC->>DB: COMMIT
LC->>DB: recordAudit(FOLDER_RESTORED)
API->>User: 200 {success, restoredItems}
Reviews (5): Last reviewed commit: "fix(folders): handle missing parent fold..." | Re-trigger Greptile |
…workflows Address two review findings: - Wrap entire folder restore in a single DB transaction to prevent partial state if any step fails - Only restore workflows archived within 5s of the folder's archivedAt, so individually-deleted workflows are not silently un-deleted - Add folder_restored to PostHog event map Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The 5-second time window for scoping which workflows to restore was a fragile heuristic (magic number, race-prone, non-deterministic). Restoring a folder now restores all archived workflows in it, matching standard trash/recycle-bin behavior. Users can re-delete any workflow they don't want after restore. The single-transaction wrapping from the prior commit is kept — that was a legitimate atomicity fix. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace manually created migration with proper drizzle-kit generated one that includes the snapshot file, fixing CI schema sync check. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@greptile |
|
@cursor review |
…mestamp Use a single timestamp across the entire folder deletion — folders, workflows, schedules, webhooks, etc. all get the exact same archivedAt. On restore, match workflows by exact archivedAt equality with the folder's timestamp, so individually-deleted workflows are not silently un-deleted. - Add optional archivedAt to ArchiveWorkflowOptions (backwards-compatible) - Pass shared timestamp through deleteFolderRecursively → archiveWorkflowsByIdsInWorkspace - Filter restore with eq(workflow.archivedAt, folderArchivedAt) instead of isNotNull Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…missing When individually restoring a workflow from Recently Deleted, check if its folder still exists and is active. If the folder is archived or missing, clear folderId so the workflow appears at root instead of being orphaned (invisible in sidebar). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@cursor review |
|
@greptile |
Three issues caught by audit: 1. Child folder restore used isNotNull instead of timestamp matching, so individually-deleted child folders would be incorrectly restored. Now uses eq(archivedAt, folderArchivedAt) for both workflows AND child folders — consistent and deterministic. 2. No workspace archived check — could restore a folder into an archived workspace. Now checks getWorkspaceWithOwner, matching the existing restoreWorkflow pattern. 3. Re-restoring an already-restored folder returned an error. Now returns success with zero counts (idempotent). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ensures optimistic folder objects include archivedAt: null for consistency with the database schema shape. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@greptile |
|
@cursor review |
If the parent folder row no longer exists (not just archived), the restored folder now correctly gets reparented to root instead of retaining a dangling parentId reference. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@cursor review |
|
@greptile |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit a80cc8b. Configure here.
Summary
Changes
archivedAtcolumn + index toworkflowFoldertablearchivedAttimestamp instead of hard-delete; scope filter on GET/api/folders; new POST/api/folders/[id]/restoreendpoint with batch workflow restorationuseRestoreFoldermutation hookTest plan