import { Navigate, createFileRoute } from "@tanstack/react-router"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useEffect, useState } from "react"; import type { ModelOption, Profile, ProfileUpdate, TeamSettings, UserMapping, } from "@/lib/api"; import { AppShell, SettingsRow, SettingsSection } from "@/components/AppShell"; import { ProfileForm } from "@/components/ProfileForm"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Skeleton } from "@/components/ui/skeleton"; import { api } from "@/lib/api"; import { useSession } from "@/lib/session"; export const Route = createFileRoute("/admin")({ component: AdminPage }); function AdminPage() { const session = useSession(); const qc = useQueryClient(); const [selected, setSelected] = useState(null); const [error, setError] = useState(null); const options = useQuery({ queryKey: ["options"], queryFn: api.options, enabled: !!session.data?.is_admin, }); const profiles = useQuery({ queryKey: ["adminProfiles"], queryFn: api.adminListProfiles, enabled: !!session.data?.is_admin, }); const save = useMutation({ mutationFn: ({ login, body }: { login: string; body: ProfileUpdate }) => api.adminSaveProfile(login, body), onSuccess: () => { void qc.invalidateQueries({ queryKey: ["adminProfiles"] }); setError(null); }, onError: (e: Error) => setError(e.message), }); if (session.isLoading) { return (
); } if (!session.data) return ; if (!session.data.is_admin) return ; const activeProfile: Profile | null = (selected && profiles.data?.find((p) => p.login === selected)) || null; return (
{profiles.isLoading ? ( ) : ( profiles.data?.map((p) => ( )) )}
{!activeProfile ? (

Pick a user on the left to edit their profile.

) : options.isLoading ? ( ) : ( save.mutateAsync({ login: activeProfile.login!, body }) } saving={save.isPending} error={error} /> )}
); } function UserMappingsSection({ enabled }: { enabled: boolean }) { const qc = useQueryClient(); const [login, setLogin] = useState(""); const [email, setEmail] = useState(""); const [slackId, setSlackId] = useState(""); const [error, setError] = useState(null); const [notice, setNotice] = useState(null); const mappings = useQuery({ queryKey: ["adminUserMappings"], queryFn: api.adminListUserMappings, enabled, }); const invalidate = () => void qc.invalidateQueries({ queryKey: ["adminUserMappings"] }); const save = useMutation({ mutationFn: () => api.adminSaveUserMapping({ github_login: login.trim(), work_email: email.trim(), slack_user_id: slackId.trim() || null, }), onSuccess: () => { setLogin(""); setEmail(""); setSlackId(""); setError(null); setNotice(null); invalidate(); }, onError: (e: Error) => setError(e.message), }); const remove = useMutation({ mutationFn: (gh: string) => api.adminDeleteUserMapping(gh), onSuccess: invalidate, onError: (e: Error) => setError(e.message), }); const importLegacy = useMutation({ mutationFn: api.adminImportUserMappings, onSuccess: (res) => { setNotice(`Imported ${res.created} legacy mapping(s).`); setError(null); invalidate(); }, onError: (e: Error) => setError(e.message), }); return (
setLogin(e.target.value)} /> setEmail(e.target.value)} /> setSlackId(e.target.value)} />
{notice && {notice}} {error && {error}}
{mappings.isLoading ? ( ) : !mappings.data?.length ? (

No mappings yet.

) : ( mappings.data.map((m: UserMapping) => (
{m.github_login} {m.work_email} {m.slack_user_id ? ` · ${m.slack_user_id}` : ""} {m.source ? ` · ${m.source}` : ""}
)) )}
); } function GlobalDefaultsSection({ models }: { models: Array }) { const qc = useQueryClient(); const settings = useQuery({ queryKey: ["teamSettings"], queryFn: api.getTeamSettings, }); const [error, setError] = useState(null); const save = useMutation({ mutationFn: (body: TeamSettings) => api.saveTeamSettings(body), onSuccess: (saved) => { qc.setQueryData(["teamSettings"], saved); setError(null); }, onError: (e: Error) => setError(e.message), }); return (
settings.data && save.mutate({ ...settings.data, default_agent_model: model, default_agent_reasoning_effort: effort, }) } disabled={!settings.data || save.isPending} /> settings.data && save.mutate({ ...settings.data, default_agent_subagent_model: model, default_agent_subagent_reasoning_effort: effort, }) } disabled={!settings.data || save.isPending} /> settings.data && save.mutate({ ...settings.data, default_reviewer_model: model, default_reviewer_reasoning_effort: effort, }) } disabled={!settings.data || save.isPending} /> settings.data && save.mutate({ ...settings.data, default_reviewer_subagent_model: model, default_reviewer_subagent_reasoning_effort: effort, }) } disabled={!settings.data || save.isPending} />
{error &&

{error}

}
); } interface RolePickerProps { label: string; description: string; models: Array; model: string | null; effort: string | null; onChange: (model: string, effort: string) => void; disabled: boolean; } function RolePicker({ label, description, models, model, effort, onChange, disabled, }: RolePickerProps) { const [localModel, setLocalModel] = useState(model ?? ""); const [localEffort, setLocalEffort] = useState(effort ?? ""); useEffect(() => { setLocalModel(model ?? ""); setLocalEffort(effort ?? ""); }, [model, effort]); const selectedModel = models.find((m) => m.id === localModel); const availableEfforts = selectedModel?.efforts ?? []; const handleModelChange = (value: string | null) => { if (!value) return; const nextModel = models.find((m) => m.id === value); if (!nextModel) return; const nextEffort = nextModel.efforts.includes(localEffort) ? localEffort : nextModel.default_effort; setLocalModel(value); setLocalEffort(nextEffort); onChange(value, nextEffort); }; const handleEffortChange = (value: string | null) => { if (!value || !localModel) return; setLocalEffort(value); onChange(localModel, value); }; return ( } /> ); }