Skip to content

Fix broken new-interlinear-project#122

Merged
alex-rawlings-yyc merged 7 commits into
mainfrom
fix-create-proj
Jun 23, 2026
Merged

Fix broken new-interlinear-project#122
alex-rawlings-yyc merged 7 commits into
mainfrom
fix-create-proj

Conversation

@imnasnainaec

@imnasnainaec imnasnainaec commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Problem

Clicking "New Interlinear Project…" → "Create" did nothing — no project was created and the project list stayed empty. The New dialog was calling an internal draft-seeding function instead of persisting to the backend.

Changes

ProjectModals.tsx

  • Replaced the draft-only New flow with an immediate backend call to interlinearizer.createProject, so the project appears in "Select Interlinear Project…" right away
  • Added isCreating state to disable Create/Cancel buttons during the backend round-trip (prevents double-submit)
  • On backend failure the create dialog stays open so the user can retry without re-entering their name, description, and language; previously the dialog closed unconditionally
  • Removed a duplicate papi.notifications.send from the catch block — interlinearizer.createProject already sends its own error notification before rethrowing (matching the convention used by Save As)

useDraftProject.ts

  • Updated newDraft JSDoc to reflect that the caller is responsible for immediately persisting the new project to the backend

Tests

  • Added coverage for the create-success path, the backend-failure path (modal stays open), and the invalid-response-shape path
  • Added coverage for the discard-confirm → create → success path

Test plan

  • Click "New Interlinear Project…", fill in a name, click "Create" — project appears in "Select Interlinear Project…" immediately
  • Repeat with unsaved changes in the draft — discard confirm appears; confirming creates the project; canceling returns to the create form with inputs intact
  • Simulate a backend failure (kill FW Lite before clicking Create) — one error notification appears; create dialog stays open for retry
  • npm test passes

This change is Reviewable

Summary by CodeRabbit

Release Notes

  • New Features
    • Project creation workflow streamlined—new projects are now created and persisted to the backend immediately upon submission, eliminating the need for manual save operations afterward.
    • Create and cancel actions are disabled during backend persistence to reflect in-flight state and prevent concurrent submissions.

@imnasnainaec imnasnainaec self-assigned this Jun 22, 2026
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@imnasnainaec, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 28 minutes and 7 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 21f103a5-85a4-4b5f-90e2-02ef43e24d99

📥 Commits

Reviewing files that changed from the base of the PR and between 5f32de4 and d89e86f.

📒 Files selected for processing (1)
  • src/__tests__/components/modals/ProjectModals.test.tsx
📝 Walkthrough

Walkthrough

The "New Project" modal flow is refactored from a deferred two-step process (empty draft + later Save As) to immediate backend persistence. A new async createAndPersistProject helper seeds the draft, calls interlinearizer.createProject, and sets or resets activeProject based on the result. An isCreating flag gates UI buttons during the round-trip. Tests are updated to cover success, failure, and dirty-draft discard paths.

Changes

New Project Immediate-Persist Flow

Layer / File(s) Summary
createAndPersistProject helper and isCreating state
src/components/modals/ProjectModals.tsx, src/hooks/useDraftProject.ts
Adds isCreating boolean state; replaces synchronous startNewDraft with async createAndPersistProject that seeds the draft via newDraft, calls interlinearizer.createProject, validates the result, sets activeProject on success or resets it on failure, and returns a boolean. Doc comments on the module, deferred-action type, newDraft prop, and UseDraftProjectResult.newDraft are updated to describe immediate persistence semantics.
Async submit handler and deferred discard path
src/components/modals/ProjectModals.tsx
Converts handleCreateDraft to an async submit handler that defers to pendingReplace { kind: 'new' } when the draft is dirty; otherwise sets isCreating, awaits createAndPersistProject, closes the create modal on success, and clears isCreating in a finally. Updates handleConfirmReplace's deferred 'new' branch to use the same async create-and-persist behavior. Passes isCreating as isSubmitting to CreateProjectModal.
Tests: success, failure, and dirty-draft discard paths
src/__tests__/components/modals/ProjectModals.test.tsx
Adds makeWebViewStateWithActiveProjectSpies helper. Replaces the old empty-draft test with: a successful create-submit test (asserts newDraft call, createProject invocation, and activeProject set without reset); two failure-path tests (assert activeProject reset, optional error notification, modal stays open); a dirty-draft test (asserts discard modal blocks newDraft and backend creation until confirmed); and a post-confirm test (asserts backend creation, modal close, and activeProject set to created project).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • sillsdev/interlinearizer-extension#33: The main PR's createAndPersistProject directly calls and branches on interlinearizer.createProject's return value (setting/resetting activeProject), which is the command/storage functionality introduced in that PR.
  • sillsdev/interlinearizer-extension#113: Both PRs wire isSubmitting-based disabling in CreateProjectModal/DiscardDraftConfirm during backend round-trips.
  • sillsdev/interlinearizer-extension#120: Both modify handleConfirmReplace in ProjectModals.tsx, specifically its deferred pendingReplace path and finally-block cleanup behavior.

Suggested reviewers

  • alex-rawlings-yyc

Poem

🐇 Hop, hop, no waiting around,
The draft is seeded, the project is found!
createAndPersist calls the back,
No empty draft left on the rack.
isCreating flips, the buttons grey,
On success we close — hip hip hooray! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly identifies the core issue fixed: the broken new-interlinear-project creation flow now works by immediately persisting to the backend instead of just drafting.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-create-proj

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

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

imnasnainaec and others added 3 commits June 22, 2026 15:37
- Restructure createAndPersistProject to eliminate duplicate
  setCreateSourceIsSelect/setModal calls via a single exit path
- Set isCreating during the discard-confirm createAndPersistProject
  path so the create modal buttons are disabled in that flow too
- Update stale JSDoc: PendingReplace comment and component summary
  now accurately describe the immediate-persist New behavior
- Split the old New-flow test into success and failure cases so
  resetActiveProject is asserted for the right reason in each
- Add validation-failure test (parse succeeds, non-project shape)
  to cover lines 242-245 and restore 100% coverage
- Fix no-type-assertion lint error in makeWebViewStateWithActiveProjectSpies
- Update discard-confirm test to assert createProject is called after confirm

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Matches the openProject convention: the catch block now sends the same
notification as the validation-failure path, so transport-level
rejections (where the backend may not have reached its own notification
send) also surface visible feedback to the user.

Expanded the JSDoc to document why the pre-round-trip newDraft() call
is safe: dirty=false means all draft data is committed to the active
project (or the draft was empty), and dirty=true means the user has
already confirmed discarding via DiscardDraftConfirm.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove papi.notifications.send from createAndPersistProject catch
  block; interlinearizer.createProject already sends its own
  notification before rethrowing, so the second call caused a
  duplicate toast (handleSaveAsNew follows the same convention)
- createAndPersistProject now returns boolean success; callers own
  the modal transition so the create modal stays open on failure,
  preserving the user name/description/language inputs for retry
- useDraftProject.ts newDraft JSDoc updated to reflect that the
  caller is responsible for immediately persisting to the backend

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The handleConfirmReplace else branch was uncovered: the existing
test used the default mock (undefined -> JSON.parse throws ->
success false), leaving the if (success) close path unreached.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@imnasnainaec

Copy link
Copy Markdown
Contributor Author

@alex-rawlings-yyc The behavior of the "New Interlinear Project..." route wasn't making sense to me, but this change is perhaps moving away from an intentional design choice. Could you review and/or test it out and let me know if that's the case.

@alex-rawlings-yyc alex-rawlings-yyc left a comment

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.

@imnasnainaec I think what I wanted "New Interlinear Project..." to do initially was act like "New..." in a word processor, but prompting the user to add information deviated from that design and "Wipe..." does that well enough on its own. I think what you've done here is better; feel free to merge (though I imagine GitHub will want me to review again once this is no longer a draft)

@alex-rawlings-yyc reviewed 3 files and all commit messages, and made 1 comment.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on imnasnainaec).

@imnasnainaec imnasnainaec marked this pull request as ready for review June 23, 2026 17:36

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/__tests__/components/modals/ProjectModals.test.tsx`:
- Around line 633-640: The assertion on lines 633-640 uses expect.anything() for
all arguments, but this matcher does not match undefined values. Since the
interlinearizer.createProject signature includes undefined as argument 4, this
allows false negatives where the command could be called with undefined and the
test would still pass. Replace the expect.anything() placeholders with the
actual expected argument values or appropriate matchers (such as undefined where
the signature expects it) to ensure the assertion properly validates that the
command was not called with the expected parameters.

In `@src/components/modals/ProjectModals.tsx`:
- Around line 292-307: The handleCreateDraft function lacks a synchronous
in-flight guard, allowing rapid double-clicks to trigger createAndPersistProject
twice before the state update from setIsCreating(true) takes effect. Add a
synchronous guard at the beginning of handleCreateDraft (before the dirty check)
using a ref variable to track if a request is already in progress; return early
if a request is already in flight, and set this flag to true before calling
createAndPersistProject and to false in the finally block. Apply the same
pattern to any other similar functions mentioned in the comment (line 330-339
area).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e06d0d34-55d3-4e9e-bb3a-bfbab253709a

📥 Commits

Reviewing files that changed from the base of the PR and between 31ee21d and 5f32de4.

📒 Files selected for processing (3)
  • src/__tests__/components/modals/ProjectModals.test.tsx
  • src/components/modals/ProjectModals.tsx
  • src/hooks/useDraftProject.ts

Comment thread src/__tests__/components/modals/ProjectModals.test.tsx
Comment thread src/components/modals/ProjectModals.tsx
imnasnainaec and others added 2 commits June 23, 2026 14:05
expect.anything() does not match undefined, so not.toHaveBeenCalledWith
with five expect.anything() placeholders passed even when createProject
was called with undefined as arg 4. Replace with the exact expected
argument values so the assertion correctly guards the not-yet-called path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Only arg 4 (undefined targetProjectId) needed to be explicit;
the other arguments are non-undefined so expect.anything() is correct.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@alex-rawlings-yyc alex-rawlings-yyc left a comment

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.

@alex-rawlings-yyc reviewed 1 file and all commit messages.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on imnasnainaec).

@alex-rawlings-yyc alex-rawlings-yyc merged commit 39fa413 into main Jun 23, 2026
11 checks passed
@alex-rawlings-yyc alex-rawlings-yyc deleted the fix-create-proj branch June 23, 2026 18:19
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.

2 participants