Skip to content

Blade/issue template#432

Merged
DVidal1205 merged 7 commits intomainfrom
blade/issue-template
Apr 3, 2026
Merged

Blade/issue template#432
DVidal1205 merged 7 commits intomainfrom
blade/issue-template

Conversation

@aidenletourneau
Copy link
Copy Markdown
Contributor

@aidenletourneau aidenletourneau commented Apr 1, 2026

Why

We need Issue templates to create multiple issue tasks easier to create

What

Template creation/edit modal

Addresses: #398

Test Plan

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

    • Added support for creating nested sub-issues within parent issues.
    • Introduced issue template import and management functionality with a dedicated templates dialog.
  • Improvements

    • Enhanced form validation to prevent invalid issue submissions.
    • Redesigned issue creation/editing dialog with improved layout and user experience.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 1, 2026

📝 Walkthrough

Walkthrough

This PR introduces a comprehensive issue and template creation system by refactoring the create-edit dialog to use Forge UI components and a hierarchical IssueEditNode data model with nested children support, alongside new template management capabilities, tree utilities, form field components, and backend API extensions for nested issue creation and template operations.

Changes

Cohort / File(s) Summary
Frontend Dialog Refactoring
apps/blade/src/app/_components/issues/create-edit-dialog.tsx
Replaced custom portal/overlay logic with Forge UI Dialog components. Refactored form state from flat IssueFormValues to tree-based IssueEditNode model with childIssues support. Integrated Zod schema validation (issueFormSchema) and consolidated pending state across mutations. Added template import via dropdown and sub-issues section with recursive node rendering. Submission now optionally creates nested issues; updates skip sub-issue building.
New Utility Modules
apps/blade/src/app/_components/issues/issue-dialog-utils.ts, issue-form-fields.tsx, issue-node.tsx
Created issue-dialog-utils.ts with tree helpers (updateTreeNode, removeTreeNode, addChildToTreeNode), date/time formatters, and normalization utilities. Added issue-form-fields.tsx with reusable form controls (StatusSelect, PrioritySelect, TeamSelect, TimePickerField, RoleCheckboxGroup, EventFormFields). Introduced issue-node.tsx with recursive tree components (IssueNode, IssueTemplateNode) and immutable node operations, plus validation predicates.
Template Management
apps/blade/src/app/_components/issues/issue-template-dialog.tsx
New dialog component providing templates list view with edit/delete actions and a form view for create/edit. Conditionally fetches templates and roles via tRPC. Form manages template metadata and hierarchical sub-tasks via IssueTemplateNode helpers. Submission routes to createTemplate or updateTemplate with success/error toast feedback and cache invalidation.
Backend API Extensions
packages/api/src/routers/event.ts, routers/issues.ts
Enhanced createEvent to return inserted row via .returning() and validate result before returning. Extended createIssue with optional recursive children support and helper createChildIssues(). Updated createTemplate/updateTemplate/updateIssue input schemas to use new issueTemplateSchema and support priority/parent fields. Refactored template sub-issue structure naming (TemplateSubIssueIssueTemplate).
Type System
packages/consts/src/issue.ts
Added IssueEditNode (client-side edit state with optional id, optional date as Date, roles, assigneeIds, recursive children), IssueSubmitNode (submission-ready with required date as string | Date), IssueTemplate (hierarchical template schema), and IssueTemplateEditNode (edit node with clientId, daysOffset, recursive children). Replaced IssueFormValues-based submit model with explicit IssueSubmitValues interface.

Sequence Diagram

sequenceDiagram
    actor User
    participant Dialog as Create/Edit Dialog
    participant Form as Form Validation<br/>(Zod + Tree)
    participant API as API Router<br/>(Issues/Events)
    participant DB as Database

    User->>Dialog: Fill form & add sub-issues
    User->>Dialog: Submit
    Dialog->>Form: Validate IssueEditNode tree
    alt Validation fails
        Form-->>Dialog: Invalid
        Dialog-->>User: Show error
    else Validation succeeds
        Dialog->>API: createIssue({ name, team, ..., children: [...] })
        API->>API: For each child: create event (if isEvent)
        API->>DB: Insert parent issue
        API->>DB: Insert child issues recursively
        API->>DB: Insert assignments & visibility
        DB-->>API: Success
        API-->>Dialog: Return issue + children
        Dialog->>Dialog: Invalidate cache
        Dialog-->>User: Show success toast
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

Blade, API, Feature, Major

Suggested reviewers

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

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title does not meet the required format. It must start with an issue number in brackets (e.g., '[#123]') followed by a concise description under 72 characters. Update the title to '[#398] Add issue templates for creating multiple issue tasks' or similar format that starts with the bracketed issue number.
Docstring Coverage ⚠️ Warning Docstring coverage is 17.02% 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 All modified and new files were scanned for hardcoded secrets (API keys, passwords, tokens, credentials). No hardcoded secrets were found.
Validated Env Access ✅ Passed All eight files explicitly modified in this PR comply with the validated env access pattern with no direct process.env usage.
No Typescript Escape Hatches ✅ Passed No TypeScript escape hatches found across all 8 modified files (3,953 lines). Zero instances of any, @ts-ignore, @ts-expect-error, or non-null assertions.

✏️ 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 blade/issue-template

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.

@aidenletourneau aidenletourneau self-assigned this Apr 1, 2026
@aidenletourneau aidenletourneau marked this pull request as ready for review April 1, 2026 18:25
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: 1

🧹 Nitpick comments (9)
apps/blade/src/app/_components/issues/issue-form-fields.tsx (1)

236-248: Consider adding accessible labels to checkboxes.

The Checkbox components lack explicit id and associated htmlFor on the Label. While clicking the label may work via parent click handling, explicit association improves accessibility.

💡 Optional: Add explicit label association
          <div
            key={`${keyPrefix}${role.id}`}
            className="flex flex-row items-start space-x-3 space-y-0"
          >
            <Checkbox
+             id={`${keyPrefix}checkbox-${role.id}`}
              checked={selectedIds.includes(role.id)}
              onCheckedChange={(checked) =>
                onChange(
                  checked
                    ? Array.from(new Set([...selectedIds, role.id]))
                    : selectedIds.filter((id) => id !== role.id),
                )
              }
            />
-           <Label className="cursor-pointer font-normal">{role.name}</Label>
+           <Label htmlFor={`${keyPrefix}checkbox-${role.id}`} className="cursor-pointer font-normal">{role.name}</Label>
          </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/blade/src/app/_components/issues/issue-form-fields.tsx` around lines 236
- 248, The Checkbox and Label lack an explicit association—add an id to the
Checkbox and set the Label's htmlFor to that same id so screen readers and forms
can associate them; locate the Checkbox and Label inside the issue row (where
Checkbox is using checked={selectedIds.includes(role.id)} and onCheckedChange
calling onChange with role.id, and Label renders role.name) and generate a
stable id (e.g., using role.id like `role-checkbox-${role.id}`) and apply it to
Checkbox and Label.htmlFor, ensuring you don’t break existing click handlers or
selectedIds logic.
apps/blade/src/app/_components/issues/create-edit-dialog.tsx (2)

423-428: Silent return when datetime parsing fails—consider user feedback.

If startDatetime or endDatetime is null, the function returns without creating the issue or showing an error. While validation should prevent this, a defensive toast would improve debuggability.

💡 Optional: Add error feedback
           if (!startDatetime || !endDatetime) return;
+            toast.error("Invalid event start or end time");
+            return;
           }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/blade/src/app/_components/issues/create-edit-dialog.tsx` around lines
423 - 428, The code silently returns if parseEventDateTime fails for
startDatetime or endDatetime inside the formValues.isEvent branch; instead,
detect when startDatetime or endDatetime is null and surface a user-facing error
(use the app's toast/snackbar helper) before returning so the user gets
feedback. Update the block that calls parseEventDateTime (referencing
parseEventDateTime, defaultEventForm, formValues.isEvent, startDatetime,
endDatetime) to show a descriptive error toast like "Invalid event start/end
datetime" (or a localized string) and then abort the create flow.

341-395: Sequential event creation may be slow for large trees.

buildIssueNodes creates events sequentially (for...of with await). For templates with many event nodes, this could be slow. Consider if parallel creation (Promise.all) is feasible, though you'd need to handle partial failures.

For now, this is acceptable—templates typically have limited depth/breadth.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/blade/src/app/_components/issues/create-edit-dialog.tsx` around lines
341 - 395, buildIssueNodes currently creates events sequentially using a
for...of loop and awaiting createEventMutation.mutateAsync for each node, which
can be slow for large trees; refactor buildIssueNodes to launch event creation
in parallel where safe by mapping event nodes to promises (using
createEventMutation.mutateAsync for nodes with node.isEvent && node.eventData)
and awaiting Promise.all for those creations, then continue assembling the
IssueSubmitNode results (ensure you correlate created event IDs back to their
respective nodes, handle/propagate individual mutation failures or collect
errors, and keep recursion for children by calling await
buildIssueNodes(node.children) as needed); refer to buildIssueNodes,
createEventMutation.mutateAsync, node.isEvent, node.eventData, and node.children
when implementing.
packages/api/src/routers/event.ts (1)

321-347: Good improvement: returning the created event enables downstream usage.

The .returning() pattern is correct. One small note: the error code at line 336 (BAD_REQUEST) might be more accurate as INTERNAL_SERVER_ERROR since this represents a database failure, not invalid input. However, this is a minor consistency point.

💡 Optional: Use INTERNAL_SERVER_ERROR for DB failures
         if (!createdEvent) {
           throw new TRPCError({
             message: "Failed to retrieve created event",
-            code: "BAD_REQUEST",
+            code: "INTERNAL_SERVER_ERROR",
           });
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/api/src/routers/event.ts` around lines 321 - 347, Change the error
code used when failing to retrieve the created DB row to reflect a
server/database failure: in the block that calls
db.insert(Event).values(...).returning() and checks if (!createdEvent), update
the TRPCError instantiation (currently using code: "BAD_REQUEST") to use code:
"INTERNAL_SERVER_ERROR" so the error better represents a DB/server-side failure
for the createdEvent path.
apps/blade/src/app/admin/issues/page.tsx (1)

23-32: Dev page renders issue dialog components.

The [DEV] prefix in the button text suggests this is a development/testing page. Consider adding a comment or removing from production builds if not intended for end users.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/blade/src/app/admin/issues/page.tsx` around lines 23 - 32, The page
currently renders a dev-only trigger button ("[DEV] Open Issue Dialog") inside
CreateEditDialog which exposes test UI in production; either remove that button
or guard it behind an environment/feature flag check (e.g., process.env.NODE_ENV
!== 'production' or a NEXT_PUBLIC_SHOW_DEV_UI flag) and add a brief inline
comment explaining it's dev-only; update the JSX around CreateEditDialog and the
button text (and/or reference IssueTemplate) accordingly so production builds do
not show the dev button.
apps/blade/src/app/_components/issues/issue-dialog-utils.ts (1)

98-115: Minor edge case: month value of 0 passes validation.

The check !month would fail if the parsed month is 0 (which could happen with malformed input like "2024-00-15"). While unlikely in practice (the date input type prevents this), a more explicit check could be month === undefined || month < 1 || month > 12.

This is a minor edge case since the HTML date input provides valid strings.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/blade/src/app/_components/issues/issue-dialog-utils.ts` around lines 98
- 115, The validation in parseEventDateTime currently treats falsy month values
as invalid but lets a parsed month of 0 through (or out-of-range values) slip
through; update the validation to explicitly check that year, month and day are
numbers and that month is between 1 and 12 (e.g., month === undefined || month <
1 || month > 12) and similarly validate day is within 1..31 (or better, rely on
parsed.getTime after range checks) and that hour/minute are not NaN before
constructing the Date; adjust the conditional that currently reads `if (!year ||
!month || !day || Number.isNaN(hour) || Number.isNaN(minute))` to use explicit
numeric-range checks for month (and day) so inputs like "2024-00-15" return
null.
apps/blade/src/app/_components/issues/issue-template-dialog.tsx (1)

273-281: Consider adding delete confirmation.

The delete button immediately calls deleteTemplate.mutate({ id: t.id }) without confirmation. A misclick could permanently delete a template. Consider adding a confirmation dialog or using a double-click pattern.

💡 Optional: Add confirmation before delete
  <Button
    variant="ghost"
    size="sm"
    className="text-destructive hover:text-destructive"
-   onClick={() => deleteTemplate.mutate({ id: t.id })}
+   onClick={() => {
+     if (confirm(`Delete template "${t.name}"?`)) {
+       deleteTemplate.mutate({ id: t.id });
+     }
+   }}
    disabled={deleteTemplate.isPending}
  >

Or implement a more polished confirmation dialog component.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/blade/src/app/_components/issues/issue-template-dialog.tsx` around lines
273 - 281, The delete button currently calls deleteTemplate.mutate({ id: t.id })
immediately; wrap that call in a user confirmation step (e.g., show a modal
confirmation or use window.confirm) inside the Button's onClick handler in
issue-template-dialog.tsx so a user must explicitly confirm before delete;
ensure you still respect deleteTemplate.isPending (keep the disabled state while
pending) and only call deleteTemplate.mutate when the user confirms, and surface
any error/success feedback from the mutation in the dialog flow.
apps/blade/src/app/_components/issues/issue-node.tsx (2)

436-442: Same accessibility improvement applies here.

Add aria-label="Sub-task name" for screen reader support, consistent with the suggestion for IssueNode.

♿ Suggested accessibility improvement
         <Input
           className="h-7 flex-1 border-0 bg-transparent px-0 py-0 text-sm shadow-none focus-visible:ring-0"
           placeholder="Sub-task name (required)"
+          aria-label="Sub-task name"
           value={node.name}
           onChange={(e) => update("name", e.target.value)}
           onClick={(e) => e.stopPropagation()}
         />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/blade/src/app/_components/issues/issue-node.tsx` around lines 436 - 442,
The Input in the IssueNode sub-task block doesn't provide an accessible name for
screen readers; add aria-label="Sub-task name" to the Input element (the same
place where value={node.name}, onChange={(e) => update("name", e.target.value)},
and onClick={(e) => e.stopPropagation()} are defined) so assistive tech can
announce the field; ensure the attribute is added to the <Input ... /> JSX for
the sub-task name input.

149-155: Consider adding an accessible label to the name input.

The name input has a placeholder, but screen readers work best with explicit labels. Adding an aria-label provides the missing accessible name without changing the visual layout.

♿ Suggested accessibility improvement
         <Input
           className="h-7 flex-1 border-0 bg-transparent px-0 py-0 text-sm shadow-none focus-visible:ring-0"
           placeholder="Sub-issue name (required)"
+          aria-label="Sub-issue name"
           value={node.name}
           onChange={(e) => update("name", e.target.value)}
           onClick={(e) => e.stopPropagation()}
         />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/blade/src/app/_components/issues/issue-node.tsx` around lines 149 - 155,
The name Input in issue-node.tsx lacks an accessible label—add an aria-label to
the Input (the same element that uses value={node.name} and onChange={(e) =>
update("name", e.target.value)}) to provide screen readers with a descriptive
name (e.g., aria-label="Sub-issue name") and optionally aria-required="true" if
the field is required; ensure the attribute is added to the Input props so
visual layout remains unchanged.
🤖 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/issue-node.tsx`:
- Around line 467-476: The number input's onChange handler can set daysOffset to
NaN; in the Input onChange for the daysOffset field, validate and sanitize
e.target.value before calling update: trim the string, treat empty string as
undefined, attempt to parse to a number (e.g., parseFloat/parseInt), and only
call update("daysOffset", parsedNumber) if the parsed result is a finite number;
otherwise call update("daysOffset", undefined) to avoid storing NaN. Keep
references to the Input element, the onChange handler, the update function, and
the daysOffset property when making the change.

---

Nitpick comments:
In `@apps/blade/src/app/_components/issues/create-edit-dialog.tsx`:
- Around line 423-428: The code silently returns if parseEventDateTime fails for
startDatetime or endDatetime inside the formValues.isEvent branch; instead,
detect when startDatetime or endDatetime is null and surface a user-facing error
(use the app's toast/snackbar helper) before returning so the user gets
feedback. Update the block that calls parseEventDateTime (referencing
parseEventDateTime, defaultEventForm, formValues.isEvent, startDatetime,
endDatetime) to show a descriptive error toast like "Invalid event start/end
datetime" (or a localized string) and then abort the create flow.
- Around line 341-395: buildIssueNodes currently creates events sequentially
using a for...of loop and awaiting createEventMutation.mutateAsync for each
node, which can be slow for large trees; refactor buildIssueNodes to launch
event creation in parallel where safe by mapping event nodes to promises (using
createEventMutation.mutateAsync for nodes with node.isEvent && node.eventData)
and awaiting Promise.all for those creations, then continue assembling the
IssueSubmitNode results (ensure you correlate created event IDs back to their
respective nodes, handle/propagate individual mutation failures or collect
errors, and keep recursion for children by calling await
buildIssueNodes(node.children) as needed); refer to buildIssueNodes,
createEventMutation.mutateAsync, node.isEvent, node.eventData, and node.children
when implementing.

In `@apps/blade/src/app/_components/issues/issue-dialog-utils.ts`:
- Around line 98-115: The validation in parseEventDateTime currently treats
falsy month values as invalid but lets a parsed month of 0 through (or
out-of-range values) slip through; update the validation to explicitly check
that year, month and day are numbers and that month is between 1 and 12 (e.g.,
month === undefined || month < 1 || month > 12) and similarly validate day is
within 1..31 (or better, rely on parsed.getTime after range checks) and that
hour/minute are not NaN before constructing the Date; adjust the conditional
that currently reads `if (!year || !month || !day || Number.isNaN(hour) ||
Number.isNaN(minute))` to use explicit numeric-range checks for month (and day)
so inputs like "2024-00-15" return null.

In `@apps/blade/src/app/_components/issues/issue-form-fields.tsx`:
- Around line 236-248: The Checkbox and Label lack an explicit association—add
an id to the Checkbox and set the Label's htmlFor to that same id so screen
readers and forms can associate them; locate the Checkbox and Label inside the
issue row (where Checkbox is using checked={selectedIds.includes(role.id)} and
onCheckedChange calling onChange with role.id, and Label renders role.name) and
generate a stable id (e.g., using role.id like `role-checkbox-${role.id}`) and
apply it to Checkbox and Label.htmlFor, ensuring you don’t break existing click
handlers or selectedIds logic.

In `@apps/blade/src/app/_components/issues/issue-node.tsx`:
- Around line 436-442: The Input in the IssueNode sub-task block doesn't provide
an accessible name for screen readers; add aria-label="Sub-task name" to the
Input element (the same place where value={node.name}, onChange={(e) =>
update("name", e.target.value)}, and onClick={(e) => e.stopPropagation()} are
defined) so assistive tech can announce the field; ensure the attribute is added
to the <Input ... /> JSX for the sub-task name input.
- Around line 149-155: The name Input in issue-node.tsx lacks an accessible
label—add an aria-label to the Input (the same element that uses
value={node.name} and onChange={(e) => update("name", e.target.value)}) to
provide screen readers with a descriptive name (e.g., aria-label="Sub-issue
name") and optionally aria-required="true" if the field is required; ensure the
attribute is added to the Input props so visual layout remains unchanged.

In `@apps/blade/src/app/_components/issues/issue-template-dialog.tsx`:
- Around line 273-281: The delete button currently calls deleteTemplate.mutate({
id: t.id }) immediately; wrap that call in a user confirmation step (e.g., show
a modal confirmation or use window.confirm) inside the Button's onClick handler
in issue-template-dialog.tsx so a user must explicitly confirm before delete;
ensure you still respect deleteTemplate.isPending (keep the disabled state while
pending) and only call deleteTemplate.mutate when the user confirms, and surface
any error/success feedback from the mutation in the dialog flow.

In `@apps/blade/src/app/admin/issues/page.tsx`:
- Around line 23-32: The page currently renders a dev-only trigger button
("[DEV] Open Issue Dialog") inside CreateEditDialog which exposes test UI in
production; either remove that button or guard it behind an environment/feature
flag check (e.g., process.env.NODE_ENV !== 'production' or a
NEXT_PUBLIC_SHOW_DEV_UI flag) and add a brief inline comment explaining it's
dev-only; update the JSX around CreateEditDialog and the button text (and/or
reference IssueTemplate) accordingly so production builds do not show the dev
button.

In `@packages/api/src/routers/event.ts`:
- Around line 321-347: Change the error code used when failing to retrieve the
created DB row to reflect a server/database failure: in the block that calls
db.insert(Event).values(...).returning() and checks if (!createdEvent), update
the TRPCError instantiation (currently using code: "BAD_REQUEST") to use code:
"INTERNAL_SERVER_ERROR" so the error better represents a DB/server-side failure
for the createdEvent path.
🪄 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: f851da03-035b-44d7-b131-b2ad01942a9b

📥 Commits

Reviewing files that changed from the base of the PR and between 2a698f4 and 86179b2.

📒 Files selected for processing (9)
  • apps/blade/src/app/_components/issues/create-edit-dialog.tsx
  • apps/blade/src/app/_components/issues/issue-dialog-utils.ts
  • apps/blade/src/app/_components/issues/issue-form-fields.tsx
  • apps/blade/src/app/_components/issues/issue-node.tsx
  • apps/blade/src/app/_components/issues/issue-template-dialog.tsx
  • apps/blade/src/app/admin/issues/page.tsx
  • packages/api/src/routers/event.ts
  • packages/api/src/routers/issues.ts
  • packages/consts/src/issue.ts

Comment thread apps/blade/src/app/_components/issues/issue-node.tsx
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.

🧹 Nitpick comments (2)
apps/blade/src/app/_components/issues/issue-node.tsx (2)

149-155: Consider adding an accessible label for the name input.

The input relies on placeholder for context, but screen readers don't consistently announce placeholder text as a label. For better accessibility, consider adding a visually hidden label or aria-label.

♿ Suggested fix
         <Input
           className="h-7 flex-1 border-0 bg-transparent px-0 py-0 text-sm shadow-none focus-visible:ring-0"
           placeholder="Sub-issue name (required)"
+          aria-label="Sub-issue name"
           value={node.name}
           onChange={(e) => update("name", e.target.value)}
           onClick={(e) => e.stopPropagation()}
         />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/blade/src/app/_components/issues/issue-node.tsx` around lines 149 - 155,
The name input uses only a placeholder which is not reliably announced by screen
readers; add an accessible label by providing either an aria-label (e.g.,
aria-label="Sub-issue name") or rendering a visually hidden <label> tied to the
Input's id, and keep the existing props (value={node.name}, onChange={e =>
update("name", e.target.value)}, onClick={e => e.stopPropagation()}) untouched;
update the Input usage in issue-node.tsx (the Input component instance) to
include the chosen accessibility attribute so screen readers receive a proper
label.

436-442: Same accessibility improvement applies here.

Like the IssueNode input, consider adding aria-label="Sub-task name" for screen reader users.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/blade/src/app/_components/issues/issue-node.tsx` around lines 436 - 442,
The Input for sub-tasks lacks an accessible label for screen readers; add
aria-label="Sub-task name" to the Input element used in the IssueNode sub-task
UI (the same Input that uses value={node.name}, onChange={(e) => update("name",
e.target.value)} and onClick={(e) => e.stopPropagation()}) so assistive
technologies announce the field purpose.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/blade/src/app/_components/issues/issue-node.tsx`:
- Around line 149-155: The name input uses only a placeholder which is not
reliably announced by screen readers; add an accessible label by providing
either an aria-label (e.g., aria-label="Sub-issue name") or rendering a visually
hidden <label> tied to the Input's id, and keep the existing props
(value={node.name}, onChange={e => update("name", e.target.value)}, onClick={e
=> e.stopPropagation()}) untouched; update the Input usage in issue-node.tsx
(the Input component instance) to include the chosen accessibility attribute so
screen readers receive a proper label.
- Around line 436-442: The Input for sub-tasks lacks an accessible label for
screen readers; add aria-label="Sub-task name" to the Input element used in the
IssueNode sub-task UI (the same Input that uses value={node.name}, onChange={(e)
=> update("name", e.target.value)} and onClick={(e) => e.stopPropagation()}) so
assistive technologies announce the field purpose.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 62a05ccd-aa44-41f6-8d18-f56e6acdceca

📥 Commits

Reviewing files that changed from the base of the PR and between 86179b2 and 296ad1d.

📒 Files selected for processing (2)
  • apps/blade/src/app/_components/issues/issue-node.tsx
  • apps/blade/src/app/_components/issues/issue-template-dialog.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/blade/src/app/_components/issues/issue-template-dialog.tsx

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

@DVidal1205 DVidal1205 added this pull request to the merge queue Apr 3, 2026
Merged via the queue into main with commit f07d824 Apr 3, 2026
7 checks passed
@DVidal1205 DVidal1205 deleted the blade/issue-template branch April 3, 2026 15:27
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