Skip to content

Add assignee logic to issue create-edit-component#437

Merged
DVidal1205 merged 12 commits intomainfrom
add-assignee-logic
Apr 13, 2026
Merged

Add assignee logic to issue create-edit-component#437
DVidal1205 merged 12 commits intomainfrom
add-assignee-logic

Conversation

@mchdich
Copy link
Copy Markdown
Contributor

@mchdich mchdich commented Apr 12, 2026

Why

create-edit-component had a missing feature.

What

When you select a team to assign an issue to, you get another menu which allows you to select certain members of that team.

Test Plan

Merge me twin.

Checklist

  • Database: No schema changes, OR I have contacted the Development Lead to run db:push before merging
  • Environment Variables: No environment variables changed, OR I have contacted the Development Lead to modify them on Coolify BEFORE merging.

Summary by CodeRabbit

  • New Features

    • Add multi-assignee controls to issue create/edit with per-team loading, empty, and error states.
    • Assignee list is scoped to the selected team and is cleared when switching teams.
    • Backend now provides team member lists so the UI shows only relevant assignees.
  • Bug Fixes

    • Submissions now only accept and persist assignees that belong to the selected team; invalid assignments are rejected.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 12, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Loads team members for the issue create/edit dialog scoped to the selected team, exposes an "Assignees" checkbox UI, ensures assigneeIds are present in form state and filtered to team members before submission, resets assignees on team change, and makes assigneeIds required in issue edit types.

Changes

Cohort / File(s) Summary
Dialog Component
apps/blade/src/app/_components/issues/create-edit-dialog.tsx
Initialize assigneeIds in form state, load team-scoped users via api.issues.getUsersOnTeam (when open + effectiveTeam), compute assigneesForTeam and safeAssigneeIds (filtering form IDs to loaded team members), reset assigneeIds to [] on team change, add "Assignees" checkbox UI with loading/error/empty states, and submit assigneeIds: safeAssigneeIds for create/update/delete.
Issue Type Definitions
packages/consts/src/issue.ts
Added IssueAssigneeOption interface and changed IssueEditNode.assigneeIds from optional string[] to required UUID[].
API Router & Server Validation
packages/api/src/routers/issues.ts, packages/utils/src/permissions.server.ts
Added issuesRouter.getUsersOnTeam procedure returning deduped, locale-sorted users for a team; added server-side validators validateAssigneesBelongToTeam and validateIssueNodeAssignees, and integrated these checks into createIssue and updateIssue flows (including recursive children validation).

Sequence Diagram(s)

sequenceDiagram
    rect rgba(200,200,255,0.5)
    participant User as User
    end
    rect rgba(200,255,200,0.5)
    participant UI as CreateEditDialog (client)
    end
    rect rgba(255,200,200,0.5)
    participant API as Issues API
    end
    rect rgba(255,255,200,0.5)
    participant DB as Server/DB
    end

    User->>UI: Open dialog
    UI->>API: getUsersOnTeam(teamId) [if isOpen && effectiveTeam]
    API->>DB: Query Users ⟵ join Permissions where roleId = teamId
    DB-->>API: users rows
    API-->>UI: usersData / error
    UI->>UI: compute assigneesForTeam, safeAssigneeIds
    User->>UI: toggle assignee checkbox (update form.assigneeIds)
    User->>UI: Submit create/update/delete
    UI->>API: create/update/delete issue (assigneeIds: safeAssigneeIds)
    API->>DB: validate & persist issue (validate assignees belong to team)
    DB-->>API: ack
    API-->>UI: success / error
    UI-->>User: show result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

Blade, Constants, API, Feature, Major

Suggested reviewers

  • DVidal1205
🚥 Pre-merge checks | ✅ 4 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title does not include the required issue number in brackets (e.g., '[#437]') as specified in the title requirements. Update the title to start with '[#437]' followed by the description, e.g., '[#437] Add assignee logic to issue create-edit-component'.
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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
No Hardcoded Secrets ✅ Passed Comprehensive search confirms no hardcoded secrets across modified files. All sensitive data handling follows security best practices using runtime retrieval from secure sources.
Validated Env Access ✅ Passed All four modified files properly avoid direct process.env access and use validated imports, adhering to the codebase's centralized env configuration convention.
No Typescript Escape Hatches ✅ Passed The pull request contains no TypeScript escape hatches across all four modified files. All code uses proper type-safe patterns including optional chaining, nullish coalescing, and explicit type annotations.

✏️ 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 add-assignee-logic

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@apps/blade/src/app/_components/issues/create-edit-dialog.tsx`:
- Around line 742-759: The Checkbox and Label for each assignee are not
programmatically associated, breaking keyboard/screen-reader behavior; give the
Checkbox a stable id (e.g. `assignee-${user.id}`) and set the Label to point to
that id (htmlFor or for prop depending on your Label component) so clicking the
name toggles the checkbox and the checkbox gets an accessible name; update the
JSX where Checkbox, formValues.assigneeIds and updateForm are used to pass the
id to Checkbox and set the Label's htmlFor to the same string.
- Around line 313-320: safeAssigneeIds is being computed from an empty
assigneeIdSet while the users/assignees query is still loading, which causes
existing assignees to be stripped; update the logic in the Create/Edit dialog so
you either (A) keep using formValues.assigneeIds until assigneesForTeam (or
usersQuery) is loaded/successful (e.g. only filter when assigneeIdSet.size > 0),
or (B) prevent submit/rendering until the assignees query succeeds; specifically
modify the safeAssigneeIds computation (which references assigneeIdSet,
assigneesForTeam, and formValues.assigneeIds) or add a guard tied to the
users/assignees query state so existing assignees are preserved until the
options are available.
🪄 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: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 2f92d44d-c6f2-4c67-b62c-a41ae3b4212d

📥 Commits

Reviewing files that changed from the base of the PR and between 7adefc6 and e08b784.

📒 Files selected for processing (2)
  • apps/blade/src/app/_components/issues/create-edit-dialog.tsx
  • packages/consts/src/issue.ts

Comment thread apps/blade/src/app/_components/issues/create-edit-dialog.tsx
Comment thread apps/blade/src/app/_components/issues/create-edit-dialog.tsx Outdated
Copy link
Copy Markdown
Contributor

@DVidal1205 DVidal1205 left a comment

Choose a reason for hiding this comment

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

let's make a new endpoint here. currently, on account of raw users, we have about 2,000 rows in the production database. fetching all and processing on the frontend via client component requires all ~2,000 rows to make a round trip.

instead maybe add to the issues router a getUsersOnTeam procedure, that given an effective team id, just performs a join and passes the results back.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@apps/blade/src/app/_components/issues/create-edit-dialog.tsx`:
- Around line 728-755: The assignee checkbox IDs are unscoped and can collide
across multiple dialog instances; update the assigneeCheckboxId generation
inside the assigneesForTeam.map so it includes the component's baseId (e.g.,
`${baseId}-assignee-${user.id}`) and use that id for both the Checkbox id prop
and the Label htmlFor prop to preserve the Label→Checkbox association for
keyboard and screen-reader users (update the code where assigneeCheckboxId is
defined and where Checkbox and Label use it).

In `@packages/api/src/routers/issues.ts`:
- Around line 115-155: The create/update paths must validate that any submitted
assigneeIds belong to the issue's team (the same invariant enforced by
getUsersOnTeam) before writing to IssuesToUsersAssignment; implement a helper
like validateAssigneesBelongToTeam(teamId, assigneeIds) that queries Permissions
(or the same join used in getUsersOnTeam) to ensure all assigneeIds are members,
then call it from createIssue (use incoming input.team) and updateIssue (use
input.team ?? existingIssue.team) and reject the request with a clear error if
any assignee is not on the team rather than inserting invalid rows.
🪄 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: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 35048b49-01b7-4b2a-a374-4149bb0b421d

📥 Commits

Reviewing files that changed from the base of the PR and between 8382631 and fc1f951.

📒 Files selected for processing (2)
  • apps/blade/src/app/_components/issues/create-edit-dialog.tsx
  • packages/api/src/routers/issues.ts

Comment thread apps/blade/src/app/_components/issues/create-edit-dialog.tsx
Comment thread packages/api/src/routers/issues.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/utils/src/permissions.server.ts (1)

105-113: Batch nested assignee validation by team to reduce query fan-out.

validateIssueNodeAssignees currently performs one DB query per node. For deep/wide trees, this becomes many round-trips. Consider collecting assignees by team first, then validating once per team.

♻️ Suggested refactor
 export const validateIssueNodeAssignees = async (
   nodes: IssueAssigneeValidationNode[],
 ) => {
-  for (const node of nodes) {
-    await validateAssigneesBelongToTeam(node.team, node.assigneeIds);
-    if (node.children?.length) {
-      await validateIssueNodeAssignees(node.children);
-    }
-  }
+  const assigneesByTeam = new Map<string, Set<string>>();
+  const stack = [...nodes];
+
+  while (stack.length > 0) {
+    const node = stack.pop()!;
+    if (node.assigneeIds?.length) {
+      const set = assigneesByTeam.get(node.team) ?? new Set<string>();
+      for (const id of node.assigneeIds) set.add(id);
+      assigneesByTeam.set(node.team, set);
+    }
+    if (node.children?.length) stack.push(...node.children);
+  }
+
+  for (const [teamId, ids] of assigneesByTeam) {
+    await validateAssigneesBelongToTeam(teamId, [...ids]);
+  }
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/utils/src/permissions.server.ts` around lines 105 - 113,
validateIssueNodeAssignees currently calls validateAssigneesBelongToTeam per
node causing many DB round-trips; refactor it to first traverse the tree of
IssueAssigneeValidationNode (use validateIssueNodeAssignees as entry) and
collect assigneeIds grouped by team into a Map<team, Set<assigneeId>> (include
children recursively), then iterate that map and call
validateAssigneesBelongToTeam once per team with the combined array of
assigneeIds; keep the same async signature and handle empty sets by skipping
calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/api/src/routers/issues.ts`:
- Around line 167-174: The assignee validations
(permissionsServer.validateAssigneesBelongToTeam and
permissionsServer.validateIssueNodeAssignees) are invoked outside
db.transaction, creating a TOCTOU risk; move these validations into the same
db.transaction block used for the issue inserts/updates so they run under the
same transactional context, and if needed add tx-aware overloads to
permissions.server.ts (e.g., validateAssigneesBelongToTeam(tx, team,
assigneeIds) and validateIssueNodeAssignees(tx, children)) so the permission
reads use the transaction connection rather than a separate non-transactional
query.
- Around line 373-377: The update path can change the issue team without
revalidating current assignees, leaving invalid assignees attached; when calling
permissionsServer.validateAssigneesBelongToTeam, if input.assigneeIds is
undefined but input.team differs from existingIssue.team, pass the
existingIssue.assigneeIds (or explicitly clear them) into
validateAssigneesBelongToTeam so the current assignments are revalidated against
the new assignmentTeamId (assignmentTeamId, input.assigneeIds,
existingIssue.assigneeIds, input.team and existingIssue.team are the relevant
symbols to check and adjust).

---

Nitpick comments:
In `@packages/utils/src/permissions.server.ts`:
- Around line 105-113: validateIssueNodeAssignees currently calls
validateAssigneesBelongToTeam per node causing many DB round-trips; refactor it
to first traverse the tree of IssueAssigneeValidationNode (use
validateIssueNodeAssignees as entry) and collect assigneeIds grouped by team
into a Map<team, Set<assigneeId>> (include children recursively), then iterate
that map and call validateAssigneesBelongToTeam once per team with the combined
array of assigneeIds; keep the same async signature and handle empty sets by
skipping calls.
🪄 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: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: afc6395c-58c4-477c-8b0d-afdbd8309365

📥 Commits

Reviewing files that changed from the base of the PR and between 9bff5e5 and 3106a0d.

📒 Files selected for processing (2)
  • packages/api/src/routers/issues.ts
  • packages/utils/src/permissions.server.ts

Comment thread packages/api/src/routers/issues.ts Outdated
Comment thread packages/api/src/routers/issues.ts Outdated
Copy link
Copy Markdown
Contributor

@DVidal1205 DVidal1205 left a comment

Choose a reason for hiding this comment

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

lgtm ty

@DVidal1205 DVidal1205 merged commit 88c8a12 into main Apr 13, 2026
11 checks passed
@DVidal1205 DVidal1205 deleted the add-assignee-logic branch April 13, 2026 04:04
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