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
38 changes: 38 additions & 0 deletions src/components/StageBadge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { MapPin } from "lucide-react";

interface StageBadgeProps {
stageName: string;
stageColor?: string;
size?: "sm" | "md";
showIcon?: boolean;
}

export function StageBadge({
stageName,
stageColor,
size = "sm",
showIcon = true,
}: StageBadgeProps) {
const sizeClasses = {
sm: "text-xs px-2 py-1 gap-1",
md: "text-sm px-3 py-1.5 gap-2",
};

const iconSize = {
sm: "h-3 w-3",
md: "h-4 w-4",
};

return (
<div
className={`inline-flex items-center rounded-full backdrop-blur-sm border text-white font-medium ${sizeClasses[size]}`}
style={{
backgroundColor: stageColor ? `${stageColor}80` : "#7c3aed80",
borderColor: stageColor ? stageColor : "#7c3aed",
}}
>
{showIcon && <MapPin className={iconSize[size]} />}
<span>{stageName}</span>
</div>
);
}
8 changes: 6 additions & 2 deletions src/hooks/queries/stages/useCreateStage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { generateSlug } from "@/lib/slug";
async function createStage(stageData: {
name: string;
festival_edition_id: string;
stage_order?: number;
color?: string;
}) {
const { data, error } = await supabase
.from("stages")
Expand All @@ -27,8 +29,10 @@ export function useCreateStageMutation() {

return useMutation({
mutationFn: createStage,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: stagesKeys.all });
onSuccess: async () => {
await queryClient.invalidateQueries({
queryKey: stagesKeys.all,
});
toast({
title: "Success",
description: "Stage created successfully",
Expand Down
6 changes: 4 additions & 2 deletions src/hooks/queries/stages/useDeleteStage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ export function useDeleteStageMutation() {

return useMutation({
mutationFn: deleteStage,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: stagesKeys.all });
onSuccess: async () => {
await queryClient.invalidateQueries({
queryKey: stagesKeys.all,
});
toast({
title: "Success",
description: "Stage deleted successfully",
Expand Down
4 changes: 3 additions & 1 deletion src/hooks/queries/stages/useStagesByEdition.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useQuery } from "@tanstack/react-query";
import { supabase } from "@/integrations/supabase/client";
import { Stage, stagesKeys } from "./types";
import { sortStagesByOrder } from "@/lib/stageUtils";

async function fetchStagesByEdition(editionId: string): Promise<Stage[]> {
const { data, error } = await supabase
Expand All @@ -14,7 +15,8 @@ async function fetchStagesByEdition(editionId: string): Promise<Stage[]> {
throw new Error("Failed to load stages for edition");
}

return data || [];
// Apply custom sorting using shared utility
return sortStagesByOrder(data || []);
}

export function useStagesByEditionQuery(editionId: string | undefined) {
Expand Down
13 changes: 9 additions & 4 deletions src/hooks/queries/stages/useUpdateStage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { useToast } from "@/hooks/use-toast";
import { supabase } from "@/integrations/supabase/client";
import { stagesKeys } from "./types";

async function updateStage(stageId: string, stageData: { name: string }) {
async function updateStage(
stageId: string,
stageData: { name: string; stage_order?: number; color?: string },
) {
const { data, error } = await supabase
.from("stages")
.update(stageData)
Expand All @@ -25,10 +28,12 @@ export function useUpdateStageMutation() {
stageData,
}: {
stageId: string;
stageData: { name: string };
stageData: { name: string; stage_order?: number; color?: string };
}) => updateStage(stageId, stageData),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: stagesKeys.all });
onSuccess: async () => {
await queryClient.invalidateQueries({
queryKey: stagesKeys.all,
});
toast({
title: "Success",
description: "Stage updated successfully",
Expand Down
7 changes: 5 additions & 2 deletions src/hooks/useScheduleData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { formatDateTime } from "@/lib/timeUtils";
import { format, startOfDay } from "date-fns";
import type { FestivalSet } from "@/hooks/queries/sets/useSets";
import { Stage } from "./queries/stages/types";
import { sortStagesByOrder } from "@/lib/stageUtils";

export interface ScheduleDay {
date: string;
Expand All @@ -13,6 +14,7 @@ export interface ScheduleDay {
export interface ScheduleStage {
id: string;
name: string;
stage_order: number;
sets: ScheduleSet[];
}

Expand Down Expand Up @@ -127,18 +129,19 @@ export function useScheduleData(
return {
id: stageId,
name: stage?.name,
stage_order: stage?.stage_order,
sets: stageSets.sort((a, b) => {
if (!a.startTime || !b.startTime) return 0;
return a.startTime.getTime() - b.startTime.getTime();
}),
};
} satisfies ScheduleStage;
})
.filter((v: ScheduleStage | null): v is ScheduleStage => !!v);

return {
date: dateKey,
displayDate: format(date, "EEEE, MMM d"),
stages: scheduleStages.sort((a, b) => a.name.localeCompare(b.name)),
stages: sortStagesByOrder(scheduleStages),
};
});

Expand Down
6 changes: 6 additions & 0 deletions src/integrations/supabase/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -640,29 +640,35 @@ export type Database = {
stages: {
Row: {
archived: boolean;
color: string | null;
created_at: string;
festival_edition_id: string;
id: string;
name: string;
slug: string;
stage_order: number;
updated_at: string;
};
Insert: {
archived?: boolean;
color?: string | null;
created_at?: string;
festival_edition_id: string;
id?: string;
name: string;
slug: string;
stage_order?: number;
updated_at?: string;
};
Update: {
archived?: boolean;
color?: string | null;
created_at?: string;
festival_edition_id?: string;
id?: string;
name?: string;
slug?: string;
stage_order?: number;
updated_at?: string;
};
Relationships: [];
Expand Down
1 change: 1 addition & 0 deletions src/lib/constants/stages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const DEFAULT_STAGE_COLOR = "#6b7280";
28 changes: 28 additions & 0 deletions src/lib/stageUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Stage } from "@/hooks/queries/stages/types";

/**
* Sorts stages by priority: stages with order > 0 come first (sorted by order),
* then stages with order 0 come last (sorted alphabetically by name)
*/
export function sortStagesByOrder<
T extends Pick<Stage, "stage_order" | "name">,
>(items: T[]): T[] {
return items.sort((stageA, stageB) => {
const orderA = stageA.stage_order ?? 0;
const orderB = stageB.stage_order ?? 0;

// Stages with order > 0 come first, sorted by order
// Stages with order 0 come last, sorted by name
if (orderA > 0 && orderB > 0) {
return orderA - orderB;
}
if (orderA > 0 && orderB === 0) {
return -1; // A comes before B
}
if (orderA === 0 && orderB > 0) {
return 1; // B comes before A
}
// Both are 0, sort by name
return stageA.name.localeCompare(stageB.name);
});
}
Loading
Loading