mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 06:11:13 -05:00
Implement planned features
This commit is contained in:
@@ -13,8 +13,10 @@ import {
|
||||
approveSubmissionItems,
|
||||
rejectSubmissionItems,
|
||||
escalateSubmission,
|
||||
checkSubmissionConflict,
|
||||
type SubmissionItemWithDeps,
|
||||
type DependencyConflict
|
||||
type DependencyConflict,
|
||||
type ConflictCheckResult
|
||||
} from '@/lib/submissionItemsService';
|
||||
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription } from '@/components/ui/sheet';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog';
|
||||
@@ -22,7 +24,7 @@ import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { AlertCircle, CheckCircle2, XCircle, Edit, Network, ArrowUp } from 'lucide-react';
|
||||
import { AlertCircle, CheckCircle2, XCircle, Edit, Network, ArrowUp, History } from 'lucide-react';
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { useIsMobile } from '@/hooks/use-mobile';
|
||||
@@ -34,6 +36,8 @@ import { RejectionDialog } from './RejectionDialog';
|
||||
import { ItemEditDialog } from './ItemEditDialog';
|
||||
import { ValidationBlockerDialog } from './ValidationBlockerDialog';
|
||||
import { WarningConfirmDialog } from './WarningConfirmDialog';
|
||||
import { ConflictResolutionModal } from './ConflictResolutionModal';
|
||||
import { EditHistoryAccordion } from './EditHistoryAccordion';
|
||||
import { validateMultipleItems, ValidationResult } from '@/lib/entityValidationSchemas';
|
||||
import { logger } from '@/lib/logger';
|
||||
|
||||
@@ -70,6 +74,9 @@ export function SubmissionReviewManager({
|
||||
const [userConfirmedWarnings, setUserConfirmedWarnings] = useState(false);
|
||||
const [hasBlockingErrors, setHasBlockingErrors] = useState(false);
|
||||
const [globalValidationKey, setGlobalValidationKey] = useState(0);
|
||||
const [conflictData, setConflictData] = useState<ConflictCheckResult | null>(null);
|
||||
const [showConflictResolutionModal, setShowConflictResolutionModal] = useState(false);
|
||||
const [lastModifiedTimestamp, setLastModifiedTimestamp] = useState<string | null>(null);
|
||||
|
||||
const { toast } = useToast();
|
||||
const { isAdmin, isSuperuser } = useUserRole();
|
||||
@@ -113,15 +120,16 @@ export function SubmissionReviewManager({
|
||||
try {
|
||||
const { supabase } = await import('@/integrations/supabase/client');
|
||||
|
||||
// Fetch submission type
|
||||
// Fetch submission type and last_modified_at
|
||||
const { data: submission } = await supabase
|
||||
.from('content_submissions')
|
||||
.select('submission_type')
|
||||
.select('submission_type, last_modified_at')
|
||||
.eq('id', submissionId)
|
||||
.single();
|
||||
|
||||
if (submission) {
|
||||
setSubmissionType(submission.submission_type || 'submission');
|
||||
setLastModifiedTimestamp(submission.last_modified_at);
|
||||
}
|
||||
|
||||
const fetchedItems = await fetchSubmissionItems(submissionId);
|
||||
@@ -211,6 +219,18 @@ export function SubmissionReviewManager({
|
||||
dispatch({ type: 'START_APPROVAL' });
|
||||
|
||||
try {
|
||||
// Check for conflicts first (optimistic locking)
|
||||
if (lastModifiedTimestamp) {
|
||||
const conflictCheck = await checkSubmissionConflict(submissionId, lastModifiedTimestamp);
|
||||
|
||||
if (conflictCheck.hasConflict) {
|
||||
setConflictData(conflictCheck);
|
||||
setShowConflictResolutionModal(true);
|
||||
dispatch({ type: 'RESET' }); // Return to reviewing state
|
||||
return; // Block approval until conflict resolved
|
||||
}
|
||||
}
|
||||
|
||||
// Run validation on all selected items
|
||||
const validationResultsMap = await validateMultipleItems(
|
||||
selectedItems.map(item => ({
|
||||
@@ -603,6 +623,43 @@ export function SubmissionReviewManager({
|
||||
i.item_data?.name || i.item_type.replace('_', ' ')
|
||||
)}
|
||||
/>
|
||||
|
||||
<ConflictResolutionModal
|
||||
open={showConflictResolutionModal}
|
||||
onOpenChange={setShowConflictResolutionModal}
|
||||
conflictData={conflictData}
|
||||
onResolve={async (strategy) => {
|
||||
if (strategy === 'keep-mine') {
|
||||
// Log conflict resolution
|
||||
const { supabase } = await import('@/integrations/supabase/client');
|
||||
await supabase.from('conflict_resolutions').insert([{
|
||||
submission_id: submissionId,
|
||||
resolved_by: user?.id || null,
|
||||
resolution_strategy: strategy,
|
||||
conflict_details: conflictData as any,
|
||||
}]);
|
||||
|
||||
// Force override and proceed with approval
|
||||
await handleApprove();
|
||||
} else if (strategy === 'keep-theirs') {
|
||||
// Reload data and discard local changes
|
||||
await loadSubmissionItems();
|
||||
toast({
|
||||
title: 'Changes Discarded',
|
||||
description: 'Loaded the latest version from the server',
|
||||
});
|
||||
} else if (strategy === 'reload') {
|
||||
// Just reload without approving
|
||||
await loadSubmissionItems();
|
||||
toast({
|
||||
title: 'Reloaded',
|
||||
description: 'Viewing the latest version',
|
||||
});
|
||||
}
|
||||
setShowConflictResolutionModal(false);
|
||||
setConflictData(null);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -724,13 +781,13 @@ export function SubmissionReviewManager({
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onValueChange={(v) => {
|
||||
if (v === 'items' || v === 'dependencies') {
|
||||
setActiveTab(v);
|
||||
if (v === 'items' || v === 'dependencies' || v === 'history') {
|
||||
setActiveTab(v as 'items' | 'dependencies');
|
||||
}
|
||||
}}
|
||||
className="flex-1 flex flex-col"
|
||||
>
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsList className="grid w-full grid-cols-3">
|
||||
<TabsTrigger value="items">
|
||||
<CheckCircle2 className="w-4 h-4 mr-2" />
|
||||
Items ({items.length})
|
||||
@@ -739,6 +796,10 @@ export function SubmissionReviewManager({
|
||||
<Network className="w-4 h-4 mr-2" />
|
||||
Dependencies
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="history">
|
||||
<History className="w-4 h-4 mr-2" />
|
||||
History
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="items" className="flex-1 overflow-hidden">
|
||||
@@ -778,6 +839,12 @@ export function SubmissionReviewManager({
|
||||
<TabsContent value="dependencies" className="flex-1 overflow-hidden">
|
||||
<DependencyVisualizer items={items} selectedIds={selectedItemIds} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="history" className="flex-1 overflow-hidden">
|
||||
<ScrollArea className="h-full pr-4">
|
||||
<EditHistoryAccordion submissionId={submissionId} />
|
||||
</ScrollArea>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
{/* Blocking error alert */}
|
||||
|
||||
Reference in New Issue
Block a user