Implement Phase 2, Part 2

This commit is contained in:
gpt-engineer-app[bot]
2025-10-06 15:43:50 +00:00
parent a3928c7f23
commit badf3507de
4 changed files with 557 additions and 0 deletions

View File

@@ -166,8 +166,17 @@ export async function approveSubmissionItems(
for (const item of sortedItems) {
let entityId: string | null = null;
let isEdit = false;
try {
// Determine if this is an edit by checking for entity_id in item_data
isEdit = !!(
item.item_data.park_id ||
item.item_data.ride_id ||
item.item_data.company_id ||
item.item_data.ride_model_id
);
// Create the entity based on type with dependency resolution
switch (item.item_type) {
case 'park':
@@ -200,6 +209,17 @@ export async function approveSubmissionItems(
approved_entity_id: entityId,
});
// Create version history (skip for photo type)
if (item.item_type !== 'photo') {
await createVersionForApprovedItem(
item.item_type,
entityId,
userId,
item.submission_id,
isEdit
);
}
// Add to dependency map for child items
dependencyMap.set(item.id, entityId);
@@ -217,6 +237,62 @@ export async function approveSubmissionItems(
}
}
/**
* Create version history for approved submission item
*/
async function createVersionForApprovedItem(
itemType: string,
entityId: string,
userId: string,
submissionId: string,
isEdit: boolean
): Promise<void> {
const { captureCurrentState, createEntityVersion } = await import('./versioningHelpers');
// Map item_type to entity_type
let entityType: 'park' | 'ride' | 'company' | 'ride_model';
switch (itemType) {
case 'park':
entityType = 'park';
break;
case 'ride':
entityType = 'ride';
break;
case 'manufacturer':
case 'operator':
case 'property_owner':
case 'designer':
entityType = 'company';
break;
case 'ride_model':
entityType = 'ride_model';
break;
default:
console.warn(`Unknown entity type for versioning: ${itemType}`);
return;
}
// Capture current state
const currentState = await captureCurrentState(entityType, entityId);
if (!currentState) {
console.warn(`Failed to capture state for ${entityType} ${entityId}`);
return;
}
// Create version
await createEntityVersion({
entityType,
entityId,
versionData: currentState,
changedBy: userId,
changeReason: isEdit
? `Approved edit from submission #${submissionId.slice(0, 8)}`
: `Created via submission #${submissionId.slice(0, 8)}`,
submissionId,
changeType: isEdit ? 'updated' : 'created',
});
}
/**
* Topological sort for dependency-ordered processing
*/

View File

@@ -0,0 +1,207 @@
import { supabase } from '@/integrations/supabase/client';
import { toast } from '@/hooks/use-toast';
export type EntityType = 'park' | 'ride' | 'company' | 'ride_model';
export type ChangeType = 'created' | 'updated' | 'deleted' | 'restored' | 'archived';
/**
* Get the table name for a given entity type
*/
export function getEntityTableName(entityType: EntityType): string {
const tableMap: Record<EntityType, string> = {
park: 'parks',
ride: 'rides',
company: 'companies',
ride_model: 'ride_models',
};
return tableMap[entityType];
}
/**
* Capture the current state of an entity
*/
export async function captureCurrentState(
entityType: EntityType,
entityId: string
): Promise<Record<string, any> | null> {
const tableName = getEntityTableName(entityType);
const { data, error } = await supabase
.from(tableName as any)
.select('*')
.eq('id', entityId)
.single();
if (error) {
console.error('Error capturing entity state:', error);
return null;
}
return data as Record<string, any>;
}
/**
* Create a new entity version with proper error handling
*/
export async function createEntityVersion(params: {
entityType: EntityType;
entityId: string;
versionData: Record<string, any>;
changedBy: string;
changeReason?: string;
submissionId?: string;
changeType?: ChangeType;
}): Promise<string | null> {
const {
entityType,
entityId,
versionData,
changedBy,
changeReason,
submissionId,
changeType = 'updated',
} = params;
try {
const { data, error } = await supabase.rpc('create_entity_version', {
p_entity_type: entityType,
p_entity_id: entityId,
p_version_data: versionData,
p_changed_by: changedBy,
p_change_reason: changeReason || null,
p_submission_id: submissionId || null,
p_change_type: changeType,
});
if (error) {
console.error('Error creating entity version:', error);
toast({
title: 'Version Creation Failed',
description: error.message,
variant: 'destructive',
});
return null;
}
return data;
} catch (err) {
console.error('Unexpected error creating entity version:', err);
toast({
title: 'Version Creation Failed',
description: 'An unexpected error occurred',
variant: 'destructive',
});
return null;
}
}
/**
* Create entity version with audit log entry
*/
export async function createEntityVersionWithAudit(
params: {
entityType: EntityType;
entityId: string;
versionData: Record<string, any>;
changedBy: string;
changeReason?: string;
submissionId?: string;
changeType?: ChangeType;
},
auditDetails?: Record<string, any>
): Promise<string | null> {
const versionId = await createEntityVersion(params);
if (versionId && auditDetails) {
// Log to admin audit log
const { error: auditError } = await supabase.rpc('log_admin_action', {
_admin_user_id: params.changedBy,
_target_user_id: params.changedBy, // Or entity owner if available
_action: `version_${params.changeType || 'updated'}`,
_details: {
version_id: versionId,
entity_type: params.entityType,
entity_id: params.entityId,
submission_id: params.submissionId,
...auditDetails,
},
});
if (auditError) {
console.warn('Failed to create audit log entry:', auditError);
}
}
return versionId;
}
/**
* Rollback an entity to a previous version
*/
export async function rollbackToVersion(
entityType: EntityType,
entityId: string,
targetVersionId: string,
userId: string,
reason: string
): Promise<boolean> {
try {
const { data, error } = await supabase.rpc('rollback_to_version', {
p_entity_type: entityType,
p_entity_id: entityId,
p_target_version_id: targetVersionId,
p_changed_by: userId,
p_reason: reason,
});
if (error) {
console.error('Error rolling back to version:', error);
toast({
title: 'Rollback Failed',
description: error.message,
variant: 'destructive',
});
return false;
}
toast({
title: 'Rollback Successful',
description: 'Entity has been restored to the selected version',
});
return true;
} catch (err) {
console.error('Unexpected error during rollback:', err);
toast({
title: 'Rollback Failed',
description: 'An unexpected error occurred',
variant: 'destructive',
});
return false;
}
}
/**
* Compare two versions and get the diff
*/
export async function compareVersions(
fromVersionId: string,
toVersionId: string
): Promise<Record<string, any> | null> {
try {
const { data, error } = await supabase.rpc('compare_versions', {
p_from_version_id: fromVersionId,
p_to_version_id: toVersionId,
});
if (error) {
console.error('Error comparing versions:', error);
return null;
}
return data as Record<string, any>;
} catch (err) {
console.error('Unexpected error comparing versions:', err);
return null;
}
}