open-swe/ui/src/routes/my-settings.tsx

138 lines
4.3 KiB
TypeScript
Raw Normal View History

feat: restructure Open SWE Review tab + wire create_prs (#1319) * feat(dashboard): restructure Open SWE Review tab + wire create_prs Restructures the dashboard around two related changes the reviewer settings have been asking for: - Wire profile.create_prs. Defaults to true (opt-out); when off the system prompt gets a `Pull Request Policy Override` section telling the agent to push the branch and notify with the branch URL instead of opening a PR. Removes the noop Slack Notifications / Allow Artifacts / First Name / Last Name controls and their schema fields. - Repositories opt-in for Open SWE Review. New per-team enabled list stored in the LangGraph Store (`["enabled_review_repos"]`). Every reviewer webhook chokepoint now goes through `_is_repo_enabled_for_review` which AND-combines the existing env allowlist with the dashboard list. Default is empty (opt-in) — admins enable repos per-installation from the new Repositories page nested under Open SWE Review. - Open SWE Review tab now mirrors the Cursor "rules" pattern: main page shows installation rows + a Rules entry; both drill into nested pages (/review/repositories/$owner and /review/styles) with a back link. - Adds the new logo/favicon assets shipped from sidebar + html head. Tests pass with a new autouse fixture (`tests/conftest.py`) that defaults `is_review_repo_enabled` to True for existing allowlist tests. * fix(dashboard): make main content scroll independently of the sidebar Outer flex container was min-h-svh, so it grew with main's content and the whole page scrolled — sidebar moved with it. Pin to h-svh + overflow-hidden so the sidebar stays put and only <main> scrolls. * fix(dashboard): make disabled repo toggles obviously disabled Switch's disabled state used opacity-50 against a muted background, so the not-admin state looked nearly identical to the off state. Bump to opacity-40 + grayscale, and wrap each repo toggle in a span carrying a native hover tooltip explaining why it's disabled. * fix(switch): handle base-ui's data-disabled state base-ui's Switch.Root sets data-disabled (not the HTML disabled attribute) when disabled, so Tailwind's disabled: variant never matches and the button keeps its cursor-pointer + clickable look. Mirror the styling under the data-[disabled] variant and add pointer-events-none so the disabled state is both visible and actually unclickable. * feat(dashboard): paginate per-installation repository list 20 repos per page with Prev / page X of Y / Next controls at the bottom. Pager only renders when there are more than 20 repos. Page resets to 0 when navigating between installations. * feat(dashboard): global default model selectors for Agent + Reviewer Adds team-wide default model + reasoning effort for both agents in the Admin tab so operators can switch models without redeploying. Resolution chain: Agent: hardcoded -> LLM_MODEL_ID env -> team default -> user profile Reviewer: hardcoded -> LLM_MODEL_ID env -> team default -> per-call configurable Team defaults live in team_settings and are validated against the SUPPORTED_MODELS allowlist + the model's supported reasoning efforts. 'Inherit from env' clears the override and falls back to LLM_MODEL_ID. * refactor(models): drop LLM_MODEL_ID env in favour of the team default The team default is now the single source of truth for the runtime model choice; per-user (agent) and per-call configurable (reviewer) selections still win on top. When no admin has touched the team default, it surfaces the hardcoded fallback (DEFAULT_MODEL_ID + its default effort), so the admin UI's dropdown is always pre-populated with a sensible value. The Admin UI loses the 'Inherit from env' option since there is no longer an env layer to inherit from. * chore(models): set hardcoded fallback to gpt-5.5 medium Decouple the team-default boot value (gpt-5.5 / medium) from each model's ProfileForm-suggested default_effort so we can change one without nudging the other. The Opus xhigh default for new user profiles is unchanged. * feat(dashboard): trigger-mode copy, Coming Soon badges, logout in My Settings - Rename trigger mode 'ready_for_review' -> 'once_per_pr' with new description copy that matches the screenshot. Legacy stored values fall back to 'every_push' on read so the UI never shows an unknown selection. - Add a 'Coming soon' badge + greyed-out + disabled state on the controls that don't have runtime consumers yet: Trigger Mode, Autofix Mode, Autofix Severity Threshold, and Automatically fix CI failures. SettingsRow grew a comingSoon prop to keep this consistent. - My Settings drops the noop PR Preferences section and adds a Sign Out button. preferred_pr_destination is removed from the profile schema; old records get the field popped on next write. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-21 09:17:07 -07:00
import { Navigate, createFileRoute, useNavigate } from "@tanstack/react-router";
feat: auto-review PRs on opened / ready-for-review (#1325) * feat: auto-review PRs on opened / ready-for-review Trigger Open SWE Review on `pull_request` actions `opened` and `ready_for_review` against the canonical reviewer thread (no need to request open-swe[bot] as a reviewer). `converted_to_draft` now also flips watch=False on the existing reviewer thread. Draft PRs are gated by a tri-state user setting on the profile: inherit team default, always on, or always off. The team-wide `review_draft_prs` setting is the org-wide default; each user can override it in My Settings. External contributors with no Open SWE profile fall back to the team default. * fix: PR review comments — auth source + draft-aware watch toggle - `process_github_pr_ready` now dispatches with `source="github"` so the auth resolver finds the bot token persisted on the thread. The previous `source="github_auto"` fell through to the email-based path in non bot-token-only deployments and failed with a missing-user-email error. - `converted_to_draft` no longer unconditionally clears `watch`. When the PR author's effective `review_draft_prs` setting is on, watch stays on so subsequent pushes still trigger re-reviews while the PR is in draft. * feat(reviewer): skip "no issues found" comment on empty re-reviews A re-review run with no new findings to surface no longer posts another "Open SWE Review: No issues found" comment on the PR. The "no issues" summary now only appears on the first review of a PR — matching Devin's behavior, where subsequent reviews are silent unless there's something new to flag. Resolved-thread reconciliation and ``last_reviewed_sha`` persistence still happen on the skipped path, so findings the user just fixed still get their GitHub threads marked resolved, and the next push event sees an up-to-date dedup SHA. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-22 13:36:14 -07:00
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useState } from "react";
feat: refactor UI into sidebar layout (#1318) * feat(dashboard): refactor UI into Cursor-style sidebar layout Replace the top-bar AppHeader with a left sidebar that holds the four primary sections (My Settings, Cloud Agents, Open SWE Review, Integrations) and the user account menu at the bottom. Admin remains a hidden route reachable from the sidebar only when is_admin. UI: - AppShell + AppSidebar with bottom-anchored avatar/sign-out menu. - My Settings: profile (first/last name) + PR destination preference. - Cloud Agents: model/effort, default repo, base branch, branch prefix, PR toggles (auto-fix CI, create PRs, allow artifacts), Slack notifications. - Open SWE Review: trigger mode, draft reviews, PR summaries, autofix mode + severity threshold, plus the existing per-repo review-style prompts (was /review-styles). - Integrations: GitHub install status + Slack/Linear status rows. - New Switch and Menu primitives. Backend: - Extend ProfileUpdate with first_name, last_name, base_branch, branch_prefix, auto_fix_ci, create_prs, allow_artifacts, slack_notifications, preferred_pr_destination. - Add team_settings module + /team-settings GET (any session) / PUT (admin only) for the reviewer configuration. * fix(dashboard): replace base-ui Menu with plain dropdown, stabilise Selects - base-ui's Menu hit an "Invalid hook call / Cannot read properties of null (reading 'useRef')" crash inside fastComponent under Vite's dep optimisation. The sidebar account menu is the only consumer, so swap it for a useState-driven dropdown with click-outside + Esc handling and drop the unused Menu wrapper. - Initialise the Open SWE Review settings form with the same defaults the server returns instead of `null`, so the Selects don't switch from uncontrolled (`undefined`) to controlled on first data load. * fix(dashboard): address review feedback on admin overwrite + form reset - admin PUT /admin/profiles/{login} only sent model/effort/repo from the admin form. ProfileUpdate's Pydantic defaults then wrote auto_fix_ci / create_prs / slack_notifications etc back onto the target user's profile, clobbering whatever they had configured. Switch to model_dump(exclude_unset=True) and overlay the incoming fields on the user's existing stored profile so only sent fields change. - my-settings.tsx and cloud-agents.tsx initialised local state from profile.data on every change. Because useSaveProfile updates the cached profile on success, toggling any control would overwrite the user's typed-but-unsaved input in another field. Guard the initialisation effect with a ref so it runs once on first load. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-20 14:13:23 -07:00
import { AppShell, SettingsRow, SettingsSection } from "@/components/AppShell";
import { Button } from "@/components/ui/button";
feat: auto-review PRs on opened / ready-for-review (#1325) * feat: auto-review PRs on opened / ready-for-review Trigger Open SWE Review on `pull_request` actions `opened` and `ready_for_review` against the canonical reviewer thread (no need to request open-swe[bot] as a reviewer). `converted_to_draft` now also flips watch=False on the existing reviewer thread. Draft PRs are gated by a tri-state user setting on the profile: inherit team default, always on, or always off. The team-wide `review_draft_prs` setting is the org-wide default; each user can override it in My Settings. External contributors with no Open SWE profile fall back to the team default. * fix: PR review comments — auth source + draft-aware watch toggle - `process_github_pr_ready` now dispatches with `source="github"` so the auth resolver finds the bot token persisted on the thread. The previous `source="github_auto"` fell through to the email-based path in non bot-token-only deployments and failed with a missing-user-email error. - `converted_to_draft` no longer unconditionally clears `watch`. When the PR author's effective `review_draft_prs` setting is on, watch stays on so subsequent pushes still trigger re-reviews while the PR is in draft. * feat(reviewer): skip "no issues found" comment on empty re-reviews A re-review run with no new findings to surface no longer posts another "Open SWE Review: No issues found" comment on the PR. The "no issues" summary now only appears on the first review of a PR — matching Devin's behavior, where subsequent reviews are silent unless there's something new to flag. Resolved-thread reconciliation and ``last_reviewed_sha`` persistence still happen on the skipped path, so findings the user just fixed still get their GitHub threads marked resolved, and the next push event sees an up-to-date dedup SHA. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-22 13:36:14 -07:00
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
feat: refactor UI into sidebar layout (#1318) * feat(dashboard): refactor UI into Cursor-style sidebar layout Replace the top-bar AppHeader with a left sidebar that holds the four primary sections (My Settings, Cloud Agents, Open SWE Review, Integrations) and the user account menu at the bottom. Admin remains a hidden route reachable from the sidebar only when is_admin. UI: - AppShell + AppSidebar with bottom-anchored avatar/sign-out menu. - My Settings: profile (first/last name) + PR destination preference. - Cloud Agents: model/effort, default repo, base branch, branch prefix, PR toggles (auto-fix CI, create PRs, allow artifacts), Slack notifications. - Open SWE Review: trigger mode, draft reviews, PR summaries, autofix mode + severity threshold, plus the existing per-repo review-style prompts (was /review-styles). - Integrations: GitHub install status + Slack/Linear status rows. - New Switch and Menu primitives. Backend: - Extend ProfileUpdate with first_name, last_name, base_branch, branch_prefix, auto_fix_ci, create_prs, allow_artifacts, slack_notifications, preferred_pr_destination. - Add team_settings module + /team-settings GET (any session) / PUT (admin only) for the reviewer configuration. * fix(dashboard): replace base-ui Menu with plain dropdown, stabilise Selects - base-ui's Menu hit an "Invalid hook call / Cannot read properties of null (reading 'useRef')" crash inside fastComponent under Vite's dep optimisation. The sidebar account menu is the only consumer, so swap it for a useState-driven dropdown with click-outside + Esc handling and drop the unused Menu wrapper. - Initialise the Open SWE Review settings form with the same defaults the server returns instead of `null`, so the Selects don't switch from uncontrolled (`undefined`) to controlled on first data load. * fix(dashboard): address review feedback on admin overwrite + form reset - admin PUT /admin/profiles/{login} only sent model/effort/repo from the admin form. ProfileUpdate's Pydantic defaults then wrote auto_fix_ci / create_prs / slack_notifications etc back onto the target user's profile, clobbering whatever they had configured. Switch to model_dump(exclude_unset=True) and overlay the incoming fields on the user's existing stored profile so only sent fields change. - my-settings.tsx and cloud-agents.tsx initialised local state from profile.data on every change. Because useSaveProfile updates the cached profile on success, toggling any control would overwrite the user's typed-but-unsaved input in another field. Guard the initialisation effect with a ref so it runs once on first load. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-20 14:13:23 -07:00
import { Skeleton } from "@/components/ui/skeleton";
feat: restructure Open SWE Review tab + wire create_prs (#1319) * feat(dashboard): restructure Open SWE Review tab + wire create_prs Restructures the dashboard around two related changes the reviewer settings have been asking for: - Wire profile.create_prs. Defaults to true (opt-out); when off the system prompt gets a `Pull Request Policy Override` section telling the agent to push the branch and notify with the branch URL instead of opening a PR. Removes the noop Slack Notifications / Allow Artifacts / First Name / Last Name controls and their schema fields. - Repositories opt-in for Open SWE Review. New per-team enabled list stored in the LangGraph Store (`["enabled_review_repos"]`). Every reviewer webhook chokepoint now goes through `_is_repo_enabled_for_review` which AND-combines the existing env allowlist with the dashboard list. Default is empty (opt-in) — admins enable repos per-installation from the new Repositories page nested under Open SWE Review. - Open SWE Review tab now mirrors the Cursor "rules" pattern: main page shows installation rows + a Rules entry; both drill into nested pages (/review/repositories/$owner and /review/styles) with a back link. - Adds the new logo/favicon assets shipped from sidebar + html head. Tests pass with a new autouse fixture (`tests/conftest.py`) that defaults `is_review_repo_enabled` to True for existing allowlist tests. * fix(dashboard): make main content scroll independently of the sidebar Outer flex container was min-h-svh, so it grew with main's content and the whole page scrolled — sidebar moved with it. Pin to h-svh + overflow-hidden so the sidebar stays put and only <main> scrolls. * fix(dashboard): make disabled repo toggles obviously disabled Switch's disabled state used opacity-50 against a muted background, so the not-admin state looked nearly identical to the off state. Bump to opacity-40 + grayscale, and wrap each repo toggle in a span carrying a native hover tooltip explaining why it's disabled. * fix(switch): handle base-ui's data-disabled state base-ui's Switch.Root sets data-disabled (not the HTML disabled attribute) when disabled, so Tailwind's disabled: variant never matches and the button keeps its cursor-pointer + clickable look. Mirror the styling under the data-[disabled] variant and add pointer-events-none so the disabled state is both visible and actually unclickable. * feat(dashboard): paginate per-installation repository list 20 repos per page with Prev / page X of Y / Next controls at the bottom. Pager only renders when there are more than 20 repos. Page resets to 0 when navigating between installations. * feat(dashboard): global default model selectors for Agent + Reviewer Adds team-wide default model + reasoning effort for both agents in the Admin tab so operators can switch models without redeploying. Resolution chain: Agent: hardcoded -> LLM_MODEL_ID env -> team default -> user profile Reviewer: hardcoded -> LLM_MODEL_ID env -> team default -> per-call configurable Team defaults live in team_settings and are validated against the SUPPORTED_MODELS allowlist + the model's supported reasoning efforts. 'Inherit from env' clears the override and falls back to LLM_MODEL_ID. * refactor(models): drop LLM_MODEL_ID env in favour of the team default The team default is now the single source of truth for the runtime model choice; per-user (agent) and per-call configurable (reviewer) selections still win on top. When no admin has touched the team default, it surfaces the hardcoded fallback (DEFAULT_MODEL_ID + its default effort), so the admin UI's dropdown is always pre-populated with a sensible value. The Admin UI loses the 'Inherit from env' option since there is no longer an env layer to inherit from. * chore(models): set hardcoded fallback to gpt-5.5 medium Decouple the team-default boot value (gpt-5.5 / medium) from each model's ProfileForm-suggested default_effort so we can change one without nudging the other. The Opus xhigh default for new user profiles is unchanged. * feat(dashboard): trigger-mode copy, Coming Soon badges, logout in My Settings - Rename trigger mode 'ready_for_review' -> 'once_per_pr' with new description copy that matches the screenshot. Legacy stored values fall back to 'every_push' on read so the UI never shows an unknown selection. - Add a 'Coming soon' badge + greyed-out + disabled state on the controls that don't have runtime consumers yet: Trigger Mode, Autofix Mode, Autofix Severity Threshold, and Automatically fix CI failures. SettingsRow grew a comingSoon prop to keep this consistent. - My Settings drops the noop PR Preferences section and adds a Sign Out button. preferred_pr_destination is removed from the profile schema; old records get the field popped on next write. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-21 09:17:07 -07:00
import { api } from "@/lib/api";
feat: auto-review PRs on opened / ready-for-review (#1325) * feat: auto-review PRs on opened / ready-for-review Trigger Open SWE Review on `pull_request` actions `opened` and `ready_for_review` against the canonical reviewer thread (no need to request open-swe[bot] as a reviewer). `converted_to_draft` now also flips watch=False on the existing reviewer thread. Draft PRs are gated by a tri-state user setting on the profile: inherit team default, always on, or always off. The team-wide `review_draft_prs` setting is the org-wide default; each user can override it in My Settings. External contributors with no Open SWE profile fall back to the team default. * fix: PR review comments — auth source + draft-aware watch toggle - `process_github_pr_ready` now dispatches with `source="github"` so the auth resolver finds the bot token persisted on the thread. The previous `source="github_auto"` fell through to the email-based path in non bot-token-only deployments and failed with a missing-user-email error. - `converted_to_draft` no longer unconditionally clears `watch`. When the PR author's effective `review_draft_prs` setting is on, watch stays on so subsequent pushes still trigger re-reviews while the PR is in draft. * feat(reviewer): skip "no issues found" comment on empty re-reviews A re-review run with no new findings to surface no longer posts another "Open SWE Review: No issues found" comment on the PR. The "no issues" summary now only appears on the first review of a PR — matching Devin's behavior, where subsequent reviews are silent unless there's something new to flag. Resolved-thread reconciliation and ``last_reviewed_sha`` persistence still happen on the skipped path, so findings the user just fixed still get their GitHub threads marked resolved, and the next push event sees an up-to-date dedup SHA. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-22 13:36:14 -07:00
import { buildProfileUpdate, useOptions, useProfile, useSaveProfile } from "@/lib/profile";
feat: refactor UI into sidebar layout (#1318) * feat(dashboard): refactor UI into Cursor-style sidebar layout Replace the top-bar AppHeader with a left sidebar that holds the four primary sections (My Settings, Cloud Agents, Open SWE Review, Integrations) and the user account menu at the bottom. Admin remains a hidden route reachable from the sidebar only when is_admin. UI: - AppShell + AppSidebar with bottom-anchored avatar/sign-out menu. - My Settings: profile (first/last name) + PR destination preference. - Cloud Agents: model/effort, default repo, base branch, branch prefix, PR toggles (auto-fix CI, create PRs, allow artifacts), Slack notifications. - Open SWE Review: trigger mode, draft reviews, PR summaries, autofix mode + severity threshold, plus the existing per-repo review-style prompts (was /review-styles). - Integrations: GitHub install status + Slack/Linear status rows. - New Switch and Menu primitives. Backend: - Extend ProfileUpdate with first_name, last_name, base_branch, branch_prefix, auto_fix_ci, create_prs, allow_artifacts, slack_notifications, preferred_pr_destination. - Add team_settings module + /team-settings GET (any session) / PUT (admin only) for the reviewer configuration. * fix(dashboard): replace base-ui Menu with plain dropdown, stabilise Selects - base-ui's Menu hit an "Invalid hook call / Cannot read properties of null (reading 'useRef')" crash inside fastComponent under Vite's dep optimisation. The sidebar account menu is the only consumer, so swap it for a useState-driven dropdown with click-outside + Esc handling and drop the unused Menu wrapper. - Initialise the Open SWE Review settings form with the same defaults the server returns instead of `null`, so the Selects don't switch from uncontrolled (`undefined`) to controlled on first data load. * fix(dashboard): address review feedback on admin overwrite + form reset - admin PUT /admin/profiles/{login} only sent model/effort/repo from the admin form. ProfileUpdate's Pydantic defaults then wrote auto_fix_ci / create_prs / slack_notifications etc back onto the target user's profile, clobbering whatever they had configured. Switch to model_dump(exclude_unset=True) and overlay the incoming fields on the user's existing stored profile so only sent fields change. - my-settings.tsx and cloud-agents.tsx initialised local state from profile.data on every change. Because useSaveProfile updates the cached profile on success, toggling any control would overwrite the user's typed-but-unsaved input in another field. Guard the initialisation effect with a ref so it runs once on first load. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-20 14:13:23 -07:00
import { useSession } from "@/lib/session";
export const Route = createFileRoute("/my-settings")({ component: MySettingsPage });
feat: auto-review PRs on opened / ready-for-review (#1325) * feat: auto-review PRs on opened / ready-for-review Trigger Open SWE Review on `pull_request` actions `opened` and `ready_for_review` against the canonical reviewer thread (no need to request open-swe[bot] as a reviewer). `converted_to_draft` now also flips watch=False on the existing reviewer thread. Draft PRs are gated by a tri-state user setting on the profile: inherit team default, always on, or always off. The team-wide `review_draft_prs` setting is the org-wide default; each user can override it in My Settings. External contributors with no Open SWE profile fall back to the team default. * fix: PR review comments — auth source + draft-aware watch toggle - `process_github_pr_ready` now dispatches with `source="github"` so the auth resolver finds the bot token persisted on the thread. The previous `source="github_auto"` fell through to the email-based path in non bot-token-only deployments and failed with a missing-user-email error. - `converted_to_draft` no longer unconditionally clears `watch`. When the PR author's effective `review_draft_prs` setting is on, watch stays on so subsequent pushes still trigger re-reviews while the PR is in draft. * feat(reviewer): skip "no issues found" comment on empty re-reviews A re-review run with no new findings to surface no longer posts another "Open SWE Review: No issues found" comment on the PR. The "no issues" summary now only appears on the first review of a PR — matching Devin's behavior, where subsequent reviews are silent unless there's something new to flag. Resolved-thread reconciliation and ``last_reviewed_sha`` persistence still happen on the skipped path, so findings the user just fixed still get their GitHub threads marked resolved, and the next push event sees an up-to-date dedup SHA. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-22 13:36:14 -07:00
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;
}
feat: refactor UI into sidebar layout (#1318) * feat(dashboard): refactor UI into Cursor-style sidebar layout Replace the top-bar AppHeader with a left sidebar that holds the four primary sections (My Settings, Cloud Agents, Open SWE Review, Integrations) and the user account menu at the bottom. Admin remains a hidden route reachable from the sidebar only when is_admin. UI: - AppShell + AppSidebar with bottom-anchored avatar/sign-out menu. - My Settings: profile (first/last name) + PR destination preference. - Cloud Agents: model/effort, default repo, base branch, branch prefix, PR toggles (auto-fix CI, create PRs, allow artifacts), Slack notifications. - Open SWE Review: trigger mode, draft reviews, PR summaries, autofix mode + severity threshold, plus the existing per-repo review-style prompts (was /review-styles). - Integrations: GitHub install status + Slack/Linear status rows. - New Switch and Menu primitives. Backend: - Extend ProfileUpdate with first_name, last_name, base_branch, branch_prefix, auto_fix_ci, create_prs, allow_artifacts, slack_notifications, preferred_pr_destination. - Add team_settings module + /team-settings GET (any session) / PUT (admin only) for the reviewer configuration. * fix(dashboard): replace base-ui Menu with plain dropdown, stabilise Selects - base-ui's Menu hit an "Invalid hook call / Cannot read properties of null (reading 'useRef')" crash inside fastComponent under Vite's dep optimisation. The sidebar account menu is the only consumer, so swap it for a useState-driven dropdown with click-outside + Esc handling and drop the unused Menu wrapper. - Initialise the Open SWE Review settings form with the same defaults the server returns instead of `null`, so the Selects don't switch from uncontrolled (`undefined`) to controlled on first data load. * fix(dashboard): address review feedback on admin overwrite + form reset - admin PUT /admin/profiles/{login} only sent model/effort/repo from the admin form. ProfileUpdate's Pydantic defaults then wrote auto_fix_ci / create_prs / slack_notifications etc back onto the target user's profile, clobbering whatever they had configured. Switch to model_dump(exclude_unset=True) and overlay the incoming fields on the user's existing stored profile so only sent fields change. - my-settings.tsx and cloud-agents.tsx initialised local state from profile.data on every change. Because useSaveProfile updates the cached profile on success, toggling any control would overwrite the user's typed-but-unsaved input in another field. Guard the initialisation effect with a ref so it runs once on first load. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-20 14:13:23 -07:00
function MySettingsPage() {
const session = useSession();
feat: restructure Open SWE Review tab + wire create_prs (#1319) * feat(dashboard): restructure Open SWE Review tab + wire create_prs Restructures the dashboard around two related changes the reviewer settings have been asking for: - Wire profile.create_prs. Defaults to true (opt-out); when off the system prompt gets a `Pull Request Policy Override` section telling the agent to push the branch and notify with the branch URL instead of opening a PR. Removes the noop Slack Notifications / Allow Artifacts / First Name / Last Name controls and their schema fields. - Repositories opt-in for Open SWE Review. New per-team enabled list stored in the LangGraph Store (`["enabled_review_repos"]`). Every reviewer webhook chokepoint now goes through `_is_repo_enabled_for_review` which AND-combines the existing env allowlist with the dashboard list. Default is empty (opt-in) — admins enable repos per-installation from the new Repositories page nested under Open SWE Review. - Open SWE Review tab now mirrors the Cursor "rules" pattern: main page shows installation rows + a Rules entry; both drill into nested pages (/review/repositories/$owner and /review/styles) with a back link. - Adds the new logo/favicon assets shipped from sidebar + html head. Tests pass with a new autouse fixture (`tests/conftest.py`) that defaults `is_review_repo_enabled` to True for existing allowlist tests. * fix(dashboard): make main content scroll independently of the sidebar Outer flex container was min-h-svh, so it grew with main's content and the whole page scrolled — sidebar moved with it. Pin to h-svh + overflow-hidden so the sidebar stays put and only <main> scrolls. * fix(dashboard): make disabled repo toggles obviously disabled Switch's disabled state used opacity-50 against a muted background, so the not-admin state looked nearly identical to the off state. Bump to opacity-40 + grayscale, and wrap each repo toggle in a span carrying a native hover tooltip explaining why it's disabled. * fix(switch): handle base-ui's data-disabled state base-ui's Switch.Root sets data-disabled (not the HTML disabled attribute) when disabled, so Tailwind's disabled: variant never matches and the button keeps its cursor-pointer + clickable look. Mirror the styling under the data-[disabled] variant and add pointer-events-none so the disabled state is both visible and actually unclickable. * feat(dashboard): paginate per-installation repository list 20 repos per page with Prev / page X of Y / Next controls at the bottom. Pager only renders when there are more than 20 repos. Page resets to 0 when navigating between installations. * feat(dashboard): global default model selectors for Agent + Reviewer Adds team-wide default model + reasoning effort for both agents in the Admin tab so operators can switch models without redeploying. Resolution chain: Agent: hardcoded -> LLM_MODEL_ID env -> team default -> user profile Reviewer: hardcoded -> LLM_MODEL_ID env -> team default -> per-call configurable Team defaults live in team_settings and are validated against the SUPPORTED_MODELS allowlist + the model's supported reasoning efforts. 'Inherit from env' clears the override and falls back to LLM_MODEL_ID. * refactor(models): drop LLM_MODEL_ID env in favour of the team default The team default is now the single source of truth for the runtime model choice; per-user (agent) and per-call configurable (reviewer) selections still win on top. When no admin has touched the team default, it surfaces the hardcoded fallback (DEFAULT_MODEL_ID + its default effort), so the admin UI's dropdown is always pre-populated with a sensible value. The Admin UI loses the 'Inherit from env' option since there is no longer an env layer to inherit from. * chore(models): set hardcoded fallback to gpt-5.5 medium Decouple the team-default boot value (gpt-5.5 / medium) from each model's ProfileForm-suggested default_effort so we can change one without nudging the other. The Opus xhigh default for new user profiles is unchanged. * feat(dashboard): trigger-mode copy, Coming Soon badges, logout in My Settings - Rename trigger mode 'ready_for_review' -> 'once_per_pr' with new description copy that matches the screenshot. Legacy stored values fall back to 'every_push' on read so the UI never shows an unknown selection. - Add a 'Coming soon' badge + greyed-out + disabled state on the controls that don't have runtime consumers yet: Trigger Mode, Autofix Mode, Autofix Severity Threshold, and Automatically fix CI failures. SettingsRow grew a comingSoon prop to keep this consistent. - My Settings drops the noop PR Preferences section and adds a Sign Out button. preferred_pr_destination is removed from the profile schema; old records get the field popped on next write. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-21 09:17:07 -07:00
const qc = useQueryClient();
const navigate = useNavigate();
feat: auto-review PRs on opened / ready-for-review (#1325) * feat: auto-review PRs on opened / ready-for-review Trigger Open SWE Review on `pull_request` actions `opened` and `ready_for_review` against the canonical reviewer thread (no need to request open-swe[bot] as a reviewer). `converted_to_draft` now also flips watch=False on the existing reviewer thread. Draft PRs are gated by a tri-state user setting on the profile: inherit team default, always on, or always off. The team-wide `review_draft_prs` setting is the org-wide default; each user can override it in My Settings. External contributors with no Open SWE profile fall back to the team default. * fix: PR review comments — auth source + draft-aware watch toggle - `process_github_pr_ready` now dispatches with `source="github"` so the auth resolver finds the bot token persisted on the thread. The previous `source="github_auto"` fell through to the email-based path in non bot-token-only deployments and failed with a missing-user-email error. - `converted_to_draft` no longer unconditionally clears `watch`. When the PR author's effective `review_draft_prs` setting is on, watch stays on so subsequent pushes still trigger re-reviews while the PR is in draft. * feat(reviewer): skip "no issues found" comment on empty re-reviews A re-review run with no new findings to surface no longer posts another "Open SWE Review: No issues found" comment on the PR. The "no issues" summary now only appears on the first review of a PR — matching Devin's behavior, where subsequent reviews are silent unless there's something new to flag. Resolved-thread reconciliation and ``last_reviewed_sha`` persistence still happen on the skipped path, so findings the user just fixed still get their GitHub threads marked resolved, and the next push event sees an up-to-date dedup SHA. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-22 13:36:14 -07:00
const profile = useProfile();
const options = useOptions();
const save = useSaveProfile();
const teamSettings = useQuery({
queryKey: ["teamSettings"],
queryFn: api.getTeamSettings,
enabled: !!session.data,
});
const [error, setError] = useState<string | null>(null);
feat: refactor UI into sidebar layout (#1318) * feat(dashboard): refactor UI into Cursor-style sidebar layout Replace the top-bar AppHeader with a left sidebar that holds the four primary sections (My Settings, Cloud Agents, Open SWE Review, Integrations) and the user account menu at the bottom. Admin remains a hidden route reachable from the sidebar only when is_admin. UI: - AppShell + AppSidebar with bottom-anchored avatar/sign-out menu. - My Settings: profile (first/last name) + PR destination preference. - Cloud Agents: model/effort, default repo, base branch, branch prefix, PR toggles (auto-fix CI, create PRs, allow artifacts), Slack notifications. - Open SWE Review: trigger mode, draft reviews, PR summaries, autofix mode + severity threshold, plus the existing per-repo review-style prompts (was /review-styles). - Integrations: GitHub install status + Slack/Linear status rows. - New Switch and Menu primitives. Backend: - Extend ProfileUpdate with first_name, last_name, base_branch, branch_prefix, auto_fix_ci, create_prs, allow_artifacts, slack_notifications, preferred_pr_destination. - Add team_settings module + /team-settings GET (any session) / PUT (admin only) for the reviewer configuration. * fix(dashboard): replace base-ui Menu with plain dropdown, stabilise Selects - base-ui's Menu hit an "Invalid hook call / Cannot read properties of null (reading 'useRef')" crash inside fastComponent under Vite's dep optimisation. The sidebar account menu is the only consumer, so swap it for a useState-driven dropdown with click-outside + Esc handling and drop the unused Menu wrapper. - Initialise the Open SWE Review settings form with the same defaults the server returns instead of `null`, so the Selects don't switch from uncontrolled (`undefined`) to controlled on first data load. * fix(dashboard): address review feedback on admin overwrite + form reset - admin PUT /admin/profiles/{login} only sent model/effort/repo from the admin form. ProfileUpdate's Pydantic defaults then wrote auto_fix_ci / create_prs / slack_notifications etc back onto the target user's profile, clobbering whatever they had configured. Switch to model_dump(exclude_unset=True) and overlay the incoming fields on the user's existing stored profile so only sent fields change. - my-settings.tsx and cloud-agents.tsx initialised local state from profile.data on every change. Because useSaveProfile updates the cached profile on success, toggling any control would overwrite the user's typed-but-unsaved input in another field. Guard the initialisation effect with a ref so it runs once on first load. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-20 14:13:23 -07:00
if (session.isLoading) {
return (
<main className="p-6">
<Skeleton className="h-40 w-full" />
</main>
);
}
if (!session.data) return <Navigate to="/login" />;
feat: restructure Open SWE Review tab + wire create_prs (#1319) * feat(dashboard): restructure Open SWE Review tab + wire create_prs Restructures the dashboard around two related changes the reviewer settings have been asking for: - Wire profile.create_prs. Defaults to true (opt-out); when off the system prompt gets a `Pull Request Policy Override` section telling the agent to push the branch and notify with the branch URL instead of opening a PR. Removes the noop Slack Notifications / Allow Artifacts / First Name / Last Name controls and their schema fields. - Repositories opt-in for Open SWE Review. New per-team enabled list stored in the LangGraph Store (`["enabled_review_repos"]`). Every reviewer webhook chokepoint now goes through `_is_repo_enabled_for_review` which AND-combines the existing env allowlist with the dashboard list. Default is empty (opt-in) — admins enable repos per-installation from the new Repositories page nested under Open SWE Review. - Open SWE Review tab now mirrors the Cursor "rules" pattern: main page shows installation rows + a Rules entry; both drill into nested pages (/review/repositories/$owner and /review/styles) with a back link. - Adds the new logo/favicon assets shipped from sidebar + html head. Tests pass with a new autouse fixture (`tests/conftest.py`) that defaults `is_review_repo_enabled` to True for existing allowlist tests. * fix(dashboard): make main content scroll independently of the sidebar Outer flex container was min-h-svh, so it grew with main's content and the whole page scrolled — sidebar moved with it. Pin to h-svh + overflow-hidden so the sidebar stays put and only <main> scrolls. * fix(dashboard): make disabled repo toggles obviously disabled Switch's disabled state used opacity-50 against a muted background, so the not-admin state looked nearly identical to the off state. Bump to opacity-40 + grayscale, and wrap each repo toggle in a span carrying a native hover tooltip explaining why it's disabled. * fix(switch): handle base-ui's data-disabled state base-ui's Switch.Root sets data-disabled (not the HTML disabled attribute) when disabled, so Tailwind's disabled: variant never matches and the button keeps its cursor-pointer + clickable look. Mirror the styling under the data-[disabled] variant and add pointer-events-none so the disabled state is both visible and actually unclickable. * feat(dashboard): paginate per-installation repository list 20 repos per page with Prev / page X of Y / Next controls at the bottom. Pager only renders when there are more than 20 repos. Page resets to 0 when navigating between installations. * feat(dashboard): global default model selectors for Agent + Reviewer Adds team-wide default model + reasoning effort for both agents in the Admin tab so operators can switch models without redeploying. Resolution chain: Agent: hardcoded -> LLM_MODEL_ID env -> team default -> user profile Reviewer: hardcoded -> LLM_MODEL_ID env -> team default -> per-call configurable Team defaults live in team_settings and are validated against the SUPPORTED_MODELS allowlist + the model's supported reasoning efforts. 'Inherit from env' clears the override and falls back to LLM_MODEL_ID. * refactor(models): drop LLM_MODEL_ID env in favour of the team default The team default is now the single source of truth for the runtime model choice; per-user (agent) and per-call configurable (reviewer) selections still win on top. When no admin has touched the team default, it surfaces the hardcoded fallback (DEFAULT_MODEL_ID + its default effort), so the admin UI's dropdown is always pre-populated with a sensible value. The Admin UI loses the 'Inherit from env' option since there is no longer an env layer to inherit from. * chore(models): set hardcoded fallback to gpt-5.5 medium Decouple the team-default boot value (gpt-5.5 / medium) from each model's ProfileForm-suggested default_effort so we can change one without nudging the other. The Opus xhigh default for new user profiles is unchanged. * feat(dashboard): trigger-mode copy, Coming Soon badges, logout in My Settings - Rename trigger mode 'ready_for_review' -> 'once_per_pr' with new description copy that matches the screenshot. Legacy stored values fall back to 'every_push' on read so the UI never shows an unknown selection. - Add a 'Coming soon' badge + greyed-out + disabled state on the controls that don't have runtime consumers yet: Trigger Mode, Autofix Mode, Autofix Severity Threshold, and Automatically fix CI failures. SettingsRow grew a comingSoon prop to keep this consistent. - My Settings drops the noop PR Preferences section and adds a Sign Out button. preferred_pr_destination is removed from the profile schema; old records get the field popped on next write. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-21 09:17:07 -07:00
const handleLogout = async () => {
await api.logout();
qc.setQueryData(["session"], null);
void navigate({ to: "/login" });
feat: refactor UI into sidebar layout (#1318) * feat(dashboard): refactor UI into Cursor-style sidebar layout Replace the top-bar AppHeader with a left sidebar that holds the four primary sections (My Settings, Cloud Agents, Open SWE Review, Integrations) and the user account menu at the bottom. Admin remains a hidden route reachable from the sidebar only when is_admin. UI: - AppShell + AppSidebar with bottom-anchored avatar/sign-out menu. - My Settings: profile (first/last name) + PR destination preference. - Cloud Agents: model/effort, default repo, base branch, branch prefix, PR toggles (auto-fix CI, create PRs, allow artifacts), Slack notifications. - Open SWE Review: trigger mode, draft reviews, PR summaries, autofix mode + severity threshold, plus the existing per-repo review-style prompts (was /review-styles). - Integrations: GitHub install status + Slack/Linear status rows. - New Switch and Menu primitives. Backend: - Extend ProfileUpdate with first_name, last_name, base_branch, branch_prefix, auto_fix_ci, create_prs, allow_artifacts, slack_notifications, preferred_pr_destination. - Add team_settings module + /team-settings GET (any session) / PUT (admin only) for the reviewer configuration. * fix(dashboard): replace base-ui Menu with plain dropdown, stabilise Selects - base-ui's Menu hit an "Invalid hook call / Cannot read properties of null (reading 'useRef')" crash inside fastComponent under Vite's dep optimisation. The sidebar account menu is the only consumer, so swap it for a useState-driven dropdown with click-outside + Esc handling and drop the unused Menu wrapper. - Initialise the Open SWE Review settings form with the same defaults the server returns instead of `null`, so the Selects don't switch from uncontrolled (`undefined`) to controlled on first data load. * fix(dashboard): address review feedback on admin overwrite + form reset - admin PUT /admin/profiles/{login} only sent model/effort/repo from the admin form. ProfileUpdate's Pydantic defaults then wrote auto_fix_ci / create_prs / slack_notifications etc back onto the target user's profile, clobbering whatever they had configured. Switch to model_dump(exclude_unset=True) and overlay the incoming fields on the user's existing stored profile so only sent fields change. - my-settings.tsx and cloud-agents.tsx initialised local state from profile.data on every change. Because useSaveProfile updates the cached profile on success, toggling any control would overwrite the user's typed-but-unsaved input in another field. Guard the initialisation effect with a ref so it runs once on first load. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-20 14:13:23 -07:00
};
feat: auto-review PRs on opened / ready-for-review (#1325) * feat: auto-review PRs on opened / ready-for-review Trigger Open SWE Review on `pull_request` actions `opened` and `ready_for_review` against the canonical reviewer thread (no need to request open-swe[bot] as a reviewer). `converted_to_draft` now also flips watch=False on the existing reviewer thread. Draft PRs are gated by a tri-state user setting on the profile: inherit team default, always on, or always off. The team-wide `review_draft_prs` setting is the org-wide default; each user can override it in My Settings. External contributors with no Open SWE profile fall back to the team default. * fix: PR review comments — auth source + draft-aware watch toggle - `process_github_pr_ready` now dispatches with `source="github"` so the auth resolver finds the bot token persisted on the thread. The previous `source="github_auto"` fell through to the email-based path in non bot-token-only deployments and failed with a missing-user-email error. - `converted_to_draft` no longer unconditionally clears `watch`. When the PR author's effective `review_draft_prs` setting is on, watch stays on so subsequent pushes still trigger re-reviews while the PR is in draft. * feat(reviewer): skip "no issues found" comment on empty re-reviews A re-review run with no new findings to surface no longer posts another "Open SWE Review: No issues found" comment on the PR. The "no issues" summary now only appears on the first review of a PR — matching Devin's behavior, where subsequent reviews are silent unless there's something new to flag. Resolved-thread reconciliation and ``last_reviewed_sha`` persistence still happen on the skipped path, so findings the user just fixed still get their GitHub threads marked resolved, and the next push event sees an up-to-date dedup SHA. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-22 13:36:14 -07:00
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));
};
feat: refactor UI into sidebar layout (#1318) * feat(dashboard): refactor UI into Cursor-style sidebar layout Replace the top-bar AppHeader with a left sidebar that holds the four primary sections (My Settings, Cloud Agents, Open SWE Review, Integrations) and the user account menu at the bottom. Admin remains a hidden route reachable from the sidebar only when is_admin. UI: - AppShell + AppSidebar with bottom-anchored avatar/sign-out menu. - My Settings: profile (first/last name) + PR destination preference. - Cloud Agents: model/effort, default repo, base branch, branch prefix, PR toggles (auto-fix CI, create PRs, allow artifacts), Slack notifications. - Open SWE Review: trigger mode, draft reviews, PR summaries, autofix mode + severity threshold, plus the existing per-repo review-style prompts (was /review-styles). - Integrations: GitHub install status + Slack/Linear status rows. - New Switch and Menu primitives. Backend: - Extend ProfileUpdate with first_name, last_name, base_branch, branch_prefix, auto_fix_ci, create_prs, allow_artifacts, slack_notifications, preferred_pr_destination. - Add team_settings module + /team-settings GET (any session) / PUT (admin only) for the reviewer configuration. * fix(dashboard): replace base-ui Menu with plain dropdown, stabilise Selects - base-ui's Menu hit an "Invalid hook call / Cannot read properties of null (reading 'useRef')" crash inside fastComponent under Vite's dep optimisation. The sidebar account menu is the only consumer, so swap it for a useState-driven dropdown with click-outside + Esc handling and drop the unused Menu wrapper. - Initialise the Open SWE Review settings form with the same defaults the server returns instead of `null`, so the Selects don't switch from uncontrolled (`undefined`) to controlled on first data load. * fix(dashboard): address review feedback on admin overwrite + form reset - admin PUT /admin/profiles/{login} only sent model/effort/repo from the admin form. ProfileUpdate's Pydantic defaults then wrote auto_fix_ci / create_prs / slack_notifications etc back onto the target user's profile, clobbering whatever they had configured. Switch to model_dump(exclude_unset=True) and overlay the incoming fields on the user's existing stored profile so only sent fields change. - my-settings.tsx and cloud-agents.tsx initialised local state from profile.data on every change. Because useSaveProfile updates the cached profile on success, toggling any control would overwrite the user's typed-but-unsaved input in another field. Guard the initialisation effect with a ref so it runs once on first load. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-20 14:13:23 -07:00
return (
<AppShell user={session.data} title="My Settings">
<SettingsSection title="Profile">
feat: restructure Open SWE Review tab + wire create_prs (#1319) * feat(dashboard): restructure Open SWE Review tab + wire create_prs Restructures the dashboard around two related changes the reviewer settings have been asking for: - Wire profile.create_prs. Defaults to true (opt-out); when off the system prompt gets a `Pull Request Policy Override` section telling the agent to push the branch and notify with the branch URL instead of opening a PR. Removes the noop Slack Notifications / Allow Artifacts / First Name / Last Name controls and their schema fields. - Repositories opt-in for Open SWE Review. New per-team enabled list stored in the LangGraph Store (`["enabled_review_repos"]`). Every reviewer webhook chokepoint now goes through `_is_repo_enabled_for_review` which AND-combines the existing env allowlist with the dashboard list. Default is empty (opt-in) — admins enable repos per-installation from the new Repositories page nested under Open SWE Review. - Open SWE Review tab now mirrors the Cursor "rules" pattern: main page shows installation rows + a Rules entry; both drill into nested pages (/review/repositories/$owner and /review/styles) with a back link. - Adds the new logo/favicon assets shipped from sidebar + html head. Tests pass with a new autouse fixture (`tests/conftest.py`) that defaults `is_review_repo_enabled` to True for existing allowlist tests. * fix(dashboard): make main content scroll independently of the sidebar Outer flex container was min-h-svh, so it grew with main's content and the whole page scrolled — sidebar moved with it. Pin to h-svh + overflow-hidden so the sidebar stays put and only <main> scrolls. * fix(dashboard): make disabled repo toggles obviously disabled Switch's disabled state used opacity-50 against a muted background, so the not-admin state looked nearly identical to the off state. Bump to opacity-40 + grayscale, and wrap each repo toggle in a span carrying a native hover tooltip explaining why it's disabled. * fix(switch): handle base-ui's data-disabled state base-ui's Switch.Root sets data-disabled (not the HTML disabled attribute) when disabled, so Tailwind's disabled: variant never matches and the button keeps its cursor-pointer + clickable look. Mirror the styling under the data-[disabled] variant and add pointer-events-none so the disabled state is both visible and actually unclickable. * feat(dashboard): paginate per-installation repository list 20 repos per page with Prev / page X of Y / Next controls at the bottom. Pager only renders when there are more than 20 repos. Page resets to 0 when navigating between installations. * feat(dashboard): global default model selectors for Agent + Reviewer Adds team-wide default model + reasoning effort for both agents in the Admin tab so operators can switch models without redeploying. Resolution chain: Agent: hardcoded -> LLM_MODEL_ID env -> team default -> user profile Reviewer: hardcoded -> LLM_MODEL_ID env -> team default -> per-call configurable Team defaults live in team_settings and are validated against the SUPPORTED_MODELS allowlist + the model's supported reasoning efforts. 'Inherit from env' clears the override and falls back to LLM_MODEL_ID. * refactor(models): drop LLM_MODEL_ID env in favour of the team default The team default is now the single source of truth for the runtime model choice; per-user (agent) and per-call configurable (reviewer) selections still win on top. When no admin has touched the team default, it surfaces the hardcoded fallback (DEFAULT_MODEL_ID + its default effort), so the admin UI's dropdown is always pre-populated with a sensible value. The Admin UI loses the 'Inherit from env' option since there is no longer an env layer to inherit from. * chore(models): set hardcoded fallback to gpt-5.5 medium Decouple the team-default boot value (gpt-5.5 / medium) from each model's ProfileForm-suggested default_effort so we can change one without nudging the other. The Opus xhigh default for new user profiles is unchanged. * feat(dashboard): trigger-mode copy, Coming Soon badges, logout in My Settings - Rename trigger mode 'ready_for_review' -> 'once_per_pr' with new description copy that matches the screenshot. Legacy stored values fall back to 'every_push' on read so the UI never shows an unknown selection. - Add a 'Coming soon' badge + greyed-out + disabled state on the controls that don't have runtime consumers yet: Trigger Mode, Autofix Mode, Autofix Severity Threshold, and Automatically fix CI failures. SettingsRow grew a comingSoon prop to keep this consistent. - My Settings drops the noop PR Preferences section and adds a Sign Out button. preferred_pr_destination is removed from the profile schema; old records get the field popped on next write. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-21 09:17:07 -07:00
<SettingsRow
label="Email"
control={
<span className="text-xs text-muted-foreground">
{session.data.email ?? "—"}
</span>
}
/>
feat: refactor UI into sidebar layout (#1318) * feat(dashboard): refactor UI into Cursor-style sidebar layout Replace the top-bar AppHeader with a left sidebar that holds the four primary sections (My Settings, Cloud Agents, Open SWE Review, Integrations) and the user account menu at the bottom. Admin remains a hidden route reachable from the sidebar only when is_admin. UI: - AppShell + AppSidebar with bottom-anchored avatar/sign-out menu. - My Settings: profile (first/last name) + PR destination preference. - Cloud Agents: model/effort, default repo, base branch, branch prefix, PR toggles (auto-fix CI, create PRs, allow artifacts), Slack notifications. - Open SWE Review: trigger mode, draft reviews, PR summaries, autofix mode + severity threshold, plus the existing per-repo review-style prompts (was /review-styles). - Integrations: GitHub install status + Slack/Linear status rows. - New Switch and Menu primitives. Backend: - Extend ProfileUpdate with first_name, last_name, base_branch, branch_prefix, auto_fix_ci, create_prs, allow_artifacts, slack_notifications, preferred_pr_destination. - Add team_settings module + /team-settings GET (any session) / PUT (admin only) for the reviewer configuration. * fix(dashboard): replace base-ui Menu with plain dropdown, stabilise Selects - base-ui's Menu hit an "Invalid hook call / Cannot read properties of null (reading 'useRef')" crash inside fastComponent under Vite's dep optimisation. The sidebar account menu is the only consumer, so swap it for a useState-driven dropdown with click-outside + Esc handling and drop the unused Menu wrapper. - Initialise the Open SWE Review settings form with the same defaults the server returns instead of `null`, so the Selects don't switch from uncontrolled (`undefined`) to controlled on first data load. * fix(dashboard): address review feedback on admin overwrite + form reset - admin PUT /admin/profiles/{login} only sent model/effort/repo from the admin form. ProfileUpdate's Pydantic defaults then wrote auto_fix_ci / create_prs / slack_notifications etc back onto the target user's profile, clobbering whatever they had configured. Switch to model_dump(exclude_unset=True) and overlay the incoming fields on the user's existing stored profile so only sent fields change. - my-settings.tsx and cloud-agents.tsx initialised local state from profile.data on every change. Because useSaveProfile updates the cached profile on success, toggling any control would overwrite the user's typed-but-unsaved input in another field. Guard the initialisation effect with a ref so it runs once on first load. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-20 14:13:23 -07:00
</SettingsSection>
feat: auto-review PRs on opened / ready-for-review (#1325) * feat: auto-review PRs on opened / ready-for-review Trigger Open SWE Review on `pull_request` actions `opened` and `ready_for_review` against the canonical reviewer thread (no need to request open-swe[bot] as a reviewer). `converted_to_draft` now also flips watch=False on the existing reviewer thread. Draft PRs are gated by a tri-state user setting on the profile: inherit team default, always on, or always off. The team-wide `review_draft_prs` setting is the org-wide default; each user can override it in My Settings. External contributors with no Open SWE profile fall back to the team default. * fix: PR review comments — auth source + draft-aware watch toggle - `process_github_pr_ready` now dispatches with `source="github"` so the auth resolver finds the bot token persisted on the thread. The previous `source="github_auto"` fell through to the email-based path in non bot-token-only deployments and failed with a missing-user-email error. - `converted_to_draft` no longer unconditionally clears `watch`. When the PR author's effective `review_draft_prs` setting is on, watch stays on so subsequent pushes still trigger re-reviews while the PR is in draft. * feat(reviewer): skip "no issues found" comment on empty re-reviews A re-review run with no new findings to surface no longer posts another "Open SWE Review: No issues found" comment on the PR. The "no issues" summary now only appears on the first review of a PR — matching Devin's behavior, where subsequent reviews are silent unless there's something new to flag. Resolved-thread reconciliation and ``last_reviewed_sha`` persistence still happen on the skipped path, so findings the user just fixed still get their GitHub threads marked resolved, and the next push event sees an up-to-date dedup SHA. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-22 13:36:14 -07:00
<SettingsSection title="Open SWE Review">
<SettingsRow
label="Review my draft PRs"
description="Whether Open SWE Review runs on pull requests you open in draft. When set to the team default, your admin's org-wide setting applies."
control={
<Select
value={draftChoice}
onValueChange={(v) => handleDraftChoiceChange(v as DraftReviewChoice)}
disabled={profile.isLoading || save.isPending}
>
<SelectTrigger className="w-56">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="team_default">{teamDefaultLabel}</SelectItem>
<SelectItem value="always_on">Always review my drafts</SelectItem>
<SelectItem value="always_off">Never review my drafts</SelectItem>
</SelectContent>
</Select>
}
/>
</SettingsSection>
feat: restructure Open SWE Review tab + wire create_prs (#1319) * feat(dashboard): restructure Open SWE Review tab + wire create_prs Restructures the dashboard around two related changes the reviewer settings have been asking for: - Wire profile.create_prs. Defaults to true (opt-out); when off the system prompt gets a `Pull Request Policy Override` section telling the agent to push the branch and notify with the branch URL instead of opening a PR. Removes the noop Slack Notifications / Allow Artifacts / First Name / Last Name controls and their schema fields. - Repositories opt-in for Open SWE Review. New per-team enabled list stored in the LangGraph Store (`["enabled_review_repos"]`). Every reviewer webhook chokepoint now goes through `_is_repo_enabled_for_review` which AND-combines the existing env allowlist with the dashboard list. Default is empty (opt-in) — admins enable repos per-installation from the new Repositories page nested under Open SWE Review. - Open SWE Review tab now mirrors the Cursor "rules" pattern: main page shows installation rows + a Rules entry; both drill into nested pages (/review/repositories/$owner and /review/styles) with a back link. - Adds the new logo/favicon assets shipped from sidebar + html head. Tests pass with a new autouse fixture (`tests/conftest.py`) that defaults `is_review_repo_enabled` to True for existing allowlist tests. * fix(dashboard): make main content scroll independently of the sidebar Outer flex container was min-h-svh, so it grew with main's content and the whole page scrolled — sidebar moved with it. Pin to h-svh + overflow-hidden so the sidebar stays put and only <main> scrolls. * fix(dashboard): make disabled repo toggles obviously disabled Switch's disabled state used opacity-50 against a muted background, so the not-admin state looked nearly identical to the off state. Bump to opacity-40 + grayscale, and wrap each repo toggle in a span carrying a native hover tooltip explaining why it's disabled. * fix(switch): handle base-ui's data-disabled state base-ui's Switch.Root sets data-disabled (not the HTML disabled attribute) when disabled, so Tailwind's disabled: variant never matches and the button keeps its cursor-pointer + clickable look. Mirror the styling under the data-[disabled] variant and add pointer-events-none so the disabled state is both visible and actually unclickable. * feat(dashboard): paginate per-installation repository list 20 repos per page with Prev / page X of Y / Next controls at the bottom. Pager only renders when there are more than 20 repos. Page resets to 0 when navigating between installations. * feat(dashboard): global default model selectors for Agent + Reviewer Adds team-wide default model + reasoning effort for both agents in the Admin tab so operators can switch models without redeploying. Resolution chain: Agent: hardcoded -> LLM_MODEL_ID env -> team default -> user profile Reviewer: hardcoded -> LLM_MODEL_ID env -> team default -> per-call configurable Team defaults live in team_settings and are validated against the SUPPORTED_MODELS allowlist + the model's supported reasoning efforts. 'Inherit from env' clears the override and falls back to LLM_MODEL_ID. * refactor(models): drop LLM_MODEL_ID env in favour of the team default The team default is now the single source of truth for the runtime model choice; per-user (agent) and per-call configurable (reviewer) selections still win on top. When no admin has touched the team default, it surfaces the hardcoded fallback (DEFAULT_MODEL_ID + its default effort), so the admin UI's dropdown is always pre-populated with a sensible value. The Admin UI loses the 'Inherit from env' option since there is no longer an env layer to inherit from. * chore(models): set hardcoded fallback to gpt-5.5 medium Decouple the team-default boot value (gpt-5.5 / medium) from each model's ProfileForm-suggested default_effort so we can change one without nudging the other. The Opus xhigh default for new user profiles is unchanged. * feat(dashboard): trigger-mode copy, Coming Soon badges, logout in My Settings - Rename trigger mode 'ready_for_review' -> 'once_per_pr' with new description copy that matches the screenshot. Legacy stored values fall back to 'every_push' on read so the UI never shows an unknown selection. - Add a 'Coming soon' badge + greyed-out + disabled state on the controls that don't have runtime consumers yet: Trigger Mode, Autofix Mode, Autofix Severity Threshold, and Automatically fix CI failures. SettingsRow grew a comingSoon prop to keep this consistent. - My Settings drops the noop PR Preferences section and adds a Sign Out button. preferred_pr_destination is removed from the profile schema; old records get the field popped on next write. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-21 09:17:07 -07:00
<SettingsSection title="Account">
feat: refactor UI into sidebar layout (#1318) * feat(dashboard): refactor UI into Cursor-style sidebar layout Replace the top-bar AppHeader with a left sidebar that holds the four primary sections (My Settings, Cloud Agents, Open SWE Review, Integrations) and the user account menu at the bottom. Admin remains a hidden route reachable from the sidebar only when is_admin. UI: - AppShell + AppSidebar with bottom-anchored avatar/sign-out menu. - My Settings: profile (first/last name) + PR destination preference. - Cloud Agents: model/effort, default repo, base branch, branch prefix, PR toggles (auto-fix CI, create PRs, allow artifacts), Slack notifications. - Open SWE Review: trigger mode, draft reviews, PR summaries, autofix mode + severity threshold, plus the existing per-repo review-style prompts (was /review-styles). - Integrations: GitHub install status + Slack/Linear status rows. - New Switch and Menu primitives. Backend: - Extend ProfileUpdate with first_name, last_name, base_branch, branch_prefix, auto_fix_ci, create_prs, allow_artifacts, slack_notifications, preferred_pr_destination. - Add team_settings module + /team-settings GET (any session) / PUT (admin only) for the reviewer configuration. * fix(dashboard): replace base-ui Menu with plain dropdown, stabilise Selects - base-ui's Menu hit an "Invalid hook call / Cannot read properties of null (reading 'useRef')" crash inside fastComponent under Vite's dep optimisation. The sidebar account menu is the only consumer, so swap it for a useState-driven dropdown with click-outside + Esc handling and drop the unused Menu wrapper. - Initialise the Open SWE Review settings form with the same defaults the server returns instead of `null`, so the Selects don't switch from uncontrolled (`undefined`) to controlled on first data load. * fix(dashboard): address review feedback on admin overwrite + form reset - admin PUT /admin/profiles/{login} only sent model/effort/repo from the admin form. ProfileUpdate's Pydantic defaults then wrote auto_fix_ci / create_prs / slack_notifications etc back onto the target user's profile, clobbering whatever they had configured. Switch to model_dump(exclude_unset=True) and overlay the incoming fields on the user's existing stored profile so only sent fields change. - my-settings.tsx and cloud-agents.tsx initialised local state from profile.data on every change. Because useSaveProfile updates the cached profile on success, toggling any control would overwrite the user's typed-but-unsaved input in another field. Guard the initialisation effect with a ref so it runs once on first load. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-20 14:13:23 -07:00
<SettingsRow
feat: restructure Open SWE Review tab + wire create_prs (#1319) * feat(dashboard): restructure Open SWE Review tab + wire create_prs Restructures the dashboard around two related changes the reviewer settings have been asking for: - Wire profile.create_prs. Defaults to true (opt-out); when off the system prompt gets a `Pull Request Policy Override` section telling the agent to push the branch and notify with the branch URL instead of opening a PR. Removes the noop Slack Notifications / Allow Artifacts / First Name / Last Name controls and their schema fields. - Repositories opt-in for Open SWE Review. New per-team enabled list stored in the LangGraph Store (`["enabled_review_repos"]`). Every reviewer webhook chokepoint now goes through `_is_repo_enabled_for_review` which AND-combines the existing env allowlist with the dashboard list. Default is empty (opt-in) — admins enable repos per-installation from the new Repositories page nested under Open SWE Review. - Open SWE Review tab now mirrors the Cursor "rules" pattern: main page shows installation rows + a Rules entry; both drill into nested pages (/review/repositories/$owner and /review/styles) with a back link. - Adds the new logo/favicon assets shipped from sidebar + html head. Tests pass with a new autouse fixture (`tests/conftest.py`) that defaults `is_review_repo_enabled` to True for existing allowlist tests. * fix(dashboard): make main content scroll independently of the sidebar Outer flex container was min-h-svh, so it grew with main's content and the whole page scrolled — sidebar moved with it. Pin to h-svh + overflow-hidden so the sidebar stays put and only <main> scrolls. * fix(dashboard): make disabled repo toggles obviously disabled Switch's disabled state used opacity-50 against a muted background, so the not-admin state looked nearly identical to the off state. Bump to opacity-40 + grayscale, and wrap each repo toggle in a span carrying a native hover tooltip explaining why it's disabled. * fix(switch): handle base-ui's data-disabled state base-ui's Switch.Root sets data-disabled (not the HTML disabled attribute) when disabled, so Tailwind's disabled: variant never matches and the button keeps its cursor-pointer + clickable look. Mirror the styling under the data-[disabled] variant and add pointer-events-none so the disabled state is both visible and actually unclickable. * feat(dashboard): paginate per-installation repository list 20 repos per page with Prev / page X of Y / Next controls at the bottom. Pager only renders when there are more than 20 repos. Page resets to 0 when navigating between installations. * feat(dashboard): global default model selectors for Agent + Reviewer Adds team-wide default model + reasoning effort for both agents in the Admin tab so operators can switch models without redeploying. Resolution chain: Agent: hardcoded -> LLM_MODEL_ID env -> team default -> user profile Reviewer: hardcoded -> LLM_MODEL_ID env -> team default -> per-call configurable Team defaults live in team_settings and are validated against the SUPPORTED_MODELS allowlist + the model's supported reasoning efforts. 'Inherit from env' clears the override and falls back to LLM_MODEL_ID. * refactor(models): drop LLM_MODEL_ID env in favour of the team default The team default is now the single source of truth for the runtime model choice; per-user (agent) and per-call configurable (reviewer) selections still win on top. When no admin has touched the team default, it surfaces the hardcoded fallback (DEFAULT_MODEL_ID + its default effort), so the admin UI's dropdown is always pre-populated with a sensible value. The Admin UI loses the 'Inherit from env' option since there is no longer an env layer to inherit from. * chore(models): set hardcoded fallback to gpt-5.5 medium Decouple the team-default boot value (gpt-5.5 / medium) from each model's ProfileForm-suggested default_effort so we can change one without nudging the other. The Opus xhigh default for new user profiles is unchanged. * feat(dashboard): trigger-mode copy, Coming Soon badges, logout in My Settings - Rename trigger mode 'ready_for_review' -> 'once_per_pr' with new description copy that matches the screenshot. Legacy stored values fall back to 'every_push' on read so the UI never shows an unknown selection. - Add a 'Coming soon' badge + greyed-out + disabled state on the controls that don't have runtime consumers yet: Trigger Mode, Autofix Mode, Autofix Severity Threshold, and Automatically fix CI failures. SettingsRow grew a comingSoon prop to keep this consistent. - My Settings drops the noop PR Preferences section and adds a Sign Out button. preferred_pr_destination is removed from the profile schema; old records get the field popped on next write. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-21 09:17:07 -07:00
label="Sign out"
description="End your dashboard session."
feat: refactor UI into sidebar layout (#1318) * feat(dashboard): refactor UI into Cursor-style sidebar layout Replace the top-bar AppHeader with a left sidebar that holds the four primary sections (My Settings, Cloud Agents, Open SWE Review, Integrations) and the user account menu at the bottom. Admin remains a hidden route reachable from the sidebar only when is_admin. UI: - AppShell + AppSidebar with bottom-anchored avatar/sign-out menu. - My Settings: profile (first/last name) + PR destination preference. - Cloud Agents: model/effort, default repo, base branch, branch prefix, PR toggles (auto-fix CI, create PRs, allow artifacts), Slack notifications. - Open SWE Review: trigger mode, draft reviews, PR summaries, autofix mode + severity threshold, plus the existing per-repo review-style prompts (was /review-styles). - Integrations: GitHub install status + Slack/Linear status rows. - New Switch and Menu primitives. Backend: - Extend ProfileUpdate with first_name, last_name, base_branch, branch_prefix, auto_fix_ci, create_prs, allow_artifacts, slack_notifications, preferred_pr_destination. - Add team_settings module + /team-settings GET (any session) / PUT (admin only) for the reviewer configuration. * fix(dashboard): replace base-ui Menu with plain dropdown, stabilise Selects - base-ui's Menu hit an "Invalid hook call / Cannot read properties of null (reading 'useRef')" crash inside fastComponent under Vite's dep optimisation. The sidebar account menu is the only consumer, so swap it for a useState-driven dropdown with click-outside + Esc handling and drop the unused Menu wrapper. - Initialise the Open SWE Review settings form with the same defaults the server returns instead of `null`, so the Selects don't switch from uncontrolled (`undefined`) to controlled on first data load. * fix(dashboard): address review feedback on admin overwrite + form reset - admin PUT /admin/profiles/{login} only sent model/effort/repo from the admin form. ProfileUpdate's Pydantic defaults then wrote auto_fix_ci / create_prs / slack_notifications etc back onto the target user's profile, clobbering whatever they had configured. Switch to model_dump(exclude_unset=True) and overlay the incoming fields on the user's existing stored profile so only sent fields change. - my-settings.tsx and cloud-agents.tsx initialised local state from profile.data on every change. Because useSaveProfile updates the cached profile on success, toggling any control would overwrite the user's typed-but-unsaved input in another field. Guard the initialisation effect with a ref so it runs once on first load. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-20 14:13:23 -07:00
control={
feat: restructure Open SWE Review tab + wire create_prs (#1319) * feat(dashboard): restructure Open SWE Review tab + wire create_prs Restructures the dashboard around two related changes the reviewer settings have been asking for: - Wire profile.create_prs. Defaults to true (opt-out); when off the system prompt gets a `Pull Request Policy Override` section telling the agent to push the branch and notify with the branch URL instead of opening a PR. Removes the noop Slack Notifications / Allow Artifacts / First Name / Last Name controls and their schema fields. - Repositories opt-in for Open SWE Review. New per-team enabled list stored in the LangGraph Store (`["enabled_review_repos"]`). Every reviewer webhook chokepoint now goes through `_is_repo_enabled_for_review` which AND-combines the existing env allowlist with the dashboard list. Default is empty (opt-in) — admins enable repos per-installation from the new Repositories page nested under Open SWE Review. - Open SWE Review tab now mirrors the Cursor "rules" pattern: main page shows installation rows + a Rules entry; both drill into nested pages (/review/repositories/$owner and /review/styles) with a back link. - Adds the new logo/favicon assets shipped from sidebar + html head. Tests pass with a new autouse fixture (`tests/conftest.py`) that defaults `is_review_repo_enabled` to True for existing allowlist tests. * fix(dashboard): make main content scroll independently of the sidebar Outer flex container was min-h-svh, so it grew with main's content and the whole page scrolled — sidebar moved with it. Pin to h-svh + overflow-hidden so the sidebar stays put and only <main> scrolls. * fix(dashboard): make disabled repo toggles obviously disabled Switch's disabled state used opacity-50 against a muted background, so the not-admin state looked nearly identical to the off state. Bump to opacity-40 + grayscale, and wrap each repo toggle in a span carrying a native hover tooltip explaining why it's disabled. * fix(switch): handle base-ui's data-disabled state base-ui's Switch.Root sets data-disabled (not the HTML disabled attribute) when disabled, so Tailwind's disabled: variant never matches and the button keeps its cursor-pointer + clickable look. Mirror the styling under the data-[disabled] variant and add pointer-events-none so the disabled state is both visible and actually unclickable. * feat(dashboard): paginate per-installation repository list 20 repos per page with Prev / page X of Y / Next controls at the bottom. Pager only renders when there are more than 20 repos. Page resets to 0 when navigating between installations. * feat(dashboard): global default model selectors for Agent + Reviewer Adds team-wide default model + reasoning effort for both agents in the Admin tab so operators can switch models without redeploying. Resolution chain: Agent: hardcoded -> LLM_MODEL_ID env -> team default -> user profile Reviewer: hardcoded -> LLM_MODEL_ID env -> team default -> per-call configurable Team defaults live in team_settings and are validated against the SUPPORTED_MODELS allowlist + the model's supported reasoning efforts. 'Inherit from env' clears the override and falls back to LLM_MODEL_ID. * refactor(models): drop LLM_MODEL_ID env in favour of the team default The team default is now the single source of truth for the runtime model choice; per-user (agent) and per-call configurable (reviewer) selections still win on top. When no admin has touched the team default, it surfaces the hardcoded fallback (DEFAULT_MODEL_ID + its default effort), so the admin UI's dropdown is always pre-populated with a sensible value. The Admin UI loses the 'Inherit from env' option since there is no longer an env layer to inherit from. * chore(models): set hardcoded fallback to gpt-5.5 medium Decouple the team-default boot value (gpt-5.5 / medium) from each model's ProfileForm-suggested default_effort so we can change one without nudging the other. The Opus xhigh default for new user profiles is unchanged. * feat(dashboard): trigger-mode copy, Coming Soon badges, logout in My Settings - Rename trigger mode 'ready_for_review' -> 'once_per_pr' with new description copy that matches the screenshot. Legacy stored values fall back to 'every_push' on read so the UI never shows an unknown selection. - Add a 'Coming soon' badge + greyed-out + disabled state on the controls that don't have runtime consumers yet: Trigger Mode, Autofix Mode, Autofix Severity Threshold, and Automatically fix CI failures. SettingsRow grew a comingSoon prop to keep this consistent. - My Settings drops the noop PR Preferences section and adds a Sign Out button. preferred_pr_destination is removed from the profile schema; old records get the field popped on next write. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-21 09:17:07 -07:00
<Button size="sm" variant="outline" onClick={() => void handleLogout()}>
Sign out
</Button>
feat: refactor UI into sidebar layout (#1318) * feat(dashboard): refactor UI into Cursor-style sidebar layout Replace the top-bar AppHeader with a left sidebar that holds the four primary sections (My Settings, Cloud Agents, Open SWE Review, Integrations) and the user account menu at the bottom. Admin remains a hidden route reachable from the sidebar only when is_admin. UI: - AppShell + AppSidebar with bottom-anchored avatar/sign-out menu. - My Settings: profile (first/last name) + PR destination preference. - Cloud Agents: model/effort, default repo, base branch, branch prefix, PR toggles (auto-fix CI, create PRs, allow artifacts), Slack notifications. - Open SWE Review: trigger mode, draft reviews, PR summaries, autofix mode + severity threshold, plus the existing per-repo review-style prompts (was /review-styles). - Integrations: GitHub install status + Slack/Linear status rows. - New Switch and Menu primitives. Backend: - Extend ProfileUpdate with first_name, last_name, base_branch, branch_prefix, auto_fix_ci, create_prs, allow_artifacts, slack_notifications, preferred_pr_destination. - Add team_settings module + /team-settings GET (any session) / PUT (admin only) for the reviewer configuration. * fix(dashboard): replace base-ui Menu with plain dropdown, stabilise Selects - base-ui's Menu hit an "Invalid hook call / Cannot read properties of null (reading 'useRef')" crash inside fastComponent under Vite's dep optimisation. The sidebar account menu is the only consumer, so swap it for a useState-driven dropdown with click-outside + Esc handling and drop the unused Menu wrapper. - Initialise the Open SWE Review settings form with the same defaults the server returns instead of `null`, so the Selects don't switch from uncontrolled (`undefined`) to controlled on first data load. * fix(dashboard): address review feedback on admin overwrite + form reset - admin PUT /admin/profiles/{login} only sent model/effort/repo from the admin form. ProfileUpdate's Pydantic defaults then wrote auto_fix_ci / create_prs / slack_notifications etc back onto the target user's profile, clobbering whatever they had configured. Switch to model_dump(exclude_unset=True) and overlay the incoming fields on the user's existing stored profile so only sent fields change. - my-settings.tsx and cloud-agents.tsx initialised local state from profile.data on every change. Because useSaveProfile updates the cached profile on success, toggling any control would overwrite the user's typed-but-unsaved input in another field. Guard the initialisation effect with a ref so it runs once on first load. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-20 14:13:23 -07:00
}
/>
</SettingsSection>
feat: auto-review PRs on opened / ready-for-review (#1325) * feat: auto-review PRs on opened / ready-for-review Trigger Open SWE Review on `pull_request` actions `opened` and `ready_for_review` against the canonical reviewer thread (no need to request open-swe[bot] as a reviewer). `converted_to_draft` now also flips watch=False on the existing reviewer thread. Draft PRs are gated by a tri-state user setting on the profile: inherit team default, always on, or always off. The team-wide `review_draft_prs` setting is the org-wide default; each user can override it in My Settings. External contributors with no Open SWE profile fall back to the team default. * fix: PR review comments — auth source + draft-aware watch toggle - `process_github_pr_ready` now dispatches with `source="github"` so the auth resolver finds the bot token persisted on the thread. The previous `source="github_auto"` fell through to the email-based path in non bot-token-only deployments and failed with a missing-user-email error. - `converted_to_draft` no longer unconditionally clears `watch`. When the PR author's effective `review_draft_prs` setting is on, watch stays on so subsequent pushes still trigger re-reviews while the PR is in draft. * feat(reviewer): skip "no issues found" comment on empty re-reviews A re-review run with no new findings to surface no longer posts another "Open SWE Review: No issues found" comment on the PR. The "no issues" summary now only appears on the first review of a PR — matching Devin's behavior, where subsequent reviews are silent unless there's something new to flag. Resolved-thread reconciliation and ``last_reviewed_sha`` persistence still happen on the skipped path, so findings the user just fixed still get their GitHub threads marked resolved, and the next push event sees an up-to-date dedup SHA. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-22 13:36:14 -07:00
{error && <p className="text-xs text-destructive">{error}</p>}
feat: refactor UI into sidebar layout (#1318) * feat(dashboard): refactor UI into Cursor-style sidebar layout Replace the top-bar AppHeader with a left sidebar that holds the four primary sections (My Settings, Cloud Agents, Open SWE Review, Integrations) and the user account menu at the bottom. Admin remains a hidden route reachable from the sidebar only when is_admin. UI: - AppShell + AppSidebar with bottom-anchored avatar/sign-out menu. - My Settings: profile (first/last name) + PR destination preference. - Cloud Agents: model/effort, default repo, base branch, branch prefix, PR toggles (auto-fix CI, create PRs, allow artifacts), Slack notifications. - Open SWE Review: trigger mode, draft reviews, PR summaries, autofix mode + severity threshold, plus the existing per-repo review-style prompts (was /review-styles). - Integrations: GitHub install status + Slack/Linear status rows. - New Switch and Menu primitives. Backend: - Extend ProfileUpdate with first_name, last_name, base_branch, branch_prefix, auto_fix_ci, create_prs, allow_artifacts, slack_notifications, preferred_pr_destination. - Add team_settings module + /team-settings GET (any session) / PUT (admin only) for the reviewer configuration. * fix(dashboard): replace base-ui Menu with plain dropdown, stabilise Selects - base-ui's Menu hit an "Invalid hook call / Cannot read properties of null (reading 'useRef')" crash inside fastComponent under Vite's dep optimisation. The sidebar account menu is the only consumer, so swap it for a useState-driven dropdown with click-outside + Esc handling and drop the unused Menu wrapper. - Initialise the Open SWE Review settings form with the same defaults the server returns instead of `null`, so the Selects don't switch from uncontrolled (`undefined`) to controlled on first data load. * fix(dashboard): address review feedback on admin overwrite + form reset - admin PUT /admin/profiles/{login} only sent model/effort/repo from the admin form. ProfileUpdate's Pydantic defaults then wrote auto_fix_ci / create_prs / slack_notifications etc back onto the target user's profile, clobbering whatever they had configured. Switch to model_dump(exclude_unset=True) and overlay the incoming fields on the user's existing stored profile so only sent fields change. - my-settings.tsx and cloud-agents.tsx initialised local state from profile.data on every change. Because useSaveProfile updates the cached profile on success, toggling any control would overwrite the user's typed-but-unsaved input in another field. Guard the initialisation effect with a ref so it runs once on first load. --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-20 14:13:23 -07:00
</AppShell>
);
}