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 = firstModel?.id ?? ""; const fallbackEffort = 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}