import { Navigate, createFileRoute, useNavigate } from "@tanstack/react-router" import { useQuery, useQueryClient } from "@tanstack/react-query" import { useState } from "react" import { IoLogoSlack } from "react-icons/io5" import type { SessionUser } from "@/lib/api" import { AppShell, SettingsRow, SettingsSection } from "@/components/AppShell" import { Button } from "@/components/ui/button" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Skeleton } from "@/components/ui/skeleton" import { api, slackConnectUrl } from "@/lib/api" import { buildProfileUpdate, useOptions, useProfile, useSaveProfile, } from "@/lib/profile" import { useSession } from "@/lib/session" import { cn } from "@/lib/utils" export const Route = createFileRoute("/my-settings")({ component: MySettingsPage, }) type DraftReviewChoice = "team_default" | "always_on" | "always_off" function toChoice(value: boolean | null | undefined): DraftReviewChoice { if (value === true) return "always_on" if (value === false) return "always_off" return "team_default" } function fromChoice(choice: DraftReviewChoice): boolean | null { if (choice === "always_on") return true if (choice === "always_off") return false return null } function UserMappingSection({ session }: { session: SessionUser }) { const qc = useQueryClient() const mapping = useQuery({ queryKey: ["myMapping"], queryFn: api.myMapping }) const [connecting, setConnecting] = useState(false) const slackUserId = mapping.data?.slack_user_id ?? null const workEmail = mapping.data?.work_email ?? null const connected = !!slackUserId const connect = () => { setConnecting(true) // Refresh the cached mapping when the user returns from the OAuth redirect. void qc.invalidateQueries({ queryKey: ["myMapping"] }) window.location.assign(slackConnectUrl()) } return ( {session.login} } /> {connected ? "Connected" : "Not connected"} {session.slack_oauth_enabled ? ( {connecting ? "Redirecting…" : connected ? "Reconnect" : "Connect Slack"} ) : ( Sign in with Slack unavailable )} } /> ) } function MySettingsPage() { const session = useSession() const qc = useQueryClient() const navigate = useNavigate() const profile = useProfile() const options = useOptions() const save = useSaveProfile() const teamSettings = useQuery({ queryKey: ["teamSettings"], queryFn: api.getTeamSettings, enabled: !!session.data, }) const [error, setError] = useState(null) if (session.isLoading) { return ( ) } if (!session.data) return const handleLogout = async () => { await api.logout() qc.setQueryData(["session"], null) void navigate({ to: "/login" }) } const firstModel = options.data?.models[0] const fallbackModel = options.data?.default_agent_model ?? firstModel?.id ?? "" const fallbackEffort = options.data?.default_agent_reasoning_effort ?? firstModel?.default_effort ?? "" const draftChoice = toChoice(profile.data?.review_draft_prs) const teamDefaultOn = teamSettings.data?.review_draft_prs ?? false const teamDefaultLabel = `Use team default (currently: ${teamDefaultOn ? "On" : "Off"})` const handleDraftChoiceChange = (next: DraftReviewChoice) => { setError(null) save .mutateAsync( buildProfileUpdate( profile.data, { review_draft_prs: fromChoice(next) }, fallbackModel, fallbackEffort ) ) .catch((e: Error) => setError(e.message)) } return ( {session.data.email ?? "—"} } /> handleDraftChoiceChange(v as DraftReviewChoice) } disabled={profile.isLoading || save.isPending} > {teamDefaultLabel} Always review my drafts Never review my drafts } /> void handleLogout()} > Sign out } /> {error && {error}} ) }
{error}