Compare commits

..

10 Commits

Author SHA1 Message Date
gpt-engineer-app[bot]
4ee6419865 Fix ambiguous relationship queries 2025-11-05 19:55:36 +00:00
gpt-engineer-app[bot]
6cc08de96c Fix security vulnerabilities 2025-11-05 19:51:25 +00:00
gpt-engineer-app[bot]
00b2ea2192 Fix duplicate foreign key constraints 2025-11-05 19:47:16 +00:00
gpt-engineer-app[bot]
c0a4a8dc9c Fix duplicate foreign key constraints 2025-11-05 19:46:56 +00:00
gpt-engineer-app[bot]
4d571e4f12 Fix search path security warning 2025-11-05 19:44:01 +00:00
gpt-engineer-app[bot]
a168007e23 Fix search path security warning 2025-11-05 19:43:39 +00:00
gpt-engineer-app[bot]
bd3bffcc20 Fix edge function errors 2025-11-05 19:40:35 +00:00
gpt-engineer-app[bot]
d998225315 Fix: Reorder mobile menu items 2025-11-05 19:35:56 +00:00
gpt-engineer-app[bot]
45a5dadd29 Add smooth transitions and reorder menu items 2025-11-05 19:33:59 +00:00
gpt-engineer-app[bot]
3f95e447bb Fix Explore menu width 2025-11-05 19:31:20 +00:00
10 changed files with 499 additions and 34 deletions

View File

@@ -52,13 +52,6 @@ export function Header() {
Explore
</h3>
</div>
<Link
to="/parks"
className="px-3 py-2.5 text-base font-medium hover:bg-accent hover:text-accent-foreground rounded-md transition-colors"
onClick={() => setOpen(false)}
>
Parks
</Link>
<Link
to="/rides"
className="px-3 py-2.5 text-base font-medium hover:bg-accent hover:text-accent-foreground rounded-md transition-colors"
@@ -66,6 +59,13 @@ export function Header() {
>
Rides
</Link>
<Link
to="/parks"
className="px-3 py-2.5 text-base font-medium hover:bg-accent hover:text-accent-foreground rounded-md transition-colors"
onClick={() => setOpen(false)}
>
Parks
</Link>
<Link
to="/manufacturers"
className="px-3 py-2.5 text-base font-medium hover:bg-accent hover:text-accent-foreground rounded-md transition-colors"
@@ -129,20 +129,7 @@ export function Header() {
<NavigationMenuItem>
<NavigationMenuTrigger className="h-9">Explore</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid w-[400px] gap-3 p-4">
<li>
<NavigationMenuLink asChild>
<Link
to="/parks"
className="block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent/20 focus:bg-accent/20"
>
<div className="text-sm font-medium leading-none">Parks</div>
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
Browse theme parks around the world
</p>
</Link>
</NavigationMenuLink>
</li>
<ul className="grid min-w-[320px] max-w-[500px] w-fit gap-3 p-4">
<li>
<NavigationMenuLink asChild>
<Link
@@ -156,6 +143,19 @@ export function Header() {
</Link>
</NavigationMenuLink>
</li>
<li>
<NavigationMenuLink asChild>
<Link
to="/parks"
className="block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent/20 focus:bg-accent/20"
>
<div className="text-sm font-medium leading-none">Parks</div>
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
Browse theme parks around the world
</p>
</Link>
</NavigationMenuLink>
</li>
<li>
<NavigationMenuLink asChild>
<Link

View File

@@ -80,7 +80,7 @@ const NavigationMenuViewport = React.forwardRef<
<div className={cn("absolute left-0 top-full flex justify-center")}>
<NavigationMenuPrimitive.Viewport
className={cn(
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)] transition-all duration-300 ease-in-out",
className,
)}
ref={ref}

View File

@@ -139,12 +139,12 @@ export function useModerationActions(config: ModerationActionsConfig): Moderatio
.select(`
id,
item_type,
park_submission:park_submissions!park_submission_id(*),
ride_submission:ride_submissions!ride_submission_id(*),
company_submission:company_submissions!company_submission_id(*),
ride_model_submission:ride_model_submissions!ride_model_submission_id(*),
timeline_event_submission:timeline_event_submissions!timeline_event_submission_id(*),
photo_submission:photo_submissions!photo_submission_id(*)
park_submission:park_submissions!submission_items_park_submission_id_fkey(*),
ride_submission:ride_submissions!submission_items_ride_submission_id_fkey(*),
company_submission:company_submissions!submission_items_company_submission_id_fkey(*),
ride_model_submission:ride_model_submissions!submission_items_ride_model_submission_id_fkey(*),
timeline_event_submission:timeline_event_submissions!submission_items_timeline_event_submission_id_fkey(*),
photo_submission:photo_submissions!submission_items_photo_submission_id_fkey(*)
`)
.eq('submission_id', item.id)
.in('status', ['pending', 'rejected']);

View File

@@ -730,14 +730,15 @@ serve(withRateLimit(async (req) => {
.from('submission_items')
.select(`
*,
park_submission:park_submissions!item_data_id(*),
ride_submission:ride_submissions!item_data_id(*),
company_submission:company_submissions!item_data_id(*),
ride_model_submission:ride_model_submissions!item_data_id(*),
photo_submission:photo_submissions!item_data_id(
park_submission:park_submissions!park_submission_id(*),
ride_submission:ride_submissions!ride_submission_id(*),
company_submission:company_submissions!company_submission_id(*),
ride_model_submission:ride_model_submissions!ride_model_submission_id(*),
photo_submission:photo_submissions!photo_submission_id(
*,
photo_items:photo_submission_items(*)
)
),
timeline_event_submission:timeline_event_submissions!timeline_event_submission_id(*)
`)
.in('id', itemIds);

View File

@@ -0,0 +1,50 @@
-- Add foreign key constraints to submission_items table
ALTER TABLE submission_items
ADD CONSTRAINT fk_submission_items_park_submission
FOREIGN KEY (park_submission_id)
REFERENCES park_submissions(id)
ON DELETE CASCADE;
ALTER TABLE submission_items
ADD CONSTRAINT fk_submission_items_ride_submission
FOREIGN KEY (ride_submission_id)
REFERENCES ride_submissions(id)
ON DELETE CASCADE;
ALTER TABLE submission_items
ADD CONSTRAINT fk_submission_items_company_submission
FOREIGN KEY (company_submission_id)
REFERENCES company_submissions(id)
ON DELETE CASCADE;
ALTER TABLE submission_items
ADD CONSTRAINT fk_submission_items_ride_model_submission
FOREIGN KEY (ride_model_submission_id)
REFERENCES ride_model_submissions(id)
ON DELETE CASCADE;
ALTER TABLE submission_items
ADD CONSTRAINT fk_submission_items_photo_submission
FOREIGN KEY (photo_submission_id)
REFERENCES photo_submissions(id)
ON DELETE CASCADE;
ALTER TABLE submission_items
ADD CONSTRAINT fk_submission_items_timeline_event_submission
FOREIGN KEY (timeline_event_submission_id)
REFERENCES timeline_event_submissions(id)
ON DELETE CASCADE;
-- Add indexes for better query performance
CREATE INDEX IF NOT EXISTS idx_submission_items_park_submission_id
ON submission_items(park_submission_id);
CREATE INDEX IF NOT EXISTS idx_submission_items_ride_submission_id
ON submission_items(ride_submission_id);
CREATE INDEX IF NOT EXISTS idx_submission_items_company_submission_id
ON submission_items(company_submission_id);
CREATE INDEX IF NOT EXISTS idx_submission_items_ride_model_submission_id
ON submission_items(ride_model_submission_id);
CREATE INDEX IF NOT EXISTS idx_submission_items_photo_submission_id
ON submission_items(photo_submission_id);
CREATE INDEX IF NOT EXISTS idx_submission_items_timeline_event_submission_id
ON submission_items(timeline_event_submission_id);

View File

@@ -0,0 +1,44 @@
-- Fix search_path security vulnerability in update_content_submissions_updated_at
-- This addresses the function_search_path_mutable linter warning
DROP FUNCTION IF EXISTS public.update_content_submissions_updated_at() CASCADE;
CREATE OR REPLACE FUNCTION public.update_content_submissions_updated_at()
RETURNS TRIGGER
LANGUAGE plpgsql
SET search_path = 'public'
AS $$
BEGIN
-- Only update updated_at if actual content has changed
-- Ignore changes to: updated_at, assigned_to, assigned_at, locked_until, priority, review_count, first_reviewed_at, resolved_at, submitted_at
IF (
NEW.status IS DISTINCT FROM OLD.status OR
NEW.reviewer_id IS DISTINCT FROM OLD.reviewer_id OR
NEW.reviewer_notes IS DISTINCT FROM OLD.reviewer_notes OR
NEW.escalated IS DISTINCT FROM OLD.escalated OR
NEW.escalation_reason IS DISTINCT FROM OLD.escalation_reason OR
NEW.approval_mode IS DISTINCT FROM OLD.approval_mode OR
NEW.user_id IS DISTINCT FROM OLD.user_id OR
NEW.submission_type IS DISTINCT FROM OLD.submission_type OR
NEW.escalated_by IS DISTINCT FROM OLD.escalated_by OR
NEW.escalated_at IS DISTINCT FROM OLD.escalated_at OR
NEW.original_submission_id IS DISTINCT FROM OLD.original_submission_id
) THEN
NEW.updated_at = NOW();
ELSE
-- Keep the old updated_at timestamp if only metadata changed
NEW.updated_at = OLD.updated_at;
END IF;
RETURN NEW;
END;
$$;
-- Recreate trigger for content_submissions
CREATE TRIGGER update_content_submissions_updated_at
BEFORE UPDATE ON public.content_submissions
FOR EACH ROW
EXECUTE FUNCTION public.update_content_submissions_updated_at();
COMMENT ON FUNCTION public.update_content_submissions_updated_at() IS
'SECURITY HARDENED: Trigger function to update updated_at timestamp only when meaningful fields change. Includes SET search_path to prevent search path injection attacks.';

View File

@@ -0,0 +1,44 @@
-- Fix search_path security vulnerability in update_content_submissions_updated_at
-- This addresses the function_search_path_mutable linter warning
DROP FUNCTION IF EXISTS public.update_content_submissions_updated_at() CASCADE;
CREATE OR REPLACE FUNCTION public.update_content_submissions_updated_at()
RETURNS TRIGGER
LANGUAGE plpgsql
SET search_path = 'public'
AS $$
BEGIN
-- Only update updated_at if actual content has changed
-- Ignore changes to: updated_at, assigned_to, assigned_at, locked_until, priority, review_count, first_reviewed_at, resolved_at, submitted_at
IF (
NEW.status IS DISTINCT FROM OLD.status OR
NEW.reviewer_id IS DISTINCT FROM OLD.reviewer_id OR
NEW.reviewer_notes IS DISTINCT FROM OLD.reviewer_notes OR
NEW.escalated IS DISTINCT FROM OLD.escalated OR
NEW.escalation_reason IS DISTINCT FROM OLD.escalation_reason OR
NEW.approval_mode IS DISTINCT FROM OLD.approval_mode OR
NEW.user_id IS DISTINCT FROM OLD.user_id OR
NEW.submission_type IS DISTINCT FROM OLD.submission_type OR
NEW.escalated_by IS DISTINCT FROM OLD.escalated_by OR
NEW.escalated_at IS DISTINCT FROM OLD.escalated_at OR
NEW.original_submission_id IS DISTINCT FROM OLD.original_submission_id
) THEN
NEW.updated_at = NOW();
ELSE
-- Keep the old updated_at timestamp if only metadata changed
NEW.updated_at = OLD.updated_at;
END IF;
RETURN NEW;
END;
$$;
-- Recreate trigger for content_submissions
CREATE TRIGGER update_content_submissions_updated_at
BEFORE UPDATE ON public.content_submissions
FOR EACH ROW
EXECUTE FUNCTION public.update_content_submissions_updated_at();
COMMENT ON FUNCTION public.update_content_submissions_updated_at() IS
'SECURITY HARDENED: Trigger function to update updated_at timestamp only when meaningful fields change. Includes SET search_path to prevent search path injection attacks.';

View File

@@ -0,0 +1,29 @@
-- Remove duplicate foreign key constraints added in migration 20251105193953
-- Keep the original _fkey constraints that were already working
-- Drop the duplicate park_submission constraint (keep submission_items_park_submission_id_fkey)
ALTER TABLE submission_items
DROP CONSTRAINT IF EXISTS fk_submission_items_park_submission;
-- Drop the duplicate ride_submission constraint (keep submission_items_ride_submission_id_fkey)
ALTER TABLE submission_items
DROP CONSTRAINT IF EXISTS fk_submission_items_ride_submission;
-- Drop the duplicate company_submission constraint (keep submission_items_company_submission_id_fkey)
ALTER TABLE submission_items
DROP CONSTRAINT IF EXISTS fk_submission_items_company_submission;
-- Drop the duplicate ride_model_submission constraint (keep submission_items_ride_model_submission_id_fkey)
ALTER TABLE submission_items
DROP CONSTRAINT IF EXISTS fk_submission_items_ride_model_submission;
-- Drop the duplicate photo_submission constraint (keep submission_items_photo_submission_id_fkey)
ALTER TABLE submission_items
DROP CONSTRAINT IF EXISTS fk_submission_items_photo_submission;
-- Drop the duplicate timeline_event_submission constraint (keep submission_items_timeline_event_submission_id_fkey)
ALTER TABLE submission_items
DROP CONSTRAINT IF EXISTS fk_submission_items_timeline_event_submission;
COMMENT ON TABLE submission_items IS
'Submission items with single foreign key constraints to various submission types. Original _fkey constraints maintained.';

View File

@@ -0,0 +1,29 @@
-- Remove duplicate foreign key constraints added in migration 20251105193953
-- Keep the original _fkey constraints that were already working
-- Drop the duplicate park_submission constraint (keep submission_items_park_submission_id_fkey)
ALTER TABLE submission_items
DROP CONSTRAINT IF EXISTS fk_submission_items_park_submission;
-- Drop the duplicate ride_submission constraint (keep submission_items_ride_submission_id_fkey)
ALTER TABLE submission_items
DROP CONSTRAINT IF EXISTS fk_submission_items_ride_submission;
-- Drop the duplicate company_submission constraint (keep submission_items_company_submission_id_fkey)
ALTER TABLE submission_items
DROP CONSTRAINT IF EXISTS fk_submission_items_company_submission;
-- Drop the duplicate ride_model_submission constraint (keep submission_items_ride_model_submission_id_fkey)
ALTER TABLE submission_items
DROP CONSTRAINT IF EXISTS fk_submission_items_ride_model_submission;
-- Drop the duplicate photo_submission constraint (keep submission_items_photo_submission_id_fkey)
ALTER TABLE submission_items
DROP CONSTRAINT IF EXISTS fk_submission_items_photo_submission;
-- Drop the duplicate timeline_event_submission constraint (keep submission_items_timeline_event_submission_id_fkey)
ALTER TABLE submission_items
DROP CONSTRAINT IF EXISTS fk_submission_items_timeline_event_submission;
COMMENT ON TABLE submission_items IS
'Submission items with single foreign key constraints to various submission types. Original _fkey constraints maintained.';

View File

@@ -0,0 +1,268 @@
-- Comprehensive fix: Add SET search_path to all remaining SECURITY DEFINER functions
-- This prevents search_path injection attacks
-- ============================================================================
-- RATING SYSTEM FUNCTIONS (from migration 20250920125706)
-- ============================================================================
-- 1. Fix update_park_ratings
CREATE OR REPLACE FUNCTION public.update_park_ratings(target_park_id UUID)
RETURNS void AS $$
DECLARE
avg_rating DECIMAL(3,2);
review_cnt INTEGER;
BEGIN
SELECT
COALESCE(AVG(rating), 0)::DECIMAL(3,2),
COUNT(*)
INTO avg_rating, review_cnt
FROM public.reviews
WHERE park_id = target_park_id AND moderation_status = 'approved';
UPDATE public.parks
SET
average_rating = avg_rating,
review_count = review_cnt,
updated_at = now()
WHERE id = target_park_id;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER SET search_path = 'public';
-- 2. Fix update_ride_ratings
CREATE OR REPLACE FUNCTION public.update_ride_ratings(target_ride_id UUID)
RETURNS void AS $$
DECLARE
avg_rating DECIMAL(3,2);
review_cnt INTEGER;
BEGIN
SELECT
COALESCE(AVG(rating), 0)::DECIMAL(3,2),
COUNT(*)
INTO avg_rating, review_cnt
FROM public.reviews
WHERE ride_id = target_ride_id AND moderation_status = 'approved';
UPDATE public.rides
SET
average_rating = avg_rating,
review_count = review_cnt,
updated_at = now()
WHERE id = target_ride_id;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER SET search_path = 'public';
-- 3. Fix update_company_ratings
CREATE OR REPLACE FUNCTION public.update_company_ratings(target_company_id UUID)
RETURNS void AS $$
DECLARE
avg_rating DECIMAL(3,2);
review_cnt INTEGER;
BEGIN
-- Calculate ratings from parks operated by this company
WITH park_reviews AS (
SELECT r.rating
FROM public.reviews r
JOIN public.parks p ON r.park_id = p.id
WHERE (p.operator_id = target_company_id OR p.property_owner_id = target_company_id)
AND r.moderation_status = 'approved'
),
-- Calculate ratings from rides manufactured/designed by this company
ride_reviews AS (
SELECT r.rating
FROM public.reviews r
JOIN public.rides rd ON r.ride_id = rd.id
WHERE (rd.manufacturer_id = target_company_id OR rd.designer_id = target_company_id)
AND r.moderation_status = 'approved'
),
-- Combine all reviews
all_reviews AS (
SELECT rating FROM park_reviews
UNION ALL
SELECT rating FROM ride_reviews
)
SELECT
COALESCE(AVG(rating), 0)::DECIMAL(3,2),
COUNT(*)
INTO avg_rating, review_cnt
FROM all_reviews;
UPDATE public.companies
SET
average_rating = avg_rating,
review_count = review_cnt,
updated_at = now()
WHERE id = target_company_id;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER SET search_path = 'public';
-- 4. Fix update_all_ratings_for_review (trigger function)
CREATE OR REPLACE FUNCTION public.update_all_ratings_for_review()
RETURNS trigger AS $$
DECLARE
company_ids UUID[];
BEGIN
-- Handle both INSERT/UPDATE and DELETE cases
IF TG_OP = 'DELETE' THEN
-- Update park rating if this was a park review
IF OLD.park_id IS NOT NULL THEN
PERFORM public.update_park_ratings(OLD.park_id);
-- Update related company ratings
SELECT ARRAY[p.operator_id, p.property_owner_id]
INTO company_ids
FROM public.parks p
WHERE p.id = OLD.park_id;
-- Update company ratings for related companies
IF company_ids IS NOT NULL THEN
FOR i IN 1..array_length(company_ids, 1) LOOP
IF company_ids[i] IS NOT NULL THEN
PERFORM public.update_company_ratings(company_ids[i]);
END IF;
END LOOP;
END IF;
END IF;
-- Update ride rating if this was a ride review
IF OLD.ride_id IS NOT NULL THEN
PERFORM public.update_ride_ratings(OLD.ride_id);
-- Update related company ratings
SELECT ARRAY[r.manufacturer_id, r.designer_id]
INTO company_ids
FROM public.rides r
WHERE r.id = OLD.ride_id;
-- Update company ratings for related companies
IF company_ids IS NOT NULL THEN
FOR i IN 1..array_length(company_ids, 1) LOOP
IF company_ids[i] IS NOT NULL THEN
PERFORM public.update_company_ratings(company_ids[i]);
END IF;
END LOOP;
END IF;
END IF;
RETURN OLD;
ELSE
-- Handle INSERT/UPDATE
-- Update park rating if this is a park review
IF NEW.park_id IS NOT NULL THEN
PERFORM public.update_park_ratings(NEW.park_id);
-- Update related company ratings
SELECT ARRAY[p.operator_id, p.property_owner_id]
INTO company_ids
FROM public.parks p
WHERE p.id = NEW.park_id;
-- Update company ratings for related companies
IF company_ids IS NOT NULL THEN
FOR i IN 1..array_length(company_ids, 1) LOOP
IF company_ids[i] IS NOT NULL THEN
PERFORM public.update_company_ratings(company_ids[i]);
END IF;
END LOOP;
END IF;
END IF;
-- Update ride rating if this is a ride review
IF NEW.ride_id IS NOT NULL THEN
PERFORM public.update_ride_ratings(NEW.ride_id);
-- Update related company ratings
SELECT ARRAY[r.manufacturer_id, r.designer_id]
INTO company_ids
FROM public.rides r
WHERE r.id = NEW.ride_id;
-- Update company ratings for related companies
IF company_ids IS NOT NULL THEN
FOR i IN 1..array_length(company_ids, 1) LOOP
IF company_ids[i] IS NOT NULL THEN
PERFORM public.update_company_ratings(company_ids[i]);
END IF;
END LOOP;
END IF;
END IF;
RETURN NEW;
END IF;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER SET search_path = 'public';
-- ============================================================================
-- TICKET SYSTEM FUNCTIONS (from migration 20251028183015)
-- ============================================================================
-- 5. Fix generate_ticket_number
CREATE OR REPLACE FUNCTION public.generate_ticket_number()
RETURNS TEXT AS $$
BEGIN
RETURN 'TW-' || LPAD(nextval('contact_ticket_number_seq')::TEXT, 6, '0');
END;
$$ LANGUAGE plpgsql SECURITY DEFINER SET search_path = 'public';
-- 6. Fix set_ticket_number (trigger function)
CREATE OR REPLACE FUNCTION public.set_ticket_number()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.ticket_number IS NULL THEN
NEW.ticket_number := generate_ticket_number();
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER SET search_path = 'public';
-- ============================================================================
-- MODERATION QUEUE FUNCTION (from migration 20251103162832)
-- ============================================================================
-- 7. Fix get_submission_item_entity_data
CREATE OR REPLACE FUNCTION public.get_submission_item_entity_data(
p_item_type text,
p_item_data_id uuid
) RETURNS jsonb AS $$
DECLARE
v_result jsonb;
BEGIN
CASE p_item_type
WHEN 'park' THEN
SELECT to_jsonb(ps.*) INTO v_result
FROM park_submissions ps
WHERE ps.id = p_item_data_id;
WHEN 'ride' THEN
SELECT to_jsonb(rs.*) INTO v_result
FROM ride_submissions rs
WHERE rs.id = p_item_data_id;
WHEN 'manufacturer', 'operator', 'designer', 'property_owner' THEN
SELECT to_jsonb(cs.*) INTO v_result
FROM company_submissions cs
WHERE cs.id = p_item_data_id;
WHEN 'ride_model' THEN
SELECT to_jsonb(rms.*) INTO v_result
FROM ride_model_submissions rms
WHERE rms.id = p_item_data_id;
WHEN 'photo' THEN
SELECT to_jsonb(ps.*) INTO v_result
FROM photo_submissions ps
WHERE ps.id = p_item_data_id;
ELSE
v_result := NULL;
END CASE;
RETURN v_result;
END;
$$ LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path = 'public';
-- ============================================================================
-- VERIFICATION COMMENTS
-- ============================================================================
COMMENT ON FUNCTION public.update_park_ratings IS 'Recalculates park ratings from approved reviews. Protected with SET search_path.';
COMMENT ON FUNCTION public.update_ride_ratings IS 'Recalculates ride ratings from approved reviews. Protected with SET search_path.';
COMMENT ON FUNCTION public.update_company_ratings IS 'Recalculates company ratings from associated parks/rides. Protected with SET search_path.';
COMMENT ON FUNCTION public.update_all_ratings_for_review IS 'Trigger to update all entity ratings when a review changes. Protected with SET search_path.';
COMMENT ON FUNCTION public.generate_ticket_number IS 'Generates unique ticket numbers for contact submissions. Protected with SET search_path.';
COMMENT ON FUNCTION public.set_ticket_number IS 'Trigger to auto-assign ticket numbers. Protected with SET search_path.';
COMMENT ON FUNCTION public.get_submission_item_entity_data IS 'Retrieves entity data for moderation queue items. Protected with SET search_path.';