mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-28 11:26:58 -05:00
Compare commits
5 Commits
090f6aca48
...
d00ea2a3ee
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d00ea2a3ee | ||
|
|
5c24038470 | ||
|
|
93e8e98957 | ||
|
|
c8a015a15b | ||
|
|
93e48ac457 |
@@ -93,14 +93,14 @@ interface ParkFormProps {
|
||||
}
|
||||
|
||||
const parkTypes = [
|
||||
'Theme Park',
|
||||
'Amusement Park',
|
||||
'Water Park',
|
||||
'Family Entertainment Center',
|
||||
'Adventure Park',
|
||||
'Safari Park',
|
||||
'Carnival',
|
||||
'Fair'
|
||||
{ value: 'theme_park', label: 'Theme Park' },
|
||||
{ value: 'amusement_park', label: 'Amusement Park' },
|
||||
{ value: 'water_park', label: 'Water Park' },
|
||||
{ value: 'family_entertainment', label: 'Family Entertainment Center' },
|
||||
{ value: 'adventure_park', label: 'Adventure Park' },
|
||||
{ value: 'safari_park', label: 'Safari Park' },
|
||||
{ value: 'carnival', label: 'Carnival' },
|
||||
{ value: 'fair', label: 'Fair' }
|
||||
];
|
||||
|
||||
const statusOptions = [
|
||||
@@ -363,8 +363,8 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }:
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{parkTypes.map((type) => (
|
||||
<SelectItem key={type} value={type}>
|
||||
{type}
|
||||
<SelectItem key={type.value} value={type.value}>
|
||||
{type.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
|
||||
@@ -177,7 +177,7 @@ export const SubmissionItemsList = memo(function SubmissionItemsList({
|
||||
);
|
||||
}
|
||||
|
||||
// Use rich displays for detailed view
|
||||
// Use rich displays for detailed view - show BOTH rich display AND field-by-field changes
|
||||
if (item.item_type === 'park' && entityData) {
|
||||
return (
|
||||
<>
|
||||
@@ -186,6 +186,17 @@ export const SubmissionItemsList = memo(function SubmissionItemsList({
|
||||
data={entityData as unknown as ParkSubmissionData}
|
||||
actionType={actionType}
|
||||
/>
|
||||
<div className="mt-6 pt-6 border-t">
|
||||
<div className="text-xs font-semibold text-muted-foreground uppercase tracking-wide mb-3">
|
||||
All Fields (Detailed View)
|
||||
</div>
|
||||
<SubmissionChangesDisplay
|
||||
item={item}
|
||||
view="detailed"
|
||||
showImages={showImages}
|
||||
submissionId={submissionId}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -198,6 +209,17 @@ export const SubmissionItemsList = memo(function SubmissionItemsList({
|
||||
data={entityData as unknown as RideSubmissionData}
|
||||
actionType={actionType}
|
||||
/>
|
||||
<div className="mt-6 pt-6 border-t">
|
||||
<div className="text-xs font-semibold text-muted-foreground uppercase tracking-wide mb-3">
|
||||
All Fields (Detailed View)
|
||||
</div>
|
||||
<SubmissionChangesDisplay
|
||||
item={item}
|
||||
view="detailed"
|
||||
showImages={showImages}
|
||||
submissionId={submissionId}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -210,6 +232,17 @@ export const SubmissionItemsList = memo(function SubmissionItemsList({
|
||||
data={entityData as unknown as CompanySubmissionData}
|
||||
actionType={actionType}
|
||||
/>
|
||||
<div className="mt-6 pt-6 border-t">
|
||||
<div className="text-xs font-semibold text-muted-foreground uppercase tracking-wide mb-3">
|
||||
All Fields (Detailed View)
|
||||
</div>
|
||||
<SubmissionChangesDisplay
|
||||
item={item}
|
||||
view="detailed"
|
||||
showImages={showImages}
|
||||
submissionId={submissionId}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -222,6 +255,17 @@ export const SubmissionItemsList = memo(function SubmissionItemsList({
|
||||
data={entityData as unknown as RideModelSubmissionData}
|
||||
actionType={actionType}
|
||||
/>
|
||||
<div className="mt-6 pt-6 border-t">
|
||||
<div className="text-xs font-semibold text-muted-foreground uppercase tracking-wide mb-3">
|
||||
All Fields (Detailed View)
|
||||
</div>
|
||||
<SubmissionChangesDisplay
|
||||
item={item}
|
||||
view="detailed"
|
||||
showImages={showImages}
|
||||
submissionId={submissionId}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export function RichParkDisplay({ data, actionType, showAllFields = true }: Rich
|
||||
if (!data) return;
|
||||
|
||||
const fetchRelatedData = async () => {
|
||||
// Fetch location
|
||||
// Fetch location if location_id exists (for edits)
|
||||
if (data.location_id) {
|
||||
const { data: locationData } = await supabase
|
||||
.from('locations')
|
||||
@@ -29,6 +29,10 @@ export function RichParkDisplay({ data, actionType, showAllFields = true }: Rich
|
||||
.eq('id', data.location_id)
|
||||
.single();
|
||||
setLocation(locationData);
|
||||
}
|
||||
// Otherwise use temp_location_data (for new submissions)
|
||||
else if (data.temp_location_data) {
|
||||
setLocation(data.temp_location_data);
|
||||
}
|
||||
|
||||
// Fetch operator
|
||||
@@ -53,7 +57,7 @@ export function RichParkDisplay({ data, actionType, showAllFields = true }: Rich
|
||||
};
|
||||
|
||||
fetchRelatedData();
|
||||
}, [data.location_id, data.operator_id, data.property_owner_id]);
|
||||
}, [data.location_id, data.temp_location_data, data.operator_id, data.property_owner_id]);
|
||||
|
||||
const getStatusColor = (status: string | undefined) => {
|
||||
if (!status) return 'bg-gray-500';
|
||||
|
||||
@@ -602,23 +602,39 @@ export async function validateEntityData(
|
||||
try {
|
||||
switch (tableName) {
|
||||
case 'parks': {
|
||||
const { data } = await supabase.from('parks').select('slug').eq('id', entityId).single();
|
||||
originalSlug = data?.slug || null;
|
||||
const { data, error } = await supabase.from('parks').select('slug').eq('id', entityId).maybeSingle();
|
||||
if (error || !data) {
|
||||
originalSlug = null;
|
||||
break;
|
||||
}
|
||||
originalSlug = data.slug || null;
|
||||
break;
|
||||
}
|
||||
case 'rides': {
|
||||
const { data } = await supabase.from('rides').select('slug').eq('id', entityId).single();
|
||||
originalSlug = data?.slug || null;
|
||||
const { data, error } = await supabase.from('rides').select('slug').eq('id', entityId).maybeSingle();
|
||||
if (error || !data) {
|
||||
originalSlug = null;
|
||||
break;
|
||||
}
|
||||
originalSlug = data.slug || null;
|
||||
break;
|
||||
}
|
||||
case 'companies': {
|
||||
const { data } = await supabase.from('companies').select('slug').eq('id', entityId).single();
|
||||
originalSlug = data?.slug || null;
|
||||
const { data, error } = await supabase.from('companies').select('slug').eq('id', entityId).maybeSingle();
|
||||
if (error || !data) {
|
||||
originalSlug = null;
|
||||
break;
|
||||
}
|
||||
originalSlug = data.slug || null;
|
||||
break;
|
||||
}
|
||||
case 'ride_models': {
|
||||
const { data } = await supabase.from('ride_models').select('slug').eq('id', entityId).single();
|
||||
originalSlug = data?.slug || null;
|
||||
const { data, error } = await supabase.from('ride_models').select('slug').eq('id', entityId).maybeSingle();
|
||||
if (error || !data) {
|
||||
originalSlug = null;
|
||||
break;
|
||||
}
|
||||
originalSlug = data.slug || null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* These replace the `any` types in entityTransformers.ts
|
||||
*/
|
||||
|
||||
import type { LocationData } from './location';
|
||||
|
||||
export interface ParkSubmissionData {
|
||||
name: string;
|
||||
slug: string;
|
||||
@@ -19,6 +21,7 @@ export interface ParkSubmissionData {
|
||||
operator_id?: string | null;
|
||||
property_owner_id?: string | null;
|
||||
location_id?: string | null;
|
||||
temp_location_data?: LocationData | null;
|
||||
banner_image_url?: string | null;
|
||||
banner_image_id?: string | null;
|
||||
card_image_url?: string | null;
|
||||
|
||||
@@ -1179,6 +1179,13 @@ serve(withRateLimit(async (req) => {
|
||||
? 'Submission has unresolved dependencies. Escalation required.'
|
||||
: undefined;
|
||||
|
||||
// Set moderator_id session variable for audit logging
|
||||
await supabase.rpc('set_config', {
|
||||
setting: 'app.moderator_id',
|
||||
value: authenticatedUserId,
|
||||
is_local: true
|
||||
});
|
||||
|
||||
const { error: updateError } = await supabase
|
||||
.from('content_submissions')
|
||||
.update({
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
-- Update log_moderation_action to use session variable for moderator_id
|
||||
-- This allows edge functions using service role to pass the actual moderator ID
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.log_moderation_action(
|
||||
_submission_id uuid,
|
||||
_action text,
|
||||
_previous_status text DEFAULT NULL::text,
|
||||
_new_status text DEFAULT NULL::text,
|
||||
_notes text DEFAULT NULL::text,
|
||||
_metadata jsonb DEFAULT '{}'::jsonb
|
||||
)
|
||||
RETURNS uuid
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $function$
|
||||
DECLARE
|
||||
_log_id UUID;
|
||||
_metadata_record record;
|
||||
_moderator_id UUID;
|
||||
BEGIN
|
||||
-- Get moderator ID from session variable (set by edge function) or auth.uid()
|
||||
BEGIN
|
||||
_moderator_id := COALESCE(
|
||||
current_setting('app.moderator_id', true)::uuid,
|
||||
auth.uid()
|
||||
);
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
_moderator_id := auth.uid();
|
||||
END;
|
||||
|
||||
-- Insert into moderation_audit_log (without metadata JSONB column)
|
||||
INSERT INTO public.moderation_audit_log (
|
||||
submission_id,
|
||||
moderator_id,
|
||||
action,
|
||||
previous_status,
|
||||
new_status,
|
||||
notes
|
||||
) VALUES (
|
||||
_submission_id,
|
||||
_moderator_id,
|
||||
_action,
|
||||
_previous_status,
|
||||
_new_status,
|
||||
_notes
|
||||
)
|
||||
RETURNING id INTO _log_id;
|
||||
|
||||
-- Write metadata to relational moderation_audit_metadata table
|
||||
IF _metadata IS NOT NULL AND jsonb_typeof(_metadata) = 'object' THEN
|
||||
FOR _metadata_record IN
|
||||
SELECT key, value::text as text_value
|
||||
FROM jsonb_each_text(_metadata)
|
||||
LOOP
|
||||
INSERT INTO public.moderation_audit_metadata (
|
||||
audit_log_id,
|
||||
metadata_key,
|
||||
metadata_value
|
||||
) VALUES (
|
||||
_log_id,
|
||||
_metadata_record.key,
|
||||
_metadata_record.text_value
|
||||
);
|
||||
END LOOP;
|
||||
END IF;
|
||||
|
||||
RETURN _log_id;
|
||||
END;
|
||||
$function$;
|
||||
Reference in New Issue
Block a user