Skip to content

Vps 109/add group member from UI#423

Draft
rgin216 wants to merge 5 commits into
masterfrom
VPS-109/Add-group-member-from-UI
Draft

Vps 109/add group member from UI#423
rgin216 wants to merge 5 commits into
masterfrom
VPS-109/Add-group-member-from-UI

Conversation

@rgin216

@rgin216 rgin216 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Issue

Managing scenario group members required exporting the group CSV, manually adding a row, and importing it back into the application.

Solution

Added a UI flow on the manage-groups page to add a single member directly from the application. This includes a modal for name, email, role, and group number, plus a backend endpoint that appends the member without rebuilding all groups. The endpoint validates required fields, duplicate emails, and duplicate roles within a group.

Risk

Low to moderate. This adds a new group mutation path, so the main risk is inconsistent group/role data if validation misses an edge case. Existing CSV import behaviour is unchanged.

Checklist

  • Acceptance criteria met
  • Wiki documentation is written and up to date
  • Unit tests written and passing
  • Integration tests written and passing
  • Continuous integration build passing

Summary by CodeRabbit

  • New Features

    • Added "Add Member" modal and button to add group members (name, email, role, group number) with trimming/validation and submit state.
  • Bug Fixes

    • Backend now robustly handles concurrent member additions and legacy group records to prevent duplicates and ensure members are added reliably.
    • Role/email duplicate checks are case/whitespace-insensitive.
  • Tests

    • Expanded API tests, including a concurrency case verifying single-group creation with multiple simultaneous adds.

@linear

linear Bot commented Jun 5, 2026

Copy link
Copy Markdown

VPS-109

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds group-number schema/index and a DAO upsert/migration path, a new POST /:scenarioId/member API with trimming/validation and role/email checks, comprehensive concurrency-aware tests, and a frontend modal + page integration to submit and refresh group members.

Changes

Add Group Member Feature

Layer / File(s) Summary
Backend schema and DAO updates
backend/src/db/models/group.js, backend/src/db/daos/groupDao.js
Adds group string field and partial unique index on {scenarioId, group}; createGroup sets group; adds addUserToGroup that migrates legacy records, upserts by (scenarioId, group), pushes the user, and handles duplicate-key fallback.
Backend API endpoint and validation
backend/src/routes/api/group.js
Imports addUserToGroup, adds normalizeRole/trimUser, implements POST /:scenarioId/member with required-field trimming, per-scenario email uniqueness, per-group role uniqueness (case/whitespace-insensitive), scenario role-list extension, and delegates persistence to the DAO; removes old group-creation and role-list handlers.
Backend API integration tests
backend/src/routes/api/__tests__/groupApi.test.js
Jest ESM tests using MongoMemoryServer and an ephemeral Express server: seed Scenario/Group, test adding to existing group, creating a missing group, concurrent parallel posts producing one group with both members, and duplicate-email/role validations.
Frontend form modal for adding members
frontend/src/features/groups/AddGroupMemberModal.jsx
New AddGroupMemberModal component with initial state, reset-on-open, trimmed-field validation, datalist-backed Role/Group inputs, submit handler that calls onSubmit with trimmed values, and conditional UI for saving/error state.
Frontend page integration and API wiring
frontend/src/features/groups/ManageGroupsPage.jsx
Integrates the modal, adds modal visibility/isSaving state, memoizes users/groupNumbers/roles, implements addMember that POSTs to /api/group/${scenarioId}/member, shows toasts, refetches data, and renders the "Add Member" button.

🎯 3 (Moderate) | ⏱️ ~20 minutes

sequenceDiagram
  participant Browser
  participant ManageGroupsPage
  participant API as "POST /api/group/:scenarioId/member"
  participant DAO as addUserToGroup
  participant MongoDB as "Groups collection / Scenarios"

  Browser->>ManageGroupsPage: open modal, submit member payload
  ManageGroupsPage->>API: POST member (trimmed, normalized)
  API->>DAO: addUserToGroup(scenarioId, user)
  DAO->>MongoDB: find/update legacy Group or upsert Group (scenarioId, group) and $push user
  MongoDB-->>DAO: updated Group document
  DAO-->>API: return updated/created Group
  API-->>ManageGroupsPage: HTTP 200 + member payload
  ManageGroupsPage->>Browser: close modal, refetch groups, show toast
Loading

I dug through indexes and code,
pushed users where they belong,
one POST, two clicks in flight,
now groups sing the same song. 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% 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 directly summarizes the main change: adding a UI flow to create group members, which is the core objective of this PR.
Description check ✅ Passed The description covers all required template sections with adequate detail: Issue, Solution, Risk, and Checklist. The author clearly explains the problem, solution approach, and risks involved.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch VPS-109/Add-group-member-from-UI

Warning

Review ran into problems

🔥 Problems

Stopped waiting for pipeline failures after 30000ms. One of your pipelines takes longer than our 30000ms fetch window to run, so review may not consider pipeline-failure results for inline comments if any failures occurred after the fetch window. Increase the timeout if you want to wait longer or run a @coderabbit review after the pipeline has finished.


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

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 4

🤖 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 `@backend/src/db/daos/groupDao.js`:
- Around line 38-51: The addUserToGroup function has a race condition:
concurrent calls can both see no group and create duplicate Group documents; fix
by making the create-or-update atomic — either add a unique compound index on
(scenarioId, "users.group") on the Group model and handle duplicate-key errors,
or replace the read-then-create flow in addUserToGroup with an atomic
findOneAndUpdate using the Group model (match by scenarioId and "users.group":
user.group) with upsert: true and a $push of user (or $addToSet if duplicates
should be avoided), and remove the separate createGroup path; ensure you handle
the returned doc when upsert creates a new document and catch duplicate-key
errors if you also add the DB-level unique index.

In `@backend/src/routes/api/__tests__/groupApi.test.js`:
- Around line 73-78: The afterAll cleanup uses server.close with an async
callback which isn’t awaited; change it to await server.close via a Promise
(e.g., await new Promise(resolve => server.close(resolve))) and then
sequentially await mongoose.disconnect() and mongoServer.stop() outside the
server.close callback so mongoose.disconnect() and mongoServer.stop() reliably
complete; update the afterAll block and references to server.close,
mongoose.disconnect, and mongoServer.stop accordingly.

In `@frontend/src/features/groups/AddGroupMemberModal.jsx`:
- Around line 117-123: The AddGroupMemberModal's submit button currently only
uses visual styling but stays clickable; update the button in
AddGroupMemberModal.jsx to set the disabled attribute to the same condition
(disabled={ !isSubmittable || isSaving }) so it becomes non-interactive for
keyboard/screen readers, keep the className toggle for styling, and ensure
handleSubmit early-returns when !isSubmittable || isSaving to avoid accidental
invocation when disabled.

In `@frontend/src/features/groups/ManageGroupsPage.jsx`:
- Around line 172-188: The addMember function currently groups the POST and
refetch in one try so a refetch failure reports "There was an error adding the
member"; change this so the POST (api.post in addMember) is awaited in its own
try block and, on success, show the success toast and close the add modal
(setIsAddMemberOpen(false)), then perform refetch() in a separate try/catch: if
refetch fails, show a distinct warning/info toast indicating "Member added but
failed to refresh list" (and log the refetch error) rather than the generic add
error; preserve the finally behavior setting setIsAddingMember(false) and ensure
errors from the POST still show the original add-error toast and log.
🪄 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 Plus

Run ID: 97dfdda5-af37-4e1b-80fb-a4a02429f95b

📥 Commits

Reviewing files that changed from the base of the PR and between d383f2f and dc8c0f0.

📒 Files selected for processing (5)
  • backend/src/db/daos/groupDao.js
  • backend/src/routes/api/__tests__/groupApi.test.js
  • backend/src/routes/api/group.js
  • frontend/src/features/groups/AddGroupMemberModal.jsx
  • frontend/src/features/groups/ManageGroupsPage.jsx

Comment thread backend/src/db/daos/groupDao.js
Comment thread backend/src/routes/api/__tests__/groupApi.test.js
Comment thread frontend/src/features/groups/AddGroupMemberModal.jsx
Comment thread frontend/src/features/groups/ManageGroupsPage.jsx

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
frontend/src/features/groups/ManageGroupsPage.jsx (1)

172-194: ⚡ Quick win

Past review concern properly addressed; consider more actionable guidance on refetch failure.

The separation of POST and refetch is well-implemented—the member-add success is now correctly reported even if the subsequent list refresh fails.

One small UX refinement: the refetch-failure message on line 183 tells users what happened but not what to do. Consider prompting them to reload.

📝 Suggested UX improvement
-      toast("Member added but failed to refresh list");
+      toast("Member added but failed to refresh list. Please reload the page.");
🤖 Prompt for 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.

In `@frontend/src/features/groups/ManageGroupsPage.jsx` around lines 172 - 194, In
addMember, when the inner catch (refetchError) runs after await refetch(),
change the toast text to be actionable: include a short instruction like "Member
added but failed to refresh list — please reload the page or click 'Refresh'."
Update the toast call inside the refetch catch block (the block referencing
refetch and refetchError) to show this guidance and keep the console.error the
same so debugging details remain available.
🤖 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.

Nitpick comments:
In `@frontend/src/features/groups/ManageGroupsPage.jsx`:
- Around line 172-194: In addMember, when the inner catch (refetchError) runs
after await refetch(), change the toast text to be actionable: include a short
instruction like "Member added but failed to refresh list — please reload the
page or click 'Refresh'." Update the toast call inside the refetch catch block
(the block referencing refetch and refetchError) to show this guidance and keep
the console.error the same so debugging details remain available.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bbc15d66-bf97-46b4-9b4e-28b5adb443af

📥 Commits

Reviewing files that changed from the base of the PR and between dc8c0f0 and 22738f1.

📒 Files selected for processing (5)
  • backend/src/db/daos/groupDao.js
  • backend/src/db/models/group.js
  • backend/src/routes/api/__tests__/groupApi.test.js
  • frontend/src/features/groups/AddGroupMemberModal.jsx
  • frontend/src/features/groups/ManageGroupsPage.jsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/src/db/daos/groupDao.js
  • frontend/src/features/groups/AddGroupMemberModal.jsx

Comment on lines +19 to +21
group: {
type: String,
},

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.

i dont quite understand this field and the logic associated with it . what does the "group" of the group actually mean?

@harbassan harbassan marked this pull request as draft July 4, 2026 07:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants