Skip to content

Abc/admin UI cleanup#13

Open
aristachauhan wants to merge 2 commits into
mainfrom
abc/admin-ui-cleanup
Open

Abc/admin UI cleanup#13
aristachauhan wants to merge 2 commits into
mainfrom
abc/admin-ui-cleanup

Conversation

@aristachauhan

@aristachauhan aristachauhan commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added a consolidated admin status dashboard with metrics, data freshness, issues, request breakdowns, and vacation information.
    • Added reusable department settings, user management, and inline department-name editing experiences.
    • Added clearer validation, helper text, loading states, error messages, and configurable form controls.
    • Added visual metric cards and type breakdowns for easier data interpretation.
  • Refactor

    • Standardized administration screens around shared components for a more consistent interface and behavior.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Admin route-local UI is extracted into shared components for departments, users, and status views. New admin modals, metrics, type breakdowns, department controls, and reusable form fields provide shared validation, styling, and submission behavior.

Changes

Admin form foundations

Layer / File(s) Summary
Shared form APIs and controls
client/src/shared/forms/*, client/src/shared/admin/adminData.tsx
Form fields now support helper text, customizable classes, input types, checkbox fields, and configurable submit buttons. User create/update input types are exported and composed from shared editable fields.

Reusable admin workflows

Layer / File(s) Summary
User and department editing components
client/src/shared/admin/AdminModalFrame.tsx, client/src/shared/admin/AdminUserModal.tsx, client/src/shared/admin/Department*.tsx, client/src/shared/admin/InlineTextEditor.tsx
Shared modal, user form, department row/settings, and inline editing components implement validation, submission, error handling, routing-email management, and department renaming.

Status dashboard

Layer / File(s) Summary
Status presentation components
client/src/shared/admin/AdminMetricCard.tsx, client/src/shared/admin/AdminTypeBreakdown.tsx, client/src/shared/admin/AdminStatusContent.tsx
The status dashboard renders summary metrics, freshness data, issues, user and department metrics, leave-request breakdowns, and vacation-cap information through reusable presentation helpers.

Route integration

Layer / File(s) Summary
Admin route delegation
client/src/routes/(authenticated)/admin.departments.tsx, client/src/routes/(authenticated)/admin.status.tsx, client/src/routes/(authenticated)/admin.users.tsx
Admin routes now delegate UI to shared components while passing route data, mutation callbacks, modal values, labels, and formatted mutation errors.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is related to the changes, but it is too broad and generic to clearly describe the main update. Use a more specific title that names the main change, such as the shared admin UI component refactor.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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.
✨ 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 abc/admin-ui-cleanup

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.

@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: 6

🧹 Nitpick comments (1)
client/src/shared/admin/AdminStatusContent.tsx (1)

265-275: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Sync status type with upstream data source.

Hardcoding the string literal union duplicates the type definition and creates a brittle boundary. Since status is passed directly from an AdminDataSource object, use indexed access typing to ensure FreshnessRow stays synchronized if the upstream ImportStatus expands or changes.

♻️ Proposed refactor
 function FreshnessRow({
   detail,
   label,
   status,
   updatedAt,
 }: {
   detail: string;
   label: string;
-  status: 'ready' | 'planned' | 'deferred';
+  status: AdminDataSource['status'];
   updatedAt: string | null;
 }) {
🤖 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 `@client/src/shared/admin/AdminStatusContent.tsx` around lines 265 - 275,
Update the status prop type in FreshnessRow to use indexed-access typing from
the AdminDataSource object’s status field instead of a hardcoded string union.
Reuse the existing upstream data-source type so changes to ImportStatus
automatically propagate while leaving the component behavior unchanged.
🤖 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 `@client/src/routes/`(authenticated)/admin.users.tsx:
- Around line 233-241: Update both AdminUserModal submission handlers to invoke
their respective onClose callbacks only after the awaited create or update
operation succeeds. Keep the existing error handling intact and ensure both user
dialogs close after successful submission to prevent repeated submissions.

In `@client/src/shared/admin/AdminModalFrame.tsx`:
- Around line 15-20: Update the modal container in AdminModalFrame to use an
accessible DaisyUI modal or native dialog primitive with an associated title,
replacing the fixed div-only structure. Implement focus trapping within the
modal, close it on Escape, and restore focus to the triggering element when
dismissed while preserving the existing sizing and styling.

In `@client/src/shared/admin/DepartmentSettingsModal.tsx`:
- Around line 54-56: Update the removal state and handlers in
DepartmentSettingsModal to prevent concurrent routing-email removals: either
disable every removal button whenever pendingRemovalEmailId is set, or track all
active removal IDs in a Set. Ensure each request remains represented until its
own completion and cannot overwrite or prematurely clear another removal’s
state.
- Around line 171-180: Update the routing-email UI in DepartmentSettingsModal,
including the mapped entries and the add/edit controls around the affected
sections, to expose each email’s kind with a TO/CC selector and render the
stored kind for existing entries. Bind the selector to the routing email’s kind
value and preserve it when creating or updating entries so CC addresses are not
forced to the default “to”.
- Around line 119-123: Restructure the routing-email controls near
settingsForm.handleSubmit into a sibling form separate from the outer department
settings form, with its own submit handler for adding the email. Change the “Add
email” control to a submit button, while keeping the address field in the
routing-email form so pressing Enter adds the email instead of submitting
department settings.

In `@client/src/shared/admin/InlineTextEditor.tsx`:
- Around line 54-68: Add an onKeyDown handler to the input in InlineTextEditor
that detects the Enter key and blurs the input, allowing the existing onBlur
logic to submit the trimmed value. Preserve the current change, blur,
validation, and submission behavior for other keys.

---

Nitpick comments:
In `@client/src/shared/admin/AdminStatusContent.tsx`:
- Around line 265-275: Update the status prop type in FreshnessRow to use
indexed-access typing from the AdminDataSource object’s status field instead of
a hardcoded string union. Reuse the existing upstream data-source type so
changes to ImportStatus automatically propagate while leaving the component
behavior unchanged.
🪄 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: 58a4cd9a-d308-42b3-acd1-1137a1592a78

📥 Commits

Reviewing files that changed from the base of the PR and between 9c8968b and ba3b93f.

📒 Files selected for processing (18)
  • client/src/routes/(authenticated)/admin.departments.tsx
  • client/src/routes/(authenticated)/admin.status.tsx
  • client/src/routes/(authenticated)/admin.users.tsx
  • client/src/shared/admin/AdminMetricCard.tsx
  • client/src/shared/admin/AdminModalFrame.tsx
  • client/src/shared/admin/AdminStatusContent.tsx
  • client/src/shared/admin/AdminTypeBreakdown.tsx
  • client/src/shared/admin/AdminUserModal.tsx
  • client/src/shared/admin/DepartmentRow.tsx
  • client/src/shared/admin/DepartmentSettingsModal.tsx
  • client/src/shared/admin/InlineTextEditor.tsx
  • client/src/shared/admin/adminData.tsx
  • client/src/shared/forms/checkboxField.tsx
  • client/src/shared/forms/fieldWrapper.tsx
  • client/src/shared/forms/formContext.tsx
  • client/src/shared/forms/selectField.tsx
  • client/src/shared/forms/submitButton.tsx
  • client/src/shared/forms/textField.tsx

Comment on lines +233 to +241
onSubmit={async (value) => {
await updateUser(editingUser.id, {
active: value.active,
email: value.email,
employeeId: value.employeeId,
iamId: value.iamId,
name: value.name,
});
}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Close both dialogs after successful submission.

AdminUserModal only reports errors; it does not invoke onClose after success. These handlers leave both dialogs open, allowing repeated creation submissions.

Proposed fix
           onSubmit={async (value) => {
             await updateUser(editingUser.id, {
               active: value.active,
               email: value.email,
               employeeId: value.employeeId,
               iamId: value.iamId,
               name: value.name,
             });
+            setEditingUserId(null);
           }}
...
           onSubmit={async (value) => {
             await createUser({
               email: value.email,
               employeeId: value.employeeId,
               iamId: value.iamId,
               name: value.name,
             });
+            setShowCreateModal(false);
           }}

Also applies to: 259-266

🤖 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 `@client/src/routes/`(authenticated)/admin.users.tsx around lines 233 - 241,
Update both AdminUserModal submission handlers to invoke their respective
onClose callbacks only after the awaited create or update operation succeeds.
Keep the existing error handling intact and ensure both user dialogs close after
successful submission to prevent repeated submissions.

Comment on lines +15 to +20
<div className="fixed inset-0 z-50 flex items-center justify-center bg-slate-950/40 px-4 py-8">
<div
className={`max-h-[90vh] w-full overflow-y-auto rounded-[1.5rem] border border-[var(--admin-border)] bg-white p-6 shadow-2xl ${
maxWidthClassName ?? 'max-w-2xl'
}`}
>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Use an accessible dialog primitive and manage focus.

The fixed div has no dialog semantics or focus lifecycle, allowing keyboard focus behind the overlay and omitting Escape/focus restoration. Use a native <dialog> or accessible DaisyUI modal with an associated title.

As per coding guidelines, “Leverage DaisyUI components when appropriate.”

🤖 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 `@client/src/shared/admin/AdminModalFrame.tsx` around lines 15 - 20, Update the
modal container in AdminModalFrame to use an accessible DaisyUI modal or native
dialog primitive with an associated title, replacing the fixed div-only
structure. Implement focus trapping within the modal, close it on Escape, and
restore focus to the triggering element when dismissed while preserving the
existing sizing and styling.

Source: Coding guidelines

Comment on lines +54 to +56
const [pendingRemovalEmailId, setPendingRemovalEmailId] = useState<
string | null
>(null);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Prevent overlapping routing-email removals.

Only the selected row is disabled. Starting another removal overwrites pendingRemovalEmailId, and the first completion can clear the state while the second request remains active. Disable all removal buttons while pending or track pending IDs in a Set.

Also applies to: 100-110, 183-199

🤖 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 `@client/src/shared/admin/DepartmentSettingsModal.tsx` around lines 54 - 56,
Update the removal state and handlers in DepartmentSettingsModal to prevent
concurrent routing-email removals: either disable every removal button whenever
pendingRemovalEmailId is set, or track all active removal IDs in a Set. Ensure
each request remains represented until its own completion and cannot overwrite
or prematurely clear another removal’s state.

Comment on lines +119 to +123
<form
onSubmit={(event) => {
event.preventDefault();
void settingsForm.handleSubmit();
}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Give routing-email entry its own form submission boundary.

The address field belongs to the outer settings form while its button is type="button". Pressing Enter therefore saves department settings instead of adding the email. Split these into sibling forms and make “Add email” a submit button.

Also applies to: 210-232

🤖 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 `@client/src/shared/admin/DepartmentSettingsModal.tsx` around lines 119 - 123,
Restructure the routing-email controls near settingsForm.handleSubmit into a
sibling form separate from the outer department settings form, with its own
submit handler for adding the email. Change the “Add email” control to a submit
button, while keeping the address field in the routing-email form so pressing
Enter adds the email instead of submitting department settings.

Comment on lines +171 to +180
<div className="mt-4 space-y-3">
{department.routingEmails.map((email) => (
<div
className="flex flex-col gap-3 rounded-xl border border-[var(--admin-border)] bg-white px-4 py-3 sm:flex-row sm:items-center"
key={email.id}
>
<span className="badge border-0 bg-[var(--admin-sand)] text-[var(--admin-blue)]">
EMAIL
</span>
<span className="flex-1 text-sm text-[var(--admin-ink)]">

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Expose and display the routing-email kind.

kind is never rendered, so every new address uses the hidden 'to' default and existing CC entries are indistinguishable. Add a TO/CC select and display each entry’s actual kind.

Also applies to: 210-232

🤖 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 `@client/src/shared/admin/DepartmentSettingsModal.tsx` around lines 171 - 180,
Update the routing-email UI in DepartmentSettingsModal, including the mapped
entries and the add/edit controls around the affected sections, to expose each
email’s kind with a TO/CC selector and render the stored kind for existing
entries. Bind the selector to the routing email’s kind value and preserve it
when creating or updating entries so CC addresses are not forced to the default
“to”.

Comment on lines +54 to +68
<input
className={`${inputClassName} ${
hasError ? 'border-rose-400 focus:border-rose-500' : ''
}`}
disabled={field.form.state.isSubmitting}
onBlur={(event) => {
field.handleBlur();

if (event.target.value.trim()) {
void form.handleSubmit();
}
}}
onChange={(event) => field.handleChange(event.target.value)}
value={field.state.value}
/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Support pressing Enter to save the inline edit.

Users typically expect to press the Enter key to save changes in an inline text editor. Since this input is not wrapped in a semantic <form> element, pressing Enter currently does nothing.

You can capture the Enter keydown event and force a blur, which will seamlessly reuse your existing onBlur submission logic.

⌨️ Proposed fix to handle the Enter key
               <input
                 className={`${inputClassName} ${
                   hasError ? 'border-rose-400 focus:border-rose-500' : ''
                 }`}
                 disabled={field.form.state.isSubmitting}
                 onBlur={(event) => {
                   field.handleBlur();
 
                   if (event.target.value.trim()) {
                     void form.handleSubmit();
                   }
                 }}
                 onChange={(event) => field.handleChange(event.target.value)}
+                onKeyDown={(event) => {
+                  if (event.key === 'Enter') {
+                    event.preventDefault();
+                    event.currentTarget.blur();
+                  }
+                }}
                 value={field.state.value}
               />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<input
className={`${inputClassName} ${
hasError ? 'border-rose-400 focus:border-rose-500' : ''
}`}
disabled={field.form.state.isSubmitting}
onBlur={(event) => {
field.handleBlur();
if (event.target.value.trim()) {
void form.handleSubmit();
}
}}
onChange={(event) => field.handleChange(event.target.value)}
value={field.state.value}
/>
<input
className={`${inputClassName} ${
hasError ? 'border-rose-400 focus:border-rose-500' : ''
}`}
disabled={field.form.state.isSubmitting}
onBlur={(event) => {
field.handleBlur();
if (event.target.value.trim()) {
void form.handleSubmit();
}
}}
onChange={(event) => field.handleChange(event.target.value)}
onKeyDown={(event) => {
if (event.key === 'Enter') {
event.preventDefault();
event.currentTarget.blur();
}
}}
value={field.state.value}
/>
🤖 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 `@client/src/shared/admin/InlineTextEditor.tsx` around lines 54 - 68, Add an
onKeyDown handler to the input in InlineTextEditor that detects the Enter key
and blurs the input, allowing the existing onBlur logic to submit the trimmed
value. Preserve the current change, blur, validation, and submission behavior
for other keys.

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