mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 16:11:12 -05:00
feat: Add manual re-validation button
This commit is contained in:
@@ -20,6 +20,7 @@ interface ItemReviewCardProps {
|
|||||||
export function ItemReviewCard({ item, onEdit, onStatusChange, submissionId }: ItemReviewCardProps) {
|
export function ItemReviewCard({ item, onEdit, onStatusChange, submissionId }: ItemReviewCardProps) {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const [validationResult, setValidationResult] = useState<ValidationResult | null>(null);
|
const [validationResult, setValidationResult] = useState<ValidationResult | null>(null);
|
||||||
|
const [validationKey, setValidationKey] = useState(0);
|
||||||
|
|
||||||
const handleValidationChange = useCallback((result: ValidationResult) => {
|
const handleValidationChange = useCallback((result: ValidationResult) => {
|
||||||
setValidationResult(result);
|
setValidationResult(result);
|
||||||
@@ -131,6 +132,7 @@ export function ItemReviewCard({ item, onEdit, onStatusChange, submissionId }: I
|
|||||||
}}
|
}}
|
||||||
onValidationChange={handleValidationChange}
|
onValidationChange={handleValidationChange}
|
||||||
compact={false}
|
compact={false}
|
||||||
|
validationKey={validationKey}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ export function SubmissionReviewManager({
|
|||||||
const [validationResults, setValidationResults] = useState<Map<string, ValidationResult>>(new Map());
|
const [validationResults, setValidationResults] = useState<Map<string, ValidationResult>>(new Map());
|
||||||
const [userConfirmedWarnings, setUserConfirmedWarnings] = useState(false);
|
const [userConfirmedWarnings, setUserConfirmedWarnings] = useState(false);
|
||||||
const [hasBlockingErrors, setHasBlockingErrors] = useState(false);
|
const [hasBlockingErrors, setHasBlockingErrors] = useState(false);
|
||||||
|
const [globalValidationKey, setGlobalValidationKey] = useState(0);
|
||||||
|
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const { isAdmin, isSuperuser } = useUserRole();
|
const { isAdmin, isSuperuser } = useUserRole();
|
||||||
@@ -374,6 +375,7 @@ export function SubmissionReviewManager({
|
|||||||
setShowEditDialog(false);
|
setShowEditDialog(false);
|
||||||
setEditingItem(null);
|
setEditingItem(null);
|
||||||
await loadSubmissionItems();
|
await loadSubmissionItems();
|
||||||
|
setGlobalValidationKey(prev => prev + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleItemStatusChange = async (itemId: string, status: 'approved' | 'rejected') => {
|
const handleItemStatusChange = async (itemId: string, status: 'approved' | 'rejected') => {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useState, useMemo } from 'react';
|
import { useEffect, useState, useMemo } from 'react';
|
||||||
import { AlertCircle, CheckCircle, Info, AlertTriangle } from 'lucide-react';
|
import { AlertCircle, CheckCircle, Info, AlertTriangle, RefreshCw } from 'lucide-react';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
||||||
import { validateEntityData, ValidationResult } from '@/lib/entityValidationSchemas';
|
import { validateEntityData, ValidationResult } from '@/lib/entityValidationSchemas';
|
||||||
@@ -13,12 +14,14 @@ interface ValidationSummaryProps {
|
|||||||
};
|
};
|
||||||
onValidationChange?: (result: ValidationResult) => void;
|
onValidationChange?: (result: ValidationResult) => void;
|
||||||
compact?: boolean;
|
compact?: boolean;
|
||||||
|
validationKey?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ValidationSummary({ item, onValidationChange, compact = false }: ValidationSummaryProps) {
|
export function ValidationSummary({ item, onValidationChange, compact = false, validationKey }: ValidationSummaryProps) {
|
||||||
const [validationResult, setValidationResult] = useState<ValidationResult | null>(null);
|
const [validationResult, setValidationResult] = useState<ValidationResult | null>(null);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [isExpanded, setIsExpanded] = useState(false);
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
|
const [manualTriggerCount, setManualTriggerCount] = useState(0);
|
||||||
|
|
||||||
// Create stable reference for item_data to prevent unnecessary re-validations
|
// Create stable reference for item_data to prevent unnecessary re-validations
|
||||||
const itemDataString = useMemo(
|
const itemDataString = useMemo(
|
||||||
@@ -68,7 +71,7 @@ export function ValidationSummary({ item, onValidationChange, compact = false }:
|
|||||||
}
|
}
|
||||||
|
|
||||||
validate();
|
validate();
|
||||||
}, [item.item_type, itemDataString, item.id]);
|
}, [item.item_type, itemDataString, item.id, validationKey, manualTriggerCount]);
|
||||||
|
|
||||||
// Auto-expand when there are blocking errors or warnings
|
// Auto-expand when there are blocking errors or warnings
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -158,7 +161,7 @@ export function ValidationSummary({ item, onValidationChange, compact = false }:
|
|||||||
return (
|
return (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{/* Summary Badge */}
|
{/* Summary Badge */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
{validationResult.isValid && !hasWarnings && !hasSuggestions && (
|
{validationResult.isValid && !hasWarnings && !hasSuggestions && (
|
||||||
<Badge variant="secondary" className="bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-300 border-green-300 dark:border-green-700">
|
<Badge variant="secondary" className="bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-300 border-green-300 dark:border-green-700">
|
||||||
<CheckCircle className="w-4 h-4 mr-1" />
|
<CheckCircle className="w-4 h-4 mr-1" />
|
||||||
@@ -183,6 +186,16 @@ export function ValidationSummary({ item, onValidationChange, compact = false }:
|
|||||||
{validationResult.suggestions.length} Suggestion{validationResult.suggestions.length !== 1 ? 's' : ''}
|
{validationResult.suggestions.length} Suggestion{validationResult.suggestions.length !== 1 ? 's' : ''}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setManualTriggerCount(prev => prev + 1)}
|
||||||
|
className="text-xs h-7"
|
||||||
|
>
|
||||||
|
<RefreshCw className="w-3 h-3 mr-1" />
|
||||||
|
Re-validate
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Detailed Issues */}
|
{/* Detailed Issues */}
|
||||||
|
|||||||
Reference in New Issue
Block a user