import { Form, redirect, useNavigate } from "react-router"; import { auth } from "~/lib/auth.server"; import type { Route } from "./+types/$teamId.settings"; import { findTeamById, findTeamByNameInSeason, updateTeam, removeTeamOwner, } from "~/models/team"; import { findSeasonById } from "~/models/season"; import { findUserById } from "~/models/user"; import { resolveUserAvatarData } from "~/lib/avatar-data"; import { Button } from "~/components/ui/button"; import { TeamAvatar } from "~/components/TeamAvatar"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "~/components/ui/card"; import { Input } from "~/components/ui/input"; import { Label } from "~/components/ui/label"; import { AvatarEditor } from "~/components/ui/AvatarEditor"; import { parseFlagConfig } from "~/lib/flag-types"; import { deleteCloudinaryImageByUrl } from "~/lib/cloudinary.server"; import { logger } from "~/lib/logger"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "~/components/ui/alert-dialog"; import { syncPrivateBracktParticipants } from "~/services/brackt.server"; export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors { return [{ title: `Team Settings — ${data?.team?.name ?? "Team"} - Brackt` }]; } export async function loader(args: Route.LoaderArgs) { const { params } = args; const { teamId } = params; const session = await auth.api.getSession({ headers: args.request.headers }); const userId = session?.user.id ?? null; if (!userId) { throw new Response("You must be logged in", { status: 401 }); } const team = await findTeamById(teamId); if (!team) { throw new Response("Team not found", { status: 404 }); } // Only the team owner can access settings if (team.ownerId !== userId) { throw new Response("You do not have access to this team", { status: 403 }); } const season = await findSeasonById(team.seasonId); if (!season) { throw new Response("Season not found", { status: 404 }); } const owner = await findUserById(userId); const ownerAvatarData = owner ? resolveUserAvatarData(owner) : null; return { team, season, ownerAvatarData }; } export async function action(args: Route.ActionArgs) { const { params, request } = args; const { teamId } = params; const session = await auth.api.getSession({ headers: args.request.headers }); const userId = session?.user.id ?? null; if (!userId) { throw new Response("You must be logged in", { status: 401 }); } const team = await findTeamById(teamId); if (!team) { throw new Response("Team not found", { status: 404 }); } // Only the team owner can modify settings if (team.ownerId !== userId) { throw new Response("You do not have access to this team", { status: 403 }); } const formData = await request.formData(); const intent = formData.get("intent"); if (intent === "update-avatar-flag") { const rawConfig = formData.get("flagConfig"); if (typeof rawConfig !== "string") { return { error: "Flag config is required" }; } let parsedConfig: unknown; try { parsedConfig = JSON.parse(rawConfig); } catch { return { error: "Invalid flag config" }; } const flagConfig = parseFlagConfig(parsedConfig); if (!flagConfig) { return { error: "Invalid flag config" }; } await updateTeam(teamId, { flagConfig, avatarType: "flag", }); return { success: true }; } if (intent === "remove-avatar-photo") { await updateTeam(teamId, { logoUrl: null, avatarType: "owner", }); if (team.logoUrl) { deleteCloudinaryImageByUrl(team.logoUrl).catch((error) => { logger.error("Failed to delete team avatar from Cloudinary:", error); }); } return { success: true }; } if (intent === "use-owner-avatar") { await updateTeam(teamId, { avatarType: "owner", flagConfig: null }); if (team.avatarType === "uploaded" && team.logoUrl) { deleteCloudinaryImageByUrl(team.logoUrl).catch((error) => { logger.error("Failed to delete replaced team avatar from Cloudinary:", error); }); } return { success: true }; } if (intent === "update") { const name = formData.get("name"); if (!name || typeof name !== "string") { return { error: "Team name is required" }; } const trimmedName = name.trim(); if (!trimmedName) { return { error: "Team name is required" }; } const conflict = await findTeamByNameInSeason(team.seasonId, trimmedName, teamId); if (conflict) { return { error: "A team with that name already exists in this season." }; } await updateTeam(teamId, { name: trimmedName, }); await syncPrivateBracktParticipants(team.seasonId); const season = await findSeasonById(team.seasonId); return redirect(`/leagues/${season?.leagueId}?updated=true`); } if (intent === "leave") { await removeTeamOwner(teamId); return redirect("/?left=true"); } return { error: "Invalid action" }; } export default function TeamSettings({ loaderData, actionData }: Route.ComponentProps) { const { team, season } = loaderData; const navigate = useNavigate(); return (
Manage your team settings and preferences
Team settings updated.
)} {actionData && "error" in actionData && actionData.error && ({actionData.error}
)}