import { Link, Navigate, createFileRoute } from "@tanstack/react-router"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { CaretRightIcon } from "@phosphor-icons/react"; import { useEffect, useMemo, useState } from "react"; import { IoLogoGithub } from "react-icons/io5"; import type { AutofixMode, ReposPayload, TeamSettings, TriggerMode } from "@/lib/api"; import { AppShell, SettingsRow, SettingsSection } from "@/components/AppShell"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; import { Switch } from "@/components/ui/switch"; import { Textarea } from "@/components/ui/textarea"; import { ApiError, api } from "@/lib/api"; import { useSession } from "@/lib/session"; export const Route = createFileRoute("/review")({ component: ReviewPage }); const TRIGGER_MODES: Array<{ value: TriggerMode; label: string; description: string }> = [ { value: "every_push", label: "Every Push", description: "Review on every push to the PR", }, { value: "once_per_pr", label: "Once Per PR", description: "Review once when the PR is opened, skip subsequent pushes", }, { value: "manual", label: "Manual Only", description: "Only review when '@open-swe review' is commented", }, ]; const AUTOFIX_MODES: Array<{ value: AutofixMode; label: string }> = [ { value: "off", label: "Off" }, { value: "low", label: "Low" }, { value: "medium", label: "Medium" }, { value: "high", label: "High" }, ]; const DEFAULT_SETTINGS: TeamSettings = { trigger_mode: "every_push", review_draft_prs: false, pr_summaries: true, review_trace_links: true, autofix_mode: "off", autofix_severity_threshold: "medium", org_guidelines: null, default_agent_model: null, default_agent_reasoning_effort: null, default_agent_subagent_model: null, default_agent_subagent_reasoning_effort: null, default_reviewer_model: null, default_reviewer_reasoning_effort: null, default_reviewer_subagent_model: null, default_reviewer_subagent_reasoning_effort: null, }; function ReviewPage() { const session = useSession(); const qc = useQueryClient(); const settings = useQuery({ queryKey: ["teamSettings"], queryFn: api.getTeamSettings, enabled: !!session.data, }); const [local, setLocal] = useState(DEFAULT_SETTINGS); const [guidelinesDraft, setGuidelinesDraft] = useState(""); const [error, setError] = useState(null); useEffect(() => { if (settings.data) { setLocal(settings.data); setGuidelinesDraft(settings.data.org_guidelines ?? ""); } }, [settings.data]); const save = useMutation({ mutationFn: (body: TeamSettings) => api.saveTeamSettings(body), onSuccess: (saved) => { qc.setQueryData(["teamSettings"], saved); setError(null); }, onError: (e: Error) => setError(e.message), }); if (session.isLoading) { return (
); } if (!session.data) return ; const current: TeamSettings = local; const canEdit = session.data.is_admin; const persist = (patch: Partial) => { const next: TeamSettings = { ...current, ...patch }; setLocal(next); if (canEdit) save.mutate(next); }; const triggerDescription = TRIGGER_MODES.find((m) => m.value === current.trigger_mode)?.description ?? "Open SWE Review will automatically review every push to a PR"; const trimmedGuidelines = guidelinesDraft.trim(); const savedGuidelines = current.org_guidelines ?? ""; const guidelinesDirty = trimmedGuidelines !== savedGuidelines.trim(); const saveGuidelines = () => { if (!canEdit) return; persist({ org_guidelines: trimmedGuidelines || null }); }; return (
Review Style Prompts Per-repo style guides learned from past PR review feedback.