claude/sports-templates-conversion-ILWAM #71
13 changed files with 6314 additions and 164 deletions
|
|
@ -1,6 +1,8 @@
|
|||
import { eq, and } from "drizzle-orm";
|
||||
import { database } from "~/database/context";
|
||||
import * as schema from "~/database/schema";
|
||||
import { findDraftableSportsSeasonBySportId } from "~/models/sports-season";
|
||||
import { logger } from "~/lib/logger";
|
||||
|
||||
export type SeasonSport = typeof schema.seasonSports.$inferSelect;
|
||||
export type NewSeasonSport = typeof schema.seasonSports.$inferInsert;
|
||||
|
|
@ -89,7 +91,6 @@ export async function applySportsFromTemplate(
|
|||
): Promise<SeasonSport[]> {
|
||||
const db = database();
|
||||
|
||||
// Get all sports from the template
|
||||
const templateSports = await db.query.seasonTemplateSports.findMany({
|
||||
where: eq(schema.seasonTemplateSports.templateId, templateId),
|
||||
});
|
||||
|
|
@ -98,12 +99,26 @@ export async function applySportsFromTemplate(
|
|||
return [];
|
||||
}
|
||||
|
||||
// Link them to the season
|
||||
const resolved = await Promise.all(
|
||||
templateSports.map(async (ts) => {
|
||||
const season = await findDraftableSportsSeasonBySportId(ts.sportId);
|
||||
if (!season) {
|
||||
logger.warn(`No draftable season found for sport ${ts.sportId} in template ${templateId}`);
|
||||
}
|
||||
return season ?? null;
|
||||
})
|
||||
);
|
||||
|
||||
const sportsSeasonIds = resolved
|
||||
.filter((s): s is NonNullable<typeof s> => s !== null)
|
||||
.map((s) => s.id);
|
||||
|
||||
if (sportsSeasonIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return await linkMultipleSportsToSeason(
|
||||
templateSports.map((ts) => ({
|
||||
seasonId,
|
||||
sportsSeasonId: ts.sportsSeasonId,
|
||||
}))
|
||||
sportsSeasonIds.map((sportsSeasonId) => ({ seasonId, sportsSeasonId }))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@ export type NewSeasonTemplateSport = typeof schema.seasonTemplateSports.$inferIn
|
|||
|
||||
export async function addSportToTemplate(
|
||||
templateId: string,
|
||||
sportsSeasonId: string
|
||||
sportId: string
|
||||
): Promise<SeasonTemplateSport> {
|
||||
const db = database();
|
||||
const [link] = await db
|
||||
.insert(schema.seasonTemplateSports)
|
||||
.values({
|
||||
templateId,
|
||||
sportsSeasonId,
|
||||
sportId,
|
||||
})
|
||||
.returning();
|
||||
return link;
|
||||
|
|
@ -32,7 +32,7 @@ export async function addMultipleSportsToTemplate(
|
|||
|
||||
export async function removeSportFromTemplate(
|
||||
templateId: string,
|
||||
sportsSeasonId: string
|
||||
sportId: string
|
||||
): Promise<void> {
|
||||
const db = database();
|
||||
await db
|
||||
|
|
@ -40,7 +40,7 @@ export async function removeSportFromTemplate(
|
|||
.where(
|
||||
and(
|
||||
eq(schema.seasonTemplateSports.templateId, templateId),
|
||||
eq(schema.seasonTemplateSports.sportsSeasonId, sportsSeasonId)
|
||||
eq(schema.seasonTemplateSports.sportId, sportId)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
@ -52,28 +52,23 @@ export async function findSeasonTemplateSportsByTemplateId(
|
|||
return await db.query.seasonTemplateSports.findMany({
|
||||
where: eq(schema.seasonTemplateSports.templateId, templateId),
|
||||
with: {
|
||||
sportsSeason: {
|
||||
with: {
|
||||
sport: true,
|
||||
},
|
||||
},
|
||||
sport: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function findSeasonTemplateSportsBySportsSeasonId(
|
||||
sportsSeasonId: string
|
||||
export async function findSeasonTemplateSportsBySportId(
|
||||
sportId: string
|
||||
): Promise<SeasonTemplateSport[]> {
|
||||
const db = database();
|
||||
return await db.query.seasonTemplateSports.findMany({
|
||||
where: eq(schema.seasonTemplateSports.sportsSeasonId, sportsSeasonId),
|
||||
where: eq(schema.seasonTemplateSports.sportId, sportId),
|
||||
with: {
|
||||
template: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export async function deleteSeasonTemplateSport(id: string): Promise<void> {
|
||||
const db = database();
|
||||
await db.delete(schema.seasonTemplateSports).where(eq(schema.seasonTemplateSports.id, id));
|
||||
|
|
|
|||
|
|
@ -29,18 +29,14 @@ export async function findSeasonTemplateById(id: string): Promise<SeasonTemplate
|
|||
});
|
||||
}
|
||||
|
||||
export async function findSeasonTemplateWithSportsSeasons(id: string) {
|
||||
export async function findSeasonTemplateWithSports(id: string) {
|
||||
const db = database();
|
||||
return await db.query.seasonTemplates.findFirst({
|
||||
where: eq(schema.seasonTemplates.id, id),
|
||||
with: {
|
||||
seasonTemplateSports: {
|
||||
with: {
|
||||
sportsSeason: {
|
||||
with: {
|
||||
sport: true,
|
||||
},
|
||||
},
|
||||
sport: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -51,14 +47,6 @@ export async function findActiveSeasonTemplates(): Promise<SeasonTemplate[]> {
|
|||
const db = database();
|
||||
return await db.query.seasonTemplates.findMany({
|
||||
where: eq(schema.seasonTemplates.isActive, true),
|
||||
orderBy: (templates, { desc }) => [desc(templates.year)],
|
||||
});
|
||||
}
|
||||
|
||||
export async function findSeasonTemplatesByYear(year: number): Promise<SeasonTemplate[]> {
|
||||
const db = database();
|
||||
return await db.query.seasonTemplates.findMany({
|
||||
where: eq(schema.seasonTemplates.year, year),
|
||||
orderBy: (templates, { asc }) => [asc(templates.name)],
|
||||
});
|
||||
}
|
||||
|
|
@ -66,7 +54,7 @@ export async function findSeasonTemplatesByYear(year: number): Promise<SeasonTem
|
|||
export async function findAllSeasonTemplates(): Promise<SeasonTemplate[]> {
|
||||
const db = database();
|
||||
return await db.query.seasonTemplates.findMany({
|
||||
orderBy: (templates, { desc }) => [desc(templates.year)],
|
||||
orderBy: (templates, { asc }) => [asc(templates.name)],
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -151,6 +151,23 @@ export async function findDraftableSportsSeasons() {
|
|||
}));
|
||||
}
|
||||
|
||||
export async function findDraftableSportsSeasonBySportId(sportId: string) {
|
||||
const db = database();
|
||||
const today = sql`CURRENT_DATE`;
|
||||
return await db.query.sportsSeasons.findFirst({
|
||||
where: (ss, { and, eq }) =>
|
||||
and(
|
||||
eq(ss.sportId, sportId),
|
||||
lte(ss.draftOn, today),
|
||||
gte(ss.draftOff, today),
|
||||
isNull(ss.fantasySeasonId)
|
||||
),
|
||||
with: {
|
||||
sport: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateSportsSeason(
|
||||
id: string,
|
||||
data: Partial<NewSportsSeason>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import type { Route } from "./+types/admin.templates.$id";
|
|||
|
||||
import { logger } from "~/lib/logger";
|
||||
import {
|
||||
findSeasonTemplateWithSportsSeasons,
|
||||
findSeasonTemplateWithSports,
|
||||
updateSeasonTemplate,
|
||||
deleteSeasonTemplate,
|
||||
setSeasonTemplateActive
|
||||
|
|
@ -13,7 +13,7 @@ import {
|
|||
addSportToTemplate,
|
||||
removeSportFromTemplate
|
||||
} from "~/models/season-template-sport";
|
||||
import { findAllSportsSeasons } from "~/models/sports-season";
|
||||
import { findAllSports } from "~/models/sport";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { Input } from "~/components/ui/input";
|
||||
import { Label } from "~/components/ui/label";
|
||||
|
|
@ -51,19 +51,15 @@ export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors {
|
|||
}
|
||||
|
||||
export async function loader({ params }: Route.LoaderArgs) {
|
||||
const template = await findSeasonTemplateWithSportsSeasons(params.id);
|
||||
|
||||
const template = await findSeasonTemplateWithSports(params.id);
|
||||
|
||||
if (!template) {
|
||||
throw new Response("Template not found", { status: 404 });
|
||||
}
|
||||
|
||||
const allSportsSeasons = await findAllSportsSeasons();
|
||||
const allSports = await findAllSports();
|
||||
|
||||
// Type assertion since we know the sport relation is included
|
||||
return {
|
||||
template,
|
||||
allSportsSeasons: allSportsSeasons as Array<typeof allSportsSeasons[0] & { sport: { id: string; name: string; type: string; slug: string } }>
|
||||
};
|
||||
return { template, allSports };
|
||||
}
|
||||
|
||||
export async function action({ request, params }: Route.ActionArgs) {
|
||||
|
|
@ -82,45 +78,34 @@ export async function action({ request, params }: Route.ActionArgs) {
|
|||
}
|
||||
|
||||
if (intent === "add-sport") {
|
||||
const sportsSeasonId = formData.get("sportsSeasonId");
|
||||
|
||||
if (typeof sportsSeasonId === "string") {
|
||||
await addSportToTemplate(params.id, sportsSeasonId);
|
||||
const sportId = formData.get("sportId");
|
||||
|
||||
if (typeof sportId === "string") {
|
||||
await addSportToTemplate(params.id, sportId);
|
||||
}
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
if (intent === "remove-sport") {
|
||||
const sportsSeasonId = formData.get("sportsSeasonId");
|
||||
|
||||
if (typeof sportsSeasonId === "string") {
|
||||
await removeSportFromTemplate(params.id, sportsSeasonId);
|
||||
const sportId = formData.get("sportId");
|
||||
|
||||
if (typeof sportId === "string") {
|
||||
await removeSportFromTemplate(params.id, sportId);
|
||||
}
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
// Update template
|
||||
const name = formData.get("name");
|
||||
const year = formData.get("year");
|
||||
const description = formData.get("description");
|
||||
|
||||
if (typeof name !== "string" || !name.trim()) {
|
||||
return { error: "Template name is required" };
|
||||
}
|
||||
|
||||
if (typeof year !== "string") {
|
||||
return { error: "Year is required" };
|
||||
}
|
||||
|
||||
const yearNum = parseInt(year, 10);
|
||||
if (isNaN(yearNum) || yearNum < 2000 || yearNum > 2100) {
|
||||
return { error: "Year must be between 2000 and 2100" };
|
||||
}
|
||||
|
||||
try {
|
||||
await updateSeasonTemplate(params.id, {
|
||||
name: name.trim(),
|
||||
year: yearNum,
|
||||
description: typeof description === "string" && description.trim() ? description.trim() : null,
|
||||
});
|
||||
|
||||
|
|
@ -132,15 +117,15 @@ export async function action({ request, params }: Route.ActionArgs) {
|
|||
}
|
||||
|
||||
export default function EditTemplate({ loaderData, actionData }: Route.ComponentProps) {
|
||||
const { template, allSportsSeasons } = loaderData;
|
||||
const { template, allSports } = loaderData;
|
||||
const [selectedSportId, setSelectedSportId] = useState("");
|
||||
|
||||
const includedSeasonIds = new Set(
|
||||
template.seasonTemplateSports.map((s) => s.sportsSeasonId)
|
||||
const includedSportIds = new Set(
|
||||
template.seasonTemplateSports.map((s) => s.sportId)
|
||||
);
|
||||
|
||||
const availableSportsSeasons = allSportsSeasons.filter(
|
||||
(s) => !includedSeasonIds.has(s.id)
|
||||
const availableSports = allSports.filter(
|
||||
(s) => !includedSportIds.has(s.id)
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
@ -170,19 +155,6 @@ export default function EditTemplate({ loaderData, actionData }: Route.Component
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="year">Year</Label>
|
||||
<Input
|
||||
id="year"
|
||||
name="year"
|
||||
type="number"
|
||||
min="2000"
|
||||
max="2100"
|
||||
defaultValue={template.year}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="description">Description (Optional)</Label>
|
||||
<Textarea
|
||||
|
|
@ -236,9 +208,9 @@ export default function EditTemplate({ loaderData, actionData }: Route.Component
|
|||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Sports Seasons</CardTitle>
|
||||
<CardTitle>Sports</CardTitle>
|
||||
<CardDescription>
|
||||
Add sports seasons to this template
|
||||
Add sports to this template. The active season for each sport will be used automatically when a league is created.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
|
|
@ -249,17 +221,12 @@ export default function EditTemplate({ loaderData, actionData }: Route.Component
|
|||
key={templateSport.id}
|
||||
className="flex items-center justify-between p-3 border rounded-lg"
|
||||
>
|
||||
<div>
|
||||
<p className="font-medium">
|
||||
{templateSport.sportsSeason.sport.name} - {templateSport.sportsSeason.name}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{templateSport.sportsSeason.year} • {templateSport.sportsSeason.scoringType.replace("_", " ")}
|
||||
</p>
|
||||
</div>
|
||||
<p className="font-medium">
|
||||
{templateSport.sport.name}
|
||||
</p>
|
||||
<Form method="post">
|
||||
<input type="hidden" name="intent" value="remove-sport" />
|
||||
<input type="hidden" name="sportsSeasonId" value={templateSport.sportsSeasonId} />
|
||||
<input type="hidden" name="sportId" value={templateSport.sportId} />
|
||||
<Button
|
||||
type="submit"
|
||||
variant="ghost"
|
||||
|
|
@ -274,22 +241,22 @@ export default function EditTemplate({ loaderData, actionData }: Route.Component
|
|||
</div>
|
||||
)}
|
||||
|
||||
{availableSportsSeasons.length > 0 ? (
|
||||
{availableSports.length > 0 ? (
|
||||
<Form method="post" className="flex gap-2">
|
||||
<input type="hidden" name="intent" value="add-sport" />
|
||||
<Select
|
||||
name="sportsSeasonId"
|
||||
name="sportId"
|
||||
value={selectedSportId}
|
||||
onValueChange={setSelectedSportId}
|
||||
required
|
||||
>
|
||||
<SelectTrigger className="flex-1">
|
||||
<SelectValue placeholder="Select a sports season" />
|
||||
<SelectValue placeholder="Select a sport" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{availableSportsSeasons.map((season) => (
|
||||
<SelectItem key={season.id} value={season.id}>
|
||||
{season.sport.name} - {season.name} ({season.year})
|
||||
{availableSports.map((sport) => (
|
||||
<SelectItem key={sport.id} value={sport.id}>
|
||||
{sport.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
|
|
@ -300,7 +267,7 @@ export default function EditTemplate({ loaderData, actionData }: Route.Component
|
|||
</Form>
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground text-center py-4">
|
||||
All available sports seasons have been added
|
||||
All available sports have been added
|
||||
</p>
|
||||
)}
|
||||
</CardContent>
|
||||
|
|
|
|||
|
|
@ -22,27 +22,15 @@ export function meta(): Route.MetaDescriptors {
|
|||
export async function action({ request }: Route.ActionArgs) {
|
||||
const formData = await request.formData();
|
||||
const name = formData.get("name");
|
||||
const year = formData.get("year");
|
||||
const description = formData.get("description");
|
||||
|
||||
// Validation
|
||||
if (typeof name !== "string" || !name.trim()) {
|
||||
return { error: "Template name is required" };
|
||||
}
|
||||
|
||||
if (typeof year !== "string") {
|
||||
return { error: "Year is required" };
|
||||
}
|
||||
|
||||
const yearNum = parseInt(year, 10);
|
||||
if (isNaN(yearNum) || yearNum < 2000 || yearNum > 2100) {
|
||||
return { error: "Year must be between 2000 and 2100" };
|
||||
}
|
||||
|
||||
try {
|
||||
const template = await createSeasonTemplate({
|
||||
name: name.trim(),
|
||||
year: yearNum,
|
||||
description: typeof description === "string" && description.trim() ? description.trim() : null,
|
||||
isActive: true,
|
||||
});
|
||||
|
|
@ -55,15 +43,13 @@ export async function action({ request }: Route.ActionArgs) {
|
|||
}
|
||||
|
||||
export default function NewTemplate({ actionData }: Route.ComponentProps) {
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<div className="p-8">
|
||||
<div className="max-w-2xl">
|
||||
<div className="mb-6">
|
||||
<h1 className="text-3xl font-bold">Create Season Template</h1>
|
||||
<p className="text-muted-foreground mt-1">
|
||||
Bundle sports seasons together for league commissioners
|
||||
Bundle sports together for league commissioners
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -82,7 +68,7 @@ export default function NewTemplate({ actionData }: Route.ComponentProps) {
|
|||
id="name"
|
||||
name="name"
|
||||
type="text"
|
||||
placeholder="e.g., 2025 Full Season, 2025 Playoffs Only"
|
||||
placeholder="e.g., Full Season, Playoffs Only"
|
||||
required
|
||||
/>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
|
|
@ -90,25 +76,12 @@ export default function NewTemplate({ actionData }: Route.ComponentProps) {
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="year">Year</Label>
|
||||
<Input
|
||||
id="year"
|
||||
name="year"
|
||||
type="number"
|
||||
min="2000"
|
||||
max="2100"
|
||||
defaultValue={currentYear}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="description">Description (Optional)</Label>
|
||||
<Textarea
|
||||
id="description"
|
||||
name="description"
|
||||
placeholder="Describe what sports seasons are included in this template"
|
||||
placeholder="Describe what sports are included in this template"
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -138,7 +111,7 @@ export default function NewTemplate({ actionData }: Route.ComponentProps) {
|
|||
<CardContent className="text-sm text-foreground/80">
|
||||
<p>After creating the template, you'll be able to:</p>
|
||||
<ul className="list-disc list-inside mt-2 space-y-1">
|
||||
<li>Add sports seasons to the template</li>
|
||||
<li>Add sports to the template</li>
|
||||
<li>Mark which sports are required vs optional</li>
|
||||
<li>Set the number of flex spots</li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -77,7 +77,6 @@ export default function AdminTemplates({ loaderData }: Route.ComponentProps) {
|
|||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Name</TableHead>
|
||||
<TableHead>Year</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Description</TableHead>
|
||||
<TableHead className="text-right">Actions</TableHead>
|
||||
|
|
@ -87,7 +86,6 @@ export default function AdminTemplates({ loaderData }: Route.ComponentProps) {
|
|||
{templates.map((template) => (
|
||||
<TableRow key={template.id}>
|
||||
<TableCell className="font-medium">{template.name}</TableCell>
|
||||
<TableCell>{template.year}</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={template.isActive ? "default" : "secondary"}>
|
||||
{template.isActive ? "Active" : "Inactive"}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ import { createCommissioner } from "~/models/commissioner";
|
|||
import { createSeason } from "~/models/season";
|
||||
import { linkMultipleSportsToSeason } from "~/models/season-sport";
|
||||
import { createManyTeams } from "~/models/team";
|
||||
import { findActiveSeasonTemplates, findSeasonTemplateWithSportsSeasons } from "~/models/season-template";
|
||||
import { findDraftableSportsSeasons } from "~/models/sports-season";
|
||||
import { findActiveSeasonTemplates, findSeasonTemplateWithSports } from "~/models/season-template";
|
||||
import { findDraftableSportsSeasons, findDraftableSportsSeasonBySportId } from "~/models/sports-season";
|
||||
import { findUserById, updateUser } from "~/models/user";
|
||||
import { generateUniqueTeamNames, prependOwnerToTeamName } from "~/utils/team-names";
|
||||
import { format } from "date-fns";
|
||||
|
|
@ -57,7 +57,6 @@ type SportSeason = {
|
|||
type Template = {
|
||||
id: string;
|
||||
name: string;
|
||||
year: number;
|
||||
description: string | null;
|
||||
sportsSeasonIds: string[];
|
||||
};
|
||||
|
|
@ -106,12 +105,17 @@ export async function loader(args: Route.LoaderArgs) {
|
|||
|
||||
const templatesWithSports = await Promise.all(
|
||||
templates.map(async (template) => {
|
||||
const t = await findSeasonTemplateWithSportsSeasons(template.id);
|
||||
const t = await findSeasonTemplateWithSports(template.id);
|
||||
const resolvedSeasons = await Promise.all(
|
||||
(t?.seasonTemplateSports ?? []).map((ts: { sportId: string }) =>
|
||||
findDraftableSportsSeasonBySportId(ts.sportId)
|
||||
)
|
||||
);
|
||||
return {
|
||||
...template,
|
||||
sportsSeasonIds: t?.seasonTemplateSports?.map(
|
||||
(ts: { sportsSeason: { id: string } }) => ts.sportsSeason.id
|
||||
) ?? [],
|
||||
sportsSeasonIds: resolvedSeasons
|
||||
.filter((s): s is NonNullable<typeof s> => s != null)
|
||||
.map((s) => s.id),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
|
|
|||
|
|
@ -80,14 +80,11 @@ const ImportDataSchema = z.object({
|
|||
seasonTemplates: z.array(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
year: z.number(),
|
||||
description: z.string().nullable(),
|
||||
isActive: z.boolean(),
|
||||
sports: z.array(
|
||||
z.object({
|
||||
sportSlug: z.string(),
|
||||
seasonName: z.string(),
|
||||
seasonYear: z.number(),
|
||||
})
|
||||
),
|
||||
})
|
||||
|
|
@ -133,10 +130,10 @@ export async function exportSportsDataToJSON(): Promise<ExportData> {
|
|||
db.query.seasonTemplates.findMany({
|
||||
with: {
|
||||
seasonTemplateSports: {
|
||||
with: { sportsSeason: { with: { sport: true } } },
|
||||
with: { sport: true },
|
||||
},
|
||||
},
|
||||
orderBy: (t, { desc }) => [desc(t.year)],
|
||||
orderBy: (t, { asc }) => [asc(t.name)],
|
||||
}),
|
||||
]);
|
||||
|
||||
|
|
@ -197,13 +194,10 @@ export async function exportSportsDataToJSON(): Promise<ExportData> {
|
|||
})),
|
||||
seasonTemplates: seasonTemplates.map((t) => ({
|
||||
name: t.name,
|
||||
year: t.year,
|
||||
description: t.description,
|
||||
isActive: t.isActive,
|
||||
sports: t.seasonTemplateSports.map((ts) => ({
|
||||
sportSlug: ts.sportsSeason.sport.slug,
|
||||
seasonName: ts.sportsSeason.name,
|
||||
seasonYear: ts.sportsSeason.year,
|
||||
sportSlug: ts.sport.slug,
|
||||
})),
|
||||
})),
|
||||
};
|
||||
|
|
@ -529,10 +523,7 @@ export async function importSportsDataFromJSON(
|
|||
|
||||
for (const template of importData.seasonTemplates) {
|
||||
const existing = await tx.query.seasonTemplates.findFirst({
|
||||
where: and(
|
||||
eq(schema.seasonTemplates.name, template.name),
|
||||
eq(schema.seasonTemplates.year, template.year)
|
||||
),
|
||||
where: eq(schema.seasonTemplates.name, template.name),
|
||||
});
|
||||
|
||||
let templateId: string;
|
||||
|
|
@ -561,7 +552,6 @@ export async function importSportsDataFromJSON(
|
|||
.insert(schema.seasonTemplates)
|
||||
.values({
|
||||
name: template.name,
|
||||
year: template.year,
|
||||
description: template.description,
|
||||
isActive: template.isActive,
|
||||
})
|
||||
|
|
@ -571,12 +561,11 @@ export async function importSportsDataFromJSON(
|
|||
}
|
||||
|
||||
for (const templateSport of template.sports) {
|
||||
const seasonKey = `${templateSport.sportSlug}:${templateSport.seasonName}:${templateSport.seasonYear}`;
|
||||
const sportsSeasonId = sportsSeasonIdMap.get(seasonKey);
|
||||
if (sportsSeasonId) {
|
||||
const sportId = sportIdMap.get(templateSport.sportSlug);
|
||||
if (sportId) {
|
||||
await tx.insert(schema.seasonTemplateSports).values({
|
||||
templateId,
|
||||
sportsSeasonId,
|
||||
sportId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,7 +190,6 @@ export const seasonTemplates = pgTable("season_templates", {
|
|||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
name: varchar("name", { length: 255 }).notNull(),
|
||||
description: text("description"),
|
||||
year: integer("year").notNull(),
|
||||
isActive: boolean("is_active").notNull().default(true),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
|
|
@ -567,9 +566,9 @@ export const seasonTemplateSports = pgTable("season_template_sports", {
|
|||
templateId: uuid("template_id")
|
||||
.notNull()
|
||||
.references(() => seasonTemplates.id, { onDelete: "cascade" }),
|
||||
sportsSeasonId: uuid("sports_season_id")
|
||||
sportId: uuid("sport_id")
|
||||
.notNull()
|
||||
.references(() => sportsSeasons.id, { onDelete: "cascade" }),
|
||||
.references(() => sports.id, { onDelete: "cascade" }),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
|
|
@ -974,6 +973,7 @@ export const sportsRelations = relations(sports, ({ many }) => ({
|
|||
sportsSeasons: many(sportsSeasons),
|
||||
tournaments: many(tournaments),
|
||||
participants: many(participants),
|
||||
seasonTemplateSports: many(seasonTemplateSports),
|
||||
}));
|
||||
|
||||
export const sportsSeasonsRelations = relations(sportsSeasons, ({ one, many }) => ({
|
||||
|
|
@ -988,7 +988,6 @@ export const sportsSeasonsRelations = relations(sportsSeasons, ({ one, many }) =
|
|||
participants: many(seasonParticipants),
|
||||
simulatorConfig: one(sportsSeasonSimulatorConfigs),
|
||||
simulatorInputs: many(seasonParticipantSimulatorInputs),
|
||||
seasonTemplateSports: many(seasonTemplateSports),
|
||||
seasonSports: many(seasonSports),
|
||||
participantResults: many(seasonParticipantResults),
|
||||
regularSeasonStandings: many(regularSeasonStandings),
|
||||
|
|
@ -1092,9 +1091,9 @@ export const seasonTemplateSportsRelations = relations(seasonTemplateSports, ({
|
|||
fields: [seasonTemplateSports.templateId],
|
||||
references: [seasonTemplates.id],
|
||||
}),
|
||||
sportsSeason: one(sportsSeasons, {
|
||||
fields: [seasonTemplateSports.sportsSeasonId],
|
||||
references: [sportsSeasons.id],
|
||||
sport: one(sports, {
|
||||
fields: [seasonTemplateSports.sportId],
|
||||
references: [sports.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
|
|
|
|||
11
drizzle/0114_warm_swarm.sql
Normal file
11
drizzle/0114_warm_swarm.sql
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
ALTER TABLE "season_template_sports" DROP CONSTRAINT "season_template_sports_sports_season_id_sports_seasons_id_fk";
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "season_template_sports" ADD COLUMN "sport_id" uuid NOT NULL;--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "season_template_sports" ADD CONSTRAINT "season_template_sports_sport_id_sports_id_fk" FOREIGN KEY ("sport_id") REFERENCES "public"."sports"("id") ON DELETE cascade ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "season_template_sports" DROP COLUMN IF EXISTS "sports_season_id";--> statement-breakpoint
|
||||
ALTER TABLE "season_templates" DROP COLUMN IF EXISTS "year";
|
||||
6187
drizzle/meta/0114_snapshot.json
Normal file
6187
drizzle/meta/0114_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -799,6 +799,13 @@
|
|||
"when": 1779579877169,
|
||||
"tag": "0113_bent_banshee",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 114,
|
||||
"version": "7",
|
||||
"when": 1780595525976,
|
||||
"tag": "0114_warm_swarm",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue