Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,6 @@ In this codebase, it is acceptable and preferred to define helper functions (suc
## Git Workflow

- **Auto-commit Rule**: For every user message that requests code changes, automatically commit the changes after implementation with an appropriate commit message

- never run "supabase db push"
- don't run supabase db reset
2 changes: 2 additions & 0 deletions src/components/layout/AppHeader/TitleSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@ export const TitleSection = forwardRef<HTMLDivElement, TitleSectionProps>(
);
},
);

TitleSection.displayName = "TitleSection";
35 changes: 35 additions & 0 deletions src/hooks/queries/custom-links/useCustomLinks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useQuery } from "@tanstack/react-query";
import { supabase } from "@/integrations/supabase/client";
import { Tables } from "@/integrations/supabase/types";

export type CustomLink = Tables<"custom_links">;

export const customLinksKeys = {
all: ["customLinks"] as const,
byFestival: (festivalId: string) =>
[...customLinksKeys.all, festivalId] as const,
};

async function fetchCustomLinks(festivalId: string): Promise<CustomLink[]> {
const { data, error } = await supabase
.from("custom_links")
.select("*")
.eq("festival_id", festivalId)
.order("display_order", { ascending: true })
.order("created_at", { ascending: true });

if (error) {
console.error("Error fetching custom links:", error);
throw new Error("Failed to fetch custom links");
}

return data || [];
}

export function useCustomLinksQuery(festivalId: string | undefined) {
return useQuery({
queryKey: customLinksKeys.byFestival(festivalId || ""),
queryFn: () => fetchCustomLinks(festivalId!),
enabled: !!festivalId,
});
}
99 changes: 99 additions & 0 deletions src/hooks/queries/custom-links/useCustomLinksMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useToast } from "@/hooks/use-toast";
import { supabase } from "@/integrations/supabase/client";
import { customLinksKeys } from "./useCustomLinks";

interface BulkUpdateCustomLinksData {
festivalId: string;
links: Array<{
id?: string;
title: string;
url: string;
display_order: number;
}>;
}

async function bulkUpdateCustomLinks({
festivalId,
links,
}: BulkUpdateCustomLinksData) {
// First, get existing links
const { data: existingLinks, error: fetchError } = await supabase
.from("custom_links")
.select("id")
.eq("festival_id", festivalId);

if (fetchError) throw fetchError;

// Delete existing links that aren't in the new list
const newLinkIds = links.filter((link) => link.id).map((link) => link.id);
const linksToDelete =
existingLinks?.filter((link) => !newLinkIds.includes(link.id)) || [];

if (linksToDelete.length > 0) {
const { error: deleteError } = await supabase
.from("custom_links")
.delete()
.in(
"id",
linksToDelete.map((link) => link.id),
);

if (deleteError) throw deleteError;
}

// Update or create links
const promises = links.map(async (link, index) => {
const linkData = {
title: link.title,
url: link.url,
display_order: index,
};

if (link.id) {
// Update existing link
const { error } = await supabase
.from("custom_links")
.update(linkData)
.eq("id", link.id);

if (error) throw error;
} else {
// Create new link
const { error } = await supabase.from("custom_links").insert({
...linkData,
festival_id: festivalId,
});

if (error) throw error;
}
});

await Promise.all(promises);
}

export function useBulkUpdateCustomLinksMutation() {
const queryClient = useQueryClient();
const { toast } = useToast();

return useMutation({
mutationFn: bulkUpdateCustomLinks,
onSuccess: (_, variables) => {
queryClient.invalidateQueries({
queryKey: customLinksKeys.byFestival(variables.festivalId),
});
toast({
title: "Success",
description: "Custom links updated successfully",
});
},
onError: (error) => {
console.error("Error updating custom links:", error);
toast({
title: "Error",
description: "Failed to update custom links",
variant: "destructive",
});
},
});
}
38 changes: 38 additions & 0 deletions src/hooks/queries/festival-info/useFestivalInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useQuery } from "@tanstack/react-query";
import { supabase } from "@/integrations/supabase/client";
import { Tables } from "@/integrations/supabase/types";

export type FestivalInfo = Tables<"festival_info">;

export const festivalInfoKeys = {
all: ["festivalInfo"] as const,
byFestival: (festivalId: string) =>
[...festivalInfoKeys.all, festivalId] as const,
};

async function fetchFestivalInfo(festivalId: string): Promise<FestivalInfo> {
const { data, error } = await supabase
.from("festival_info")
.select("*")
.eq("festival_id", festivalId)
.single();

if (error) {
if (error.code === "PGRST116") {
throw new Error("Festival info not found");
}

console.error("Error fetching festival info:", error);
throw new Error("Failed to fetch festival info");
}

return data;
}

export function useFestivalInfoQuery(festivalId: string | undefined) {
return useQuery({
queryKey: festivalInfoKeys.byFestival(festivalId || ""),
queryFn: () => fetchFestivalInfo(festivalId!),
enabled: !!festivalId,
});
}
40 changes: 40 additions & 0 deletions src/hooks/queries/festival-info/useFestivalInfoMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { supabase } from "@/integrations/supabase/client";
import { Tables } from "@/integrations/supabase/types";
import { festivalInfoKeys } from "./useFestivalInfo";

export function useFestivalInfoMutation(festivalId: string) {
const queryClient = useQueryClient();

return useMutation({
mutationFn: async (data: Partial<Tables<"festival_info">>) => {
// Check if festival info already exists
const { data: existingInfo } = await supabase
.from("festival_info")
.select("id")
.eq("festival_id", festivalId)
.single();

if (existingInfo) {
// Update existing record
const { error } = await supabase
.from("festival_info")
.update(data)
.eq("festival_id", festivalId);
if (error) throw error;
} else {
// Create new record
const { error } = await supabase.from("festival_info").insert({
festival_id: festivalId,
...data,
});
if (error) throw error;
}
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: festivalInfoKeys.byFestival(festivalId),
});
},
});
}
1 change: 1 addition & 0 deletions src/hooks/queries/festivals/useCreateFestival.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ async function createFestival(festivalData: {
name: string;
slug: string;
description?: string;
published?: boolean;
logo_url?: string | null;
}) {
const { data, error } = await supabase
Expand Down
10 changes: 6 additions & 4 deletions src/hooks/queries/festivals/useUpdateFestival.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import { festivalsKeys } from "./types";

async function updateFestival(
festivalId: string,
festivalData: {
festivalData: Partial<{
name: string;
slug: string;
description?: string;
published?: boolean;
logo_url?: string | null;
},
}>,
) {
const { data, error } = await supabase
.from("festivals")
Expand All @@ -33,12 +34,13 @@ export function useUpdateFestivalMutation() {
festivalData,
}: {
festivalId: string;
festivalData: {
festivalData: Partial<{
name: string;
slug: string;
description?: string;
published?: boolean;
logo_url?: string | null;
};
}>;
}) => updateFestival(festivalId, festivalData),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: festivalsKeys.all() });
Expand Down
86 changes: 83 additions & 3 deletions src/integrations/supabase/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,44 @@ export type Database = {
};
Relationships: [];
};
custom_links: {
Row: {
created_at: string | null;
display_order: number | null;
festival_id: string;
id: string;
title: string;
updated_at: string | null;
url: string;
};
Insert: {
created_at?: string | null;
display_order?: number | null;
festival_id: string;
id?: string;
title: string;
updated_at?: string | null;
url: string;
};
Update: {
created_at?: string | null;
display_order?: number | null;
festival_id?: string;
id?: string;
title?: string;
updated_at?: string | null;
url?: string;
};
Relationships: [
{
foreignKeyName: "custom_links_festival_id_fkey";
columns: ["festival_id"];
isOneToOne: false;
referencedRelation: "festivals";
referencedColumns: ["id"];
},
];
};
festival_editions: {
Row: {
archived: boolean;
Expand Down Expand Up @@ -269,6 +307,47 @@ export type Database = {
},
];
};
festival_info: {
Row: {
created_at: string;
facebook_url: string | null;
festival_id: string;
id: string;
info_text: string | null;
instagram_url: string | null;
map_image_url: string | null;
updated_at: string;
};
Insert: {
created_at?: string;
facebook_url?: string | null;
festival_id: string;
id?: string;
info_text?: string | null;
instagram_url?: string | null;
map_image_url?: string | null;
updated_at?: string;
};
Update: {
created_at?: string;
facebook_url?: string | null;
festival_id?: string;
id?: string;
info_text?: string | null;
instagram_url?: string | null;
map_image_url?: string | null;
updated_at?: string;
};
Relationships: [
{
foreignKeyName: "festival_info_festival_id_fkey";
columns: ["festival_id"];
isOneToOne: true;
referencedRelation: "festivals";
referencedColumns: ["id"];
},
];
};
festivals: {
Row: {
archived: boolean;
Expand All @@ -280,7 +359,6 @@ export type Database = {
published: boolean | null;
slug: string;
updated_at: string;
website_url: string | null;
};
Insert: {
archived?: boolean;
Expand All @@ -292,7 +370,6 @@ export type Database = {
published?: boolean | null;
slug: string;
updated_at?: string;
website_url?: string | null;
};
Update: {
archived?: boolean;
Expand All @@ -304,7 +381,6 @@ export type Database = {
published?: boolean | null;
slug?: string;
updated_at?: string;
website_url?: string | null;
};
Relationships: [];
};
Expand Down Expand Up @@ -631,6 +707,10 @@ export type Database = {
[_ in never]: never;
};
Functions: {
bootstrap_super_admin: {
Args: { user_email: string };
Returns: boolean;
};
can_edit_artists: {
Args: { check_user_id: string };
Returns: boolean;
Expand Down
Loading
Loading