Skip to content

refactor(desktop): polish NewProjectModal — toast errors, Label, spinner#4037

Merged
Kitenite merged 6 commits into
mainfrom
modal-error-selectable-2
May 4, 2026
Merged

refactor(desktop): polish NewProjectModal — toast errors, Label, spinner#4037
Kitenite merged 6 commits into
mainfrom
modal-error-selectable-2

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented May 4, 2026

Summary

Following up on #4036, align the New Project modal with the rest of the desktop app (and 1Code-style conventions):

  • Replace the inline destructive error block with toast.error from sonner. Toasts are selectable/copyable by default, match every other error surface in the app, and free up modal real estate.
  • Use the shadcn Label component for the form labels.
  • Add a spinning LuLoaderCircle to the Clone button while a clone is in flight.

Test plan

  • Open New Project modal and trigger each error path (no URL, bad URL, host service unavailable, server error) — verify toast surfaces and is selectable/copyable.
  • Verify spinner appears in the Clone button during a real clone.
  • Verify form labels still associate with their inputs (click the label, the input focuses).

Summary by cubic

Polished the New Project modal for a simpler, consistent UX. Errors now use selectable toasts, the dialog matches desktop styles, and the clone form is shown by default.

  • Refactors
    • Replaced inline error panel with toast.error from @superset/ui/sonner (selectable/copyable).
    • Switched form labels to Label from @superset/ui/label.
    • Added a spinning LuLoaderCircle to the Clone button while cloning.
    • Removed the mode selector; dialog title is now "Clone a repository" and the clone form is shown by default.
    • Matched desktop dialog conventions: narrower width (max-w-[420px]), default footer, ghost Cancel.

Written for commit c3a1e29. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Error notifications now appear as toast alerts for directory browse failures, validation errors, and clone/create issues, replacing inline banners.
    • Clone button shows a loading spinner with "Cloning..." while operations run.
    • Form fields use clearer label headings for improved usability; the UI is simplified to a single "Clone a repository" form.
  • Bug Fixes

    • Stale inline error messages and mode-related disabling no longer occur.

Kitenite added 2 commits May 4, 2026 01:24
The renderer applies user-select: none globally on body, which
prevented copying error messages out of the New Project modal —
exactly the text users want to share when reporting issues.
… toast

Errors in the New Project modal now surface via sonner toasts instead of
an inline destructive panel. Toasts are selectable and copyable by
default (no select-text override needed), match the convention used
across the rest of the desktop app, and free up modal real estate.

Also:
- Use shadcn Label for form labels
- Add a spinning loader to the Clone button while cloning
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 4, 2026

Greptile Summary

This PR polishes NewProjectModal by replacing the local error state + inline destructive block with toast.error from Sonner, upgrading plain <label> elements to the shadcn <Label> component, and adding a spinning LuLoaderCircle to the Clone button while a clone is in flight. The changes are clean and consistent with the rest of the desktop app's error-surface conventions.

Confidence Score: 4/5

Safe to merge; only style-level concerns, no runtime defects introduced.

All findings are P2. The logic for async error paths is correct; the only concern is UX ergonomics around synchronous validation messages being routed through auto-dismissing toasts.

No files require special attention; the single changed file is straightforward.

Important Files Changed

Filename Overview
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/components/NewProjectModal/NewProjectModal.tsx Replaces inline error state+block with sonner toasts, swaps <label> for shadcn <Label>, and adds LuLoaderCircle spinner to the Clone button. Logic is sound; only P2 concerns around using auto-dismissing toasts for synchronous validation errors and potential button layout.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A([Clone button clicked]) --> B{activeHostUrl?}
    B -- No --> T1[toast.error: Host service not available]
    B -- Yes --> C{url trimmed?}
    C -- No --> T2[toast.error: Please enter a repository URL]
    C -- Yes --> D{parentDir trimmed?}
    D -- No --> T3[toast.error: Please select a project location]
    D -- Yes --> E{name derived from URL?}
    E -- No --> T4[toast.error: Could not derive project name]
    E -- Yes --> F[setWorking true]
    F --> G[client.project.create.mutate]
    G -- success --> H[finalizeSetup → onSuccess → reset → close]
    G -- error --> I{isLeakedSql?}
    I -- Yes --> J[console.error + generic message]
    I -- No --> K[raw message]
    J --> L[toast.error 'Could not create project' + description]
    K --> L
    L --> M[onError callback]
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/components/NewProjectModal/NewProjectModal.tsx:130-142
**Transient toasts for synchronous validation errors**

Validation errors like "Please enter a repository URL" and "Please select a project location" are synchronous checks that apply to the current form state. Routing them through `toast.error` (which auto-dismisses after ~4 s by default in Sonner) means the user's attention must jump to the toast corner at the moment of submission, and the message disappears before they may have finished reading it or correcting the input. The old inline error block was persistent and contextually anchored to the form. For async operation failures (`handleBrowse`, the actual `create` call) toasts are the right pattern; for these synchronous field-level validations, consider keeping them inline (or at minimum using `{ duration: Infinity }` so the user can dismiss on their own terms).

### Issue 2 of 2
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/components/NewProjectModal/NewProjectModal.tsx:288-295
**Spinner button may need `gap` for layout**

The `working` state renders an icon + text fragment inside `<Button>`. If the `Button` component's default styles don't include a flex `gap`, the spinner and "Cloning…" text will be flush against each other. Verify that `@superset/ui/button` applies `gap-2` (or similar) to its children, or add it explicitly. Alternatively wrap both in a `<span className="flex items-center gap-2">` if the Button doesn't handle it.

Reviews (1): Last reviewed commit: "refactor(desktop): replace inline error ..." | Re-trigger Greptile

Comment on lines 130 to 142
if (!trimmedUrl) {
setError("Please enter a repository URL");
toast.error("Please enter a repository URL");
return;
}
if (!trimmedParent) {
setError("Please select a project location");
toast.error("Please select a project location");
return;
}
const name = deriveProjectNameFromUrl(trimmedUrl);
if (!name) {
setError("Could not derive a project name from the URL or path");
toast.error("Could not derive a project name from the URL or path");
return;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Transient toasts for synchronous validation errors

Validation errors like "Please enter a repository URL" and "Please select a project location" are synchronous checks that apply to the current form state. Routing them through toast.error (which auto-dismisses after ~4 s by default in Sonner) means the user's attention must jump to the toast corner at the moment of submission, and the message disappears before they may have finished reading it or correcting the input. The old inline error block was persistent and contextually anchored to the form. For async operation failures (handleBrowse, the actual create call) toasts are the right pattern; for these synchronous field-level validations, consider keeping them inline (or at minimum using { duration: Infinity } so the user can dismiss on their own terms).

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/components/NewProjectModal/NewProjectModal.tsx
Line: 130-142

Comment:
**Transient toasts for synchronous validation errors**

Validation errors like "Please enter a repository URL" and "Please select a project location" are synchronous checks that apply to the current form state. Routing them through `toast.error` (which auto-dismisses after ~4 s by default in Sonner) means the user's attention must jump to the toast corner at the moment of submission, and the message disappears before they may have finished reading it or correcting the input. The old inline error block was persistent and contextually anchored to the form. For async operation failures (`handleBrowse`, the actual `create` call) toasts are the right pattern; for these synchronous field-level validations, consider keeping them inline (or at minimum using `{ duration: Infinity }` so the user can dismiss on their own terms).

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +288 to +295
{working ? (
<>
<LuLoaderCircle className="size-4 animate-spin" />
Cloning…
</>
) : (
"Clone"
)}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Spinner button may need gap for layout

The working state renders an icon + text fragment inside <Button>. If the Button component's default styles don't include a flex gap, the spinner and "Cloning…" text will be flush against each other. Verify that @superset/ui/button applies gap-2 (or similar) to its children, or add it explicitly. Alternatively wrap both in a <span className="flex items-center gap-2"> if the Button doesn't handle it.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/components/NewProjectModal/NewProjectModal.tsx
Line: 288-295

Comment:
**Spinner button may need `gap` for layout**

The `working` state renders an icon + text fragment inside `<Button>`. If the `Button` component's default styles don't include a flex `gap`, the spinner and "Cloning…" text will be flush against each other. Verify that `@superset/ui/button` applies `gap-2` (or similar) to its children, or add it explicitly. Alternatively wrap both in a `<span className="flex items-center gap-2">` if the Button doesn't handle it.

How can I resolve this? If you propose a fix, please make it concise.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 4, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 58b946de-16b8-4af5-b0ef-f8e93fa6479b

📥 Commits

Reviewing files that changed from the base of the PR and between 1e19571 and c3a1e29.

📒 Files selected for processing (1)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/components/NewProjectModal/NewProjectModal.tsx

📝 Walkthrough

Walkthrough

NewProjectModal now surfaces all validation and operation failures via toast.error instead of local component state, removes the inline destructive error banner and mode picker, uses Label components for headings, and shows a spinner with “Cloning…” on the primary action while cloning.

Changes

Clone modal — error-reporting and UI simplification

Layer / File(s) Summary
Imports & State
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/components/NewProjectModal/NewProjectModal.tsx
Removed local error state and related icon import; added toast and Label use; retained parentDir, url, and working state.
Browse handling
.../NewProjectModal.tsx
handleBrowse reports directory-browse failures via toast.error(...) instead of setting component error.
Validation before create
.../NewProjectModal.tsx
createFromClone validates activeHostUrl, url, selected parent directory, and derived project name and emits toast.error(...) for each missing/invalid condition; early-returns on failures.
Create error mapping & callback
.../NewProjectModal.tsx
Create errors preserve detection of "Failed query:" envelopes, map them to a generic description, call onError?.(message), and surface the message via toast.error(...) instead of storing it locally; working is finalized in finally.
UI markup & actions
.../NewProjectModal.tsx
Removed mode picker and conditional mode UI, removed inline destructive error banner and dismiss logic, added Label headings for inputs, and made primary action show spinner + “Cloning…” and disable only while working.

Sequence Diagram(s)

(Skipped — changes are UI-focused and do not introduce a multi-component sequential flow requiring diagramming.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰
In a modal neat and bright, I hop and cheer,
Toasts sing problems that once lingered near.
Labels stand tall, the clone button hums,
Spinning “Cloning…” — onward the new project comes. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main refactoring changes: replacing errors with toasts, adding Label components, and implementing a spinner button.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description covers all required template sections: a clear summary of changes, related issues reference (following up on #4036), type of change (Refactor), testing instructions, and additional context including an auto-generated summary.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch modal-error-selectable-2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/8 reviews remaining, refill in 54 minutes and 7 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Kitenite added 4 commits May 4, 2026 01:40
- Tighter modal: rounded-xl, shadow-2xl, sm:max-w-md, edge-to-edge
  layout (gap-0 p-0) so the footer can render as a distinct band
- Footer band: muted background with top border, rounded-b corners
  inherited via overflow-hidden — matches 1Code's CanvasDialogFooter
- Mode option cards: rounded-xl, visible selected state
  (border-primary/40 + bg-primary/5 + shadow-sm), hover lift
- Buttons: subtle press feedback (active:scale-[0.97])
- Reordered: mode picker first, then location/url (matches the user's
  decision flow — pick mode, then configure)
Reverts the 1Code-imported edge-to-edge layout (gap-0/p-0 with muted
footer band, font-semibold title, active:scale buttons) since it
conflicts with the rest of our dialogs. Aligns with RenameBranchDialog:
default DialogContent at max-w-[420px], default footer, ghost Cancel.

Keeps the genuinely useful changes: narrower modal width, polished mode
cards with primary-tinted selected state, and the clone spinner.
Empty and Template were placeholder cards labelled "(coming soon)" —
they took up modal real estate without doing anything. With only clone
shipping, drop the picker entirely and present the clone form directly.
The dialog title now says "Clone a repository" so the action is obvious.
@Kitenite Kitenite merged commit ecbdfc7 into main May 4, 2026
5 of 7 checks passed
@Kitenite Kitenite deleted the modal-error-selectable-2 branch May 4, 2026 08:48
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ⚠️ Neon database branch

Thank you for your contribution! 🎉

MocA-Love pushed a commit to MocA-Love/superset that referenced this pull request May 21, 2026
…ner (superset-sh#4037)

* fix(desktop): make NewProjectModal error text selectable

The renderer applies user-select: none globally on body, which
prevented copying error messages out of the New Project modal —
exactly the text users want to share when reporting issues.

* refactor(desktop): replace inline error block in NewProjectModal with toast

Errors in the New Project modal now surface via sonner toasts instead of
an inline destructive panel. Toasts are selectable and copyable by
default (no select-text override needed), match the convention used
across the rest of the desktop app, and free up modal real estate.

Also:
- Use shadcn Label for form labels
- Add a spinning loader to the Clone button while cloning

* style(desktop): polish NewProjectModal with 1Code visual DNA

- Tighter modal: rounded-xl, shadow-2xl, sm:max-w-md, edge-to-edge
  layout (gap-0 p-0) so the footer can render as a distinct band
- Footer band: muted background with top border, rounded-b corners
  inherited via overflow-hidden — matches 1Code's CanvasDialogFooter
- Mode option cards: rounded-xl, visible selected state
  (border-primary/40 + bg-primary/5 + shadow-sm), hover lift
- Buttons: subtle press feedback (active:scale-[0.97])
- Reordered: mode picker first, then location/url (matches the user's
  decision flow — pick mode, then configure)

* style(desktop): match our dialog DNA in NewProjectModal

Reverts the 1Code-imported edge-to-edge layout (gap-0/p-0 with muted
footer band, font-semibold title, active:scale buttons) since it
conflicts with the rest of our dialogs. Aligns with RenameBranchDialog:
default DialogContent at max-w-[420px], default footer, ghost Cancel.

Keeps the genuinely useful changes: narrower modal width, polished mode
cards with primary-tinted selected state, and the clone spinner.

* refactor(desktop): drop disabled mode selector from NewProjectModal

Empty and Template were placeholder cards labelled "(coming soon)" —
they took up modal real estate without doing anything. With only clone
shipping, drop the picker entirely and present the clone form directly.
The dialog title now says "Clone a repository" so the action is obvious.
MocA-Love added a commit to MocA-Love/superset that referenced this pull request May 21, 2026
superset-sh#4037 NewProjectModal: drop fork-only OPTIONS multi-mode selector and adopt
upstream v2-only simplification (toast errors + Label + spinner). The
'empty'/'template' modes were 'coming soon' disabled entries with no
implementation behind them; no user-facing fork feature lost.

superset-sh#4191 DashboardSidebarDeleteDialog: keep fork's 'Checking…' confirm-button
label UX while incorporating upstream's fixed-height warning banner slot.
Restore isCheckingStatus destructure + prop pass; add to
DestroyConfirmPane interface so the deleting-button disabled state still
gates on it.

superset-sh#4045 schema.ts: backfill sidebarFileLinks field into v2UserPreferencesSchema
and DEFAULT_V2_USER_PREFERENCES (upstream introduced these in superset-sh#3988 which
fork will adopt in Phase 5). Also add 'shift' tier to linkTierMapSchema
(matches superset-sh#3988 LinkTier expansion) so superset-sh#4045's added schema tests pass.

superset-sh#4212 tray Quit Completely: replace upstream quitAppCompletely() with
fork's existing requestQuit('stop') which already performs full host-
service + pty-daemon teardown via prepareQuit('stop') + getTodoScheduler
stop + cleanupMainWindowResources. Tray menu entry retained.

superset-sh#4225 automations/page.tsx: drop iconUrl prop from ProjectThumbnail call
(fork ProjectThumbnail uses githubOwner, iconUrl arrives with superset-sh#3823 v2
project icon settings in cycle 41). Pass githubOwner={null} as transient
fallback.

superset-sh#4226 useBranchPickerController: fork's useWorkspaceCreates.submit returns
Promise<void> (in-flight tracker carries error state); upstream changed
it to Promise<SubmitResult>. Wrap submit in try/catch and navigate using
snapshot.id so the picker compiles. Note: foreign-worktree adopt path
with canonical workspaceId waiting is deferred to cycle 44b when
WorkspaceCreatesManager / submit API are upgraded.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant