LinkDesk/frontend/docs/permanent-delete-workflow-i...

8.9 KiB

Permanent Delete Workflow Implementation

Overview

This document describes the implementation of the permanent delete confirmation workflow for the Recovery Management feature (Task 9).

Requirements

Task 9 requires the following functionality:

  1. Wire up confirmation dialog to permanent delete actions
  2. Implement confirmation token validation
  3. Add loading states during permanent deletion
  4. Handle success and error responses appropriately
  5. Update UI state after successful permanent deletion

Implementation Details

1. Confirmation Dialog Integration

The permanent delete workflow is initiated from the DeletedItemsManagementView.vue component:

Single Item Deletion

const handlePermanentDelete = (type: 'shot' | 'asset', item: DeletedShot | DeletedAsset) => {
  const deleteItem = {
    id: item.id,
    name: item.name,
    type,
    project_name: item.project_name,
    episode_name: 'episode_name' in item ? item.episode_name : undefined,
    task_count: item.task_count,
    submission_count: item.submission_count,
    attachment_count: item.attachment_count,
    note_count: item.note_count,
    review_count: item.review_count
  }
  
  itemsToDelete.value = [deleteItem]
  permanentDeleteType.value = 'single'
  showPermanentDeleteDialog.value = true
}

Bulk Deletion

const handleBulkPermanentDelete = () => {
  const deleteItems = selectedItems.value.map(item => {
    // Maps selected items to the required format
  }).filter((item): item is NonNullable<typeof item> => item !== null)
  
  itemsToDelete.value = deleteItems
  permanentDeleteType.value = 'bulk'
  showPermanentDeleteDialog.value = true
}

2. Confirmation Token Validation

The PermanentDeleteConfirmDialog.vue component implements a secure confirmation workflow:

Token Generation

const generateConfirmationToken = (): string => {
  if (isBulkOperation.value) {
    const shotCount = props.items.filter(item => item.type === 'shot').length
    const assetCount = props.items.filter(item => item.type === 'asset').length
    
    if (shotCount > 0 && assetCount > 0) {
      return 'CONFIRM_MIXED_BULK_PERMANENT_DELETE'
    } else if (shotCount > 0) {
      return 'CONFIRM_BULK_SHOTS_PERMANENT_DELETE'
    } else {
      return 'CONFIRM_BULK_ASSETS_PERMANENT_DELETE'
    }
  } else {
    const item = props.items[0]
    if (item.type === 'shot') {
      return 'CONFIRM_SHOT_PERMANENT_DELETE'
    } else {
      return 'CONFIRM_ASSET_PERMANENT_DELETE'
    }
  }
}

User Confirmation

  • User must type the exact confirmation phrase (e.g., "DELETE Shot_001" or "DELETE 5 ITEMS")
  • Paste is disabled to prevent accidental confirmations
  • Delete button is disabled until confirmation phrase matches exactly

3. Loading States

Loading states are managed throughout the deletion process:

// State management
const isPermanentDeleting = ref(false)

// In the dialog component
<Button
  variant="destructive"
  @click="handleDelete"
  :disabled="!isConfirmed || isDeleting || isLoadingInfo || !!loadError"
>
  <Loader2 v-if="isDeleting" class="mr-2 h-4 w-4 animate-spin" />
  <Trash2 v-else class="mr-2 h-4 w-4" />
  {{ isDeleting ? 'Deleting...' : 'Permanently Delete' }}
</Button>

4. Success and Error Response Handling

The executePermanentDelete function handles all response scenarios:

const executePermanentDelete = async (confirmationToken: string) => {
  try {
    isPermanentDeleting.value = true

    if (permanentDeleteType.value === 'single') {
      const item = itemsToDelete.value[0]
      let result
      
      if (item.type === 'shot') {
        result = await recoveryService.permanentDeleteShot(item.id, confirmationToken)
      } else {
        result = await recoveryService.permanentDeleteAsset(item.id, confirmationToken)
      }

      toast({
        title: 'Permanent Deletion Successful',
        description: `${result.name} and all related data have been permanently deleted`,
      })

      // Update UI state (see section 5)
    } else {
      // Handle bulk deletion with detailed feedback
      // Shows success count and any errors
    }

    showPermanentDeleteDialog.value = false
    itemsToDelete.value = []
  } catch (err: any) {
    toast({
      title: 'Permanent Deletion Failed',
      description: err.response?.data?.detail || 'Failed to permanently delete items',
      variant: 'destructive'
    })
  } finally {
    isPermanentDeleting.value = false
  }
}

5. UI State Updates

After successful deletion, the UI is updated to reflect the changes:

// Remove from lists
if (item.type === 'shot') {
  deletedShots.value = deletedShots.value.filter(s => s.id !== item.id)
  selectedItems.value = selectedItems.value.filter(selected => 
    !(selected.type === 'shot' && selected.id === item.id))
} else {
  deletedAssets.value = deletedAssets.value.filter(a => a.id !== item.id)
  selectedItems.value = selectedItems.value.filter(selected => 
    !(selected.type === 'asset' && selected.id === item.id))
}

// Close dialog and clear state
showPermanentDeleteDialog.value = false
itemsToDelete.value = []

Backend Implementation

API Endpoints

The backend provides the following endpoints:

  1. Single Shot Deletion: DELETE /admin/shots/{shot_id}/permanent
  2. Single Asset Deletion: DELETE /admin/assets/{asset_id}/permanent
  3. Bulk Shot Deletion: DELETE /admin/shots/bulk-permanent
  4. Bulk Asset Deletion: DELETE /admin/assets/bulk-permanent

Security Features

  1. Rate Limiting: Maximum 10 permanent delete operations per minute per user
  2. Token Validation: Backend validates confirmation tokens before proceeding
  3. Transaction Safety: All deletions are performed within database transactions
  4. Rollback on Failure: If any part of the deletion fails, all changes are rolled back
  5. Audit Logging: All permanent deletions are logged for audit purposes

Cascading Deletion

The backend ensures complete data removal:

  • Tasks associated with the shot/asset
  • Submissions and their files
  • Attachments and their files
  • Production notes
  • Reviews
  • Activity records
  • File system cleanup

Frontend Service Layer

The recovery.ts service provides type-safe methods:

async permanentDeleteShot(shotId: number, confirmationToken: string): Promise<PermanentDeleteResult>
async permanentDeleteAsset(assetId: number, confirmationToken: string): Promise<PermanentDeleteResult>
async bulkPermanentDeleteShots(shotIds: number[], confirmationToken: string): Promise<BulkPermanentDeleteResult>
async bulkPermanentDeleteAssets(assetIds: number[], confirmationToken: string): Promise<BulkPermanentDeleteResult>

User Experience Flow

  1. User navigates to Recovery Management page
  2. User selects item(s) to permanently delete
  3. User clicks "Permanent Delete" button
  4. Confirmation dialog appears with:
    • Warning about irreversible action
    • List of items to be deleted
    • Impact summary (tasks, files, etc.)
    • Confirmation phrase requirement
  5. User types exact confirmation phrase
  6. Delete button becomes enabled
  7. User clicks "Permanently Delete"
  8. Loading state shows during deletion
  9. Success/error toast notification appears
  10. UI updates to remove deleted items
  11. Dialog closes automatically

Testing Recommendations

To verify the implementation:

  1. Single Item Deletion

    • Test shot deletion with correct confirmation
    • Test asset deletion with correct confirmation
    • Verify UI updates after deletion
  2. Bulk Deletion

    • Test bulk deletion of shots only
    • Test bulk deletion of assets only
    • Test bulk deletion of mixed items
  3. Error Scenarios

    • Test with invalid confirmation phrase
    • Test network error handling
    • Test rate limit enforcement
  4. UI States

    • Verify loading states during deletion
    • Verify disabled states during operation
    • Verify toast notifications

Compliance with Requirements

Requirement 6.3: Confirmation dialog with data loss warnings - Implemented Requirement 6.5: Success messages and UI updates - Implemented Requirement 8.1: Immediate data removal - Implemented Requirement 8.4: Rollback on failure - Implemented

Files Modified

Frontend

  • frontend/src/views/admin/DeletedItemsManagementView.vue - Main view with workflow handlers
  • frontend/src/components/admin/PermanentDeleteConfirmDialog.vue - Confirmation dialog
  • frontend/src/services/recovery.ts - Service layer methods

Backend

  • backend/routers/admin.py - API endpoints
  • backend/services/recovery_service.py - Business logic

Conclusion

Task 9 has been successfully implemented with all required functionality:

  • Confirmation dialog integration
  • Token validation
  • Loading states
  • Success/error handling
  • UI state updates

The implementation provides a secure, user-friendly workflow for permanent deletion with proper safeguards and feedback mechanisms.