diff --git a/app/components/ui/radio-group.tsx b/app/components/ui/radio-group.tsx new file mode 100644 index 0000000..2afec97 --- /dev/null +++ b/app/components/ui/radio-group.tsx @@ -0,0 +1,42 @@ +import * as React from "react" +import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" +import { Circle } from "lucide-react" + +import { cn } from "~/lib/utils" + +const RadioGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + return ( + + ) +}) +RadioGroup.displayName = RadioGroupPrimitive.Root.displayName + +const RadioGroupItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + return ( + + + + + + ) +}) +RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName + +export { RadioGroup, RadioGroupItem } diff --git a/app/models/season.ts b/app/models/season.ts index 3c4c77d..2645217 100644 --- a/app/models/season.ts +++ b/app/models/season.ts @@ -59,6 +59,27 @@ export async function findCurrentSeason( }); } +export async function findCurrentSeasonWithSports( + leagueId: string +): Promise { + const db = database(); + return await db.query.seasons.findFirst({ + where: eq(schema.seasons.leagueId, leagueId), + orderBy: (seasons, { desc }) => [desc(seasons.year)], + with: { + seasonSports: { + with: { + sportsSeason: { + with: { + sport: true, + }, + }, + }, + }, + }, + }) as SeasonWithSportsSeasons | undefined; +} + export async function findSeasonByLeagueAndYear( leagueId: string, year: number diff --git a/app/routes/leagues/$leagueId.settings.tsx b/app/routes/leagues/$leagueId.settings.tsx index d85606b..cd33f3c 100644 --- a/app/routes/leagues/$leagueId.settings.tsx +++ b/app/routes/leagues/$leagueId.settings.tsx @@ -3,8 +3,10 @@ import { Form, Link, redirect } from "react-router"; import { getAuth } from "@clerk/react-router/server"; import { findLeagueById, updateLeague, deleteLeague } from "~/models/league"; import { isCommissioner } from "~/models/commissioner"; -import { findCurrentSeason } from "~/models/season"; -import { findTeamsBySeasonId } from "~/models/team"; +import { findCurrentSeasonWithSports } from "~/models/season"; +import { findTeamsBySeasonId, createManyTeams, deleteTeam } from "~/models/team"; +import { unlinkSportFromSeason, linkMultipleSportsToSeason } from "~/models/season-sport"; +import { findAllSportsSeasons } from "~/models/sports-season"; import type { Route } from "./+types/$leagueId.settings"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; @@ -23,6 +25,8 @@ import { SelectTrigger, SelectValue, } from "~/components/ui/select"; +import { Badge } from "~/components/ui/badge"; +import { Checkbox } from "~/components/ui/checkbox"; import { AlertDialog, AlertDialogAction, @@ -61,13 +65,21 @@ export async function loader(args: Route.LoaderArgs) { }); } - // Get current season to count teams - const season = await findCurrentSeason(leagueId); + // Get current season with sports + const season = await findCurrentSeasonWithSports(leagueId); const teams = season ? await findTeamsBySeasonId(season.id) : []; + const allSportsSeasons = await findAllSportsSeasons(); + + // Count teams with owners + const teamsWithOwners = teams.filter(team => team.ownerId !== null).length; return { league, + season, + teams, teamCount: teams.length, + teamsWithOwners, + allSportsSeasons: allSportsSeasons as Array, }; } @@ -96,6 +108,48 @@ export async function action(args: Route.ActionArgs) { const formData = await request.formData(); const intent = formData.get("intent"); + // Get current season to check status + const season = await findCurrentSeasonWithSports(leagueId); + if (!season) { + return { error: "No active season found" }; + } + + if (intent === "update-sports") { + if (season.status !== "pre_draft") { + return { error: "Cannot modify sports after draft has started" }; + } + + const selectedSports = formData.getAll("sportsSeasons"); + const currentSportIds = new Set( + season.seasonSports?.map((s: any) => s.sportsSeason.id) || [] + ); + const newSportIds = new Set(selectedSports as string[]); + + // Remove sports that are no longer selected + for (const sportId of currentSportIds) { + if (!newSportIds.has(sportId)) { + await unlinkSportFromSeason(season.id, sportId); + } + } + + // Add newly selected sports + const sportsToAdd = []; + for (const sportId of newSportIds) { + if (!currentSportIds.has(sportId)) { + sportsToAdd.push({ + seasonId: season.id, + sportsSeasonId: sportId as string, + }); + } + } + + if (sportsToAdd.length > 0) { + await linkMultipleSportsToSeason(sportsToAdd); + } + + return { success: true }; + } + if (intent === "delete") { // Delete the league await deleteLeague(leagueId); @@ -106,6 +160,7 @@ export async function action(args: Route.ActionArgs) { if (intent === "update") { const name = formData.get("name"); + const teamCount = formData.get("teamCount"); // Validation if (typeof name !== "string" || !name.trim()) { @@ -121,6 +176,49 @@ export async function action(args: Route.ActionArgs) { name: name.trim(), }); + // Handle team count changes if provided + if (typeof teamCount === "string") { + const newTeamCount = parseInt(teamCount, 10); + + if (!isNaN(newTeamCount)) { + const teams = await findTeamsBySeasonId(season.id); + const currentTeamCount = teams.length; + const teamsWithOwners = teams.filter(t => t.ownerId !== null).length; + + // Validate team count + if (newTeamCount < 6 || newTeamCount > 16) { + return { error: "Number of teams must be between 6 and 16" }; + } + + if (newTeamCount < teamsWithOwners) { + return { error: `Cannot reduce team count below ${teamsWithOwners} (number of teams with owners)` }; + } + + // Add or remove teams as needed + if (newTeamCount > currentTeamCount) { + // Add new teams + const teamsToAdd = Array.from( + { length: newTeamCount - currentTeamCount }, + (_, i) => ({ + seasonId: season.id, + name: `Team ${currentTeamCount + i + 1}`, + ownerId: null, + }) + ); + await createManyTeams(teamsToAdd); + } else if (newTeamCount < currentTeamCount) { + // Remove teams without owners (from the end) + const teamsToRemove = teams + .filter(t => t.ownerId === null) + .slice(-(currentTeamCount - newTeamCount)); + + for (const team of teamsToRemove) { + await deleteTeam(team.id); + } + } + } + } + return redirect(`/leagues/${leagueId}?updated=true`); } catch (error) { console.error("Error updating league:", error); @@ -132,8 +230,25 @@ export async function action(args: Route.ActionArgs) { } export default function LeagueSettings({ loaderData, actionData }: Route.ComponentProps) { - const { league, teamCount } = loaderData; + const { league, season, teamCount, teamsWithOwners, allSportsSeasons } = loaderData; const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); + const [selectedSports, setSelectedSports] = useState>( + new Set(season?.seasonSports?.map((s: any) => s.sportsSeason.id) || []) + ); + + const canEditSports = season && season.status === "pre_draft"; + const canEditTeamCount = season && season.status === "pre_draft"; + const minTeamCount = Math.max(6, teamsWithOwners); + + const handleSportToggle = (sportId: string) => { + const newSelected = new Set(selectedSports); + if (newSelected.has(sportId)) { + newSelected.delete(sportId); + } else { + newSelected.add(sportId); + } + setSelectedSports(newSelected); + }; const handleDelete = () => { setIsDeleteDialogOpen(false); @@ -181,20 +296,31 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone
- {Array.from({ length: 11 }, (_, i) => i + 6).map((num) => ( - + {num} Teams + {num < minTeamCount && ` (${teamsWithOwners} teams have owners)`} ))}

- Team count cannot be changed after league creation + {canEditTeamCount + ? `Can be changed before draft starts (minimum ${minTeamCount} based on registered teams)` + : "Team count cannot be changed after draft has started"}

@@ -211,6 +337,84 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone + {/* Sports Seasons Management */} + + + Sports Seasons + + {canEditSports + ? "Select the sports seasons that teams can draft from" + : "Sports cannot be modified after the draft has started"} + + + +
+ + +
+
+

+ Recommendation: Select 16-20 sports seasons for optimal league balance and variety. +

+
+ +
+ +
+ {allSportsSeasons.length > 0 ? ( + allSportsSeasons.map((season) => ( +
+ handleSportToggle(season.id)} + disabled={!canEditSports} + /> + +
+ )) + ) : ( +

+ No sports seasons available +

+ )} +
+
+
+ + {canEditSports && ( + + )} + + {actionData?.success && ( +
+ Sports updated successfully! +
+ )} + + {actionData?.error && ( +
+ {actionData.error} +
+ )} +
+
+
+ {/* Danger Zone */} diff --git a/app/routes/leagues/new.tsx b/app/routes/leagues/new.tsx index a1cd833..bb93d0a 100644 --- a/app/routes/leagues/new.tsx +++ b/app/routes/leagues/new.tsx @@ -4,10 +4,16 @@ import { createLeague, setCurrentSeason } from "~/models/league"; import { createSeason } from "~/models/season"; import { createManyTeams } from "~/models/team"; import { createCommissioner } from "~/models/commissioner"; +import { linkMultipleSportsToSeason } from "~/models/season-sport"; +import { findActiveSeasonTemplates, findSeasonTemplateWithSportsSeasons } from "~/models/season-template"; +import { findAllSportsSeasons } from "~/models/sports-season"; import type { Route } from "./+types/new"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; import { Label } from "~/components/ui/label"; +import { Checkbox } from "~/components/ui/checkbox"; +import { Check } from "lucide-react"; +import { useState } from "react"; import { Card, CardContent, @@ -22,6 +28,30 @@ import { SelectTrigger, SelectValue, } from "~/components/ui/select"; +import { Badge } from "~/components/ui/badge"; + +export async function loader() { + const templates = await findActiveSeasonTemplates(); + const allSportsSeasons = await findAllSportsSeasons(); + + // Fetch templates with their sports seasons for mapping + const templatesWithSports = await Promise.all( + templates.map(async (template) => { + const templateWithSports = await findSeasonTemplateWithSportsSeasons(template.id); + return { + ...template, + sportsSeasonIds: templateWithSports?.seasonTemplateSports?.map( + (ts: any) => ts.sportsSeason.id + ) || [] + }; + }) + ); + + return { + templates: templatesWithSports, + allSportsSeasons: allSportsSeasons as Array + }; +} export async function action(args: Route.ActionArgs) { const { userId } = await getAuth(args); @@ -34,6 +64,7 @@ export async function action(args: Route.ActionArgs) { const formData = await request.formData(); const name = formData.get("name"); const teamCount = formData.get("teamCount"); + const templateId = formData.get("templateId"); // Validation if (typeof name !== "string" || !name.trim()) { @@ -68,11 +99,23 @@ export async function action(args: Route.ActionArgs) { leagueId: league.id, year: currentYear, status: "pre_draft", + templateId: typeof templateId === "string" && templateId !== "" ? templateId : null, }); // Set this as the current season for the league await setCurrentSeason(league.id, season.id); + // Add selected sports to the season + const selectedSports = formData.getAll("sportsSeasons"); + if (selectedSports.length > 0) { + await linkMultipleSportsToSeason( + selectedSports.map((id) => ({ + seasonId: season.id, + sportsSeasonId: id as string, + })) + ); + } + // Create teams with placeholder names const teams = Array.from({ length: teamCountNum }, (_, i) => ({ seasonId: season.id, @@ -90,7 +133,25 @@ export async function action(args: Route.ActionArgs) { } } -export default function NewLeague({ actionData }: Route.ComponentProps) { +export default function NewLeague({ loaderData, actionData }: Route.ComponentProps) { + const { templates, allSportsSeasons } = loaderData; + const [selectedTemplate, setSelectedTemplate] = useState(""); + const [selectedSports, setSelectedSports] = useState>(new Set()); + + const handleSportToggle = (sportId: string) => { + const newSelected = new Set(selectedSports); + if (newSelected.has(sportId)) { + newSelected.delete(sportId); + } else { + newSelected.add(sportId); + } + setSelectedSports(newSelected); + }; + + const handleTemplateClick = (templateId: string, sportsSeasonIds: string[]) => { + setSelectedTemplate(templateId); + setSelectedSports(new Set(sportsSeasonIds)); + }; return (
@@ -140,6 +201,93 @@ export default function NewLeague({ actionData }: Route.ComponentProps) {

+
+ +

+ Choose a template to auto-select sports, or manually select your own +

+ + + + {/* Template Cards */} + {templates.length > 0 && ( +
+ {templates.map((template) => ( + handleTemplateClick(template.id, template.sportsSeasonIds)} + > + +
+
+ {template.name} + {template.year} +
+ {selectedTemplate === template.id && ( +
+ +
+ )} +
+
+ {template.description && ( + +

{template.description}

+
+ )} +
+ ))} +
+ )} + + {/* Sports Checkboxes */} +
+
+

+ Recommendation: Select 16-20 sports seasons for optimal league balance and variety. +

+
+
+ +
+ {allSportsSeasons.length > 0 ? ( + allSportsSeasons.map((season) => ( +
+ handleSportToggle(season.id)} + /> + +
+ )) + ) : ( +

+ No sports seasons available +

+ )} +
+
+
+
+ {actionData?.error && (
{actionData.error} diff --git a/package-lock.json b/package-lock.json index 0c92a7a..a5603ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-navigation-menu": "^1.2.14", + "@radix-ui/react-radio-group": "^1.3.8", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-slot": "^1.2.3", "@react-router/express": "^7.7.1", @@ -19,6 +20,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "compression": "^1.8.0", + "drizzle-kit": "~0.28.1", "drizzle-orm": "~0.36.3", "express": "^5.1.0", "isbot": "^5.1.27", @@ -45,7 +47,6 @@ "@types/react": "^19.1.2", "@types/react-dom": "^19.1.2", "dotenv-cli": "^8.0.0", - "drizzle-kit": "~0.28.1", "tailwindcss": "^4.1.4", "tsx": "^4.19.2", "tw-animate-css": "^1.4.0", @@ -628,7 +629,6 @@ "version": "0.10.2", "resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz", "integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==", - "dev": true, "license": "Apache-2.0" }, "node_modules/@esbuild-kit/core-utils": { @@ -636,7 +636,6 @@ "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", "integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==", "deprecated": "Merged into tsx: https://tsx.is", - "dev": true, "license": "MIT", "dependencies": { "esbuild": "~0.18.20", @@ -650,7 +649,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -667,7 +665,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -684,7 +681,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -701,7 +697,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -718,7 +713,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -735,7 +729,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -752,7 +745,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -769,7 +761,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -786,7 +777,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -803,7 +793,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -820,7 +809,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -837,7 +825,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -854,7 +841,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -871,7 +857,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -888,7 +873,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -905,7 +889,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -922,7 +905,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -939,7 +921,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -956,7 +937,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -973,7 +953,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -990,7 +969,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1007,7 +985,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1021,7 +998,6 @@ "version": "0.18.20", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -1060,7 +1036,6 @@ "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz", "integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==", "deprecated": "Merged into tsx: https://tsx.is", - "dev": true, "license": "MIT", "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", @@ -1074,7 +1049,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1091,7 +1065,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1108,7 +1081,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1125,7 +1097,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1142,7 +1113,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1159,7 +1129,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1176,7 +1145,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1193,7 +1161,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1210,7 +1177,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1227,7 +1193,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1244,7 +1209,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1261,7 +1225,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1278,7 +1241,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1295,7 +1257,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1312,7 +1273,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1329,7 +1289,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1346,7 +1305,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1380,7 +1338,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1414,7 +1371,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1448,7 +1404,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1465,7 +1420,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1482,7 +1436,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1499,7 +1452,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2154,6 +2106,69 @@ } } }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz", + "integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-select": { "version": "2.2.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", @@ -3445,7 +3460,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, "license": "MIT" }, "node_modules/bytes": { @@ -3821,7 +3835,6 @@ "version": "0.28.1", "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.28.1.tgz", "integrity": "sha512-JimOV+ystXTWMgZkLHYHf2w3oS28hxiH1FR0dkmJLc7GHzdGJoJAQtQS5DRppnabsRZwE2U1F6CuezVBgmsBBQ==", - "dev": true, "license": "MIT", "dependencies": { "@drizzle-team/brocli": "^0.10.2", @@ -4070,7 +4083,6 @@ "version": "0.19.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -4109,7 +4121,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", - "dev": true, "license": "MIT", "dependencies": { "debug": "^4.3.4" @@ -4358,7 +4369,6 @@ "version": "4.12.0", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.12.0.tgz", "integrity": "sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==", - "dev": true, "license": "MIT", "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -5697,7 +5707,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" @@ -5987,7 +5996,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -6007,7 +6015,6 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", diff --git a/package.json b/package.json index 122bf86..39abd23 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-navigation-menu": "^1.2.14", + "@radix-ui/react-radio-group": "^1.3.8", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-slot": "^1.2.3", "@react-router/express": "^7.7.1",