Add schema reconciliation migration and file removal in bulk upload
Migration: - Add standalone hasConflict index on ConflictOfInterest - Ensure roundId is nullable on ConflictOfInterest - Drop stale composite roundId_hasConflict index Bulk upload: - Add trash icon button to remove uploaded files - Uses existing file.delete endpoint with audit logging Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
85a0fa5016
commit
5e0c8b2dfe
|
|
@ -0,0 +1,19 @@
|
|||
-- =============================================================================
|
||||
-- Schema Reconciliation: Fill remaining gaps between migrations and schema.prisma
|
||||
-- =============================================================================
|
||||
-- All statements are idempotent (safe to re-run on any database state).
|
||||
|
||||
-- 1. ConflictOfInterest: add standalone hasConflict index (schema has @@index([hasConflict]))
|
||||
-- Migration 20260205223133 only created composite (roundId, hasConflict) index.
|
||||
CREATE INDEX IF NOT EXISTS "ConflictOfInterest_hasConflict_idx" ON "ConflictOfInterest"("hasConflict");
|
||||
|
||||
-- 2. Ensure ConflictOfInterest.roundId is nullable (schema says String?)
|
||||
-- Pipeline migration (20260213) makes it nullable, but guard for safety.
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "ConflictOfInterest" ALTER COLUMN "roundId" DROP NOT NULL;
|
||||
EXCEPTION WHEN others THEN NULL;
|
||||
END $$;
|
||||
|
||||
-- 3. Drop stale composite index that no longer matches schema
|
||||
-- Schema only has @@index([hasConflict]) and @@index([userId]), not (roundId, hasConflict).
|
||||
DROP INDEX IF EXISTS "ConflictOfInterest_roundId_hasConflict_idx";
|
||||
|
|
@ -47,6 +47,7 @@ import {
|
|||
FileUp,
|
||||
AlertCircle,
|
||||
ExternalLink,
|
||||
Trash2,
|
||||
} from 'lucide-react'
|
||||
import { cn, formatFileSize } from '@/lib/utils'
|
||||
import { Pagination } from '@/components/shared/pagination'
|
||||
|
|
@ -155,6 +156,26 @@ export default function BulkUploadPage() {
|
|||
[utils, refetch]
|
||||
)
|
||||
|
||||
// Delete a file
|
||||
const deleteMutation = trpc.file.delete.useMutation({
|
||||
onSuccess: () => {
|
||||
toast.success('File removed')
|
||||
refetch()
|
||||
},
|
||||
onError: (err) => {
|
||||
toast.error(`Failed to remove file: ${err.message}`)
|
||||
},
|
||||
})
|
||||
|
||||
const handleDeleteFile = useCallback(
|
||||
(fileId: string) => {
|
||||
if (confirm('Remove this uploaded file?')) {
|
||||
deleteMutation.mutate({ id: fileId })
|
||||
}
|
||||
},
|
||||
[deleteMutation]
|
||||
)
|
||||
|
||||
const uploadMutation = trpc.file.adminUploadForRoundRequirement.useMutation()
|
||||
|
||||
// Upload a single file for a project requirement
|
||||
|
|
@ -513,7 +534,20 @@ export default function BulkUploadPage() {
|
|||
</div>
|
||||
) : req.file || uploadState?.status === 'complete' ? (
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<CheckCircle2 className="h-4 w-4 text-green-600" />
|
||||
<div className="flex items-center gap-1">
|
||||
<CheckCircle2 className="h-4 w-4 text-green-600" />
|
||||
{req.file && (
|
||||
<button
|
||||
type="button"
|
||||
className="text-muted-foreground hover:text-destructive transition-colors cursor-pointer"
|
||||
title="Remove file"
|
||||
onClick={() => handleDeleteFile(req.file!.id)}
|
||||
disabled={deleteMutation.isPending}
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{req.file?.bucket && req.file?.objectKey ? (
|
||||
<button
|
||||
type="button"
|
||||
|
|
|
|||
Loading…
Reference in New Issue