Skip to content

fix(admin): add skeleton loader to invite-user Org select#1735

Open
Shreyag02 wants to merge 1 commit into
mainfrom
fix/invite-user-org-select-missing-skeleton
Open

fix(admin): add skeleton loader to invite-user Org select#1735
Shreyag02 wants to merge 1 commit into
mainfrom
fix/invite-user-org-select-missing-skeleton

Conversation

@Shreyag02

Copy link
Copy Markdown
Contributor

Summary

Fixes the Organization select in the Users-list, Invite user dialog was missing a skeleton loader while its data loads. This adds the skeleton (driven by the real query loading state) and bundles a set of consistency cleanups across both admin invite dialogs.

Changes

  • Add a skeleton loader to the Org select (and Role/Email fields) in the Users-list invite dialog, gated on the real query loading state instead of a hardcoded flag.
  • Migrate the invite dialogs' form groups to the apsara Field composite (Field label/error), replacing the custom Label + error Text markup.
  • Disable the submit button until the required fields are present and there is no email validation error.
  • Remove now-unused CSS classes (invite-users-dialog-label, form-error-message) from invite-users.module.css.
  • Apply the same Field migration + submit-gating to the org-Members "Invite user" dialog for consistency.

Technical Details

  • Replaced a hardcoded const isLoading = true (which froze the dialog on skeletons) with isLoading = isRolesLoading || isOrganizationsLoading.
  • Field handles label↔control association and error rendering (aria-invalid), so the custom label/error CSS is no longer needed.
  • Submit gating uses watch(...) + useMemo (isDisabled): disabled on an empty email list, missing role/org, while submitting, or when errors.emails is set.
  • The Members dialog intentionally gets no skeleton — its roles come synchronously from OrganizationContext (there's no loading state to gate on). Its shared invite-users-dialog-label / form-error-message CSS classes are retained because add-tokens-dialog.tsx still uses them.
  • The SDK ships compiled bundles; admin/dist was rebuilt (pnpm build) so the change reaches consumers.

Test Plan

  • Manual testing completed
  • Build and type checking passes

SQL Safety (if your PR touches *_repository.go or goqu.*)

N/A

@Shreyag02 Shreyag02 requested a review from rohanchkrabrty July 3, 2026 10:19
@vercel

vercel Bot commented Jul 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview, Comment Jul 3, 2026 10:19am

@Shreyag02 Shreyag02 requested a review from rohilsurana July 3, 2026 10:19
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved invite-user forms with clearer validation feedback and more reliable submit disabling.
    • The invite button now stays disabled until required fields are filled, valid email addresses are entered, and loading/submitting has finished.
  • UI Improvements
    • Updated the invite dialogs to use a more consistent field layout.
    • Loading states now show built-in skeleton placeholders for a smoother experience.

Walkthrough

Two invite-user form components (organization details dialog and users list) were refactored to use Field components from apsara for emails/role/organization inputs, replacing manual label/error layouts. Both now watch form values to compute isDisabled for submit gating, and the users list form adds Skeleton loading states, replacing react-loading-skeleton. Unused CSS rules were removed.

Changes

Invite Users Form UX Refactor

Layer / File(s) Summary
Organization invite dialog: Field wiring and submit gating
web/sdk/admin/views/organizations/details/layout/invite-users-dialog.tsx
Imports Field, watches emails/role, computes isDisabled from parsed emails, missing role, submission state, and errors, and wires Field error props plus disables the Invite button accordingly.
Users list invite form: Skeleton loading and Field wiring
web/sdk/admin/views/users/list/invite-users.tsx, web/sdk/admin/views/users/list/invite-users.module.css
Switches to apsara Skeleton, derives isLoading from roles/organizations loading, computes isDisabled from watched emails/role/organizationId, restructures emails/role/organization inputs into Field wrappers with conditional skeletons, and removes now-unused CSS label/error rules.

Estimated code review effort: 2 (Simple) | ~15 minutes

Possibly related PRs

  • raystack/frontier#1435: Both touch the emails textarea rendering in invite-users.tsx, one via a resize className, the other via Field/Controller restructuring.
  • raystack/frontier#1452: Both touch the emails textarea in invite-users-dialog.tsx, one via CSS resize constraint, the other via Field/Controller refactor with validation wiring.
  • raystack/frontier#1712: Both modify the organization dropdown in invite-users.tsx, one adding Field/skeleton/error handling, the other adding search/autocomplete display.

Suggested reviewers: rsbh, rohanchkrabrty, paanSinghCoder

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
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.

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
Contributor

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 (1)
web/sdk/admin/views/organizations/details/layout/invite-users-dialog.tsx (1)

111-129: 📐 Maintainability & Code Quality | 🔵 Trivial

Duplicate email-parsing / disable logic across both invite dialogs.

This isDisabled computation (parse emails, check role, submitting, errors) is duplicated almost verbatim in web/sdk/admin/views/users/list/invite-users.tsx. Consider extracting a shared hook (e.g. useInviteFormDisabled(values, isSubmitting, errors)) to avoid divergence when validation rules change.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1b195017-2b13-4e58-a43b-dbe15e9b8041

📥 Commits

Reviewing files that changed from the base of the PR and between 854c122 and 99ce08a.

📒 Files selected for processing (3)
  • web/sdk/admin/views/organizations/details/layout/invite-users-dialog.tsx
  • web/sdk/admin/views/users/list/invite-users.module.css
  • web/sdk/admin/views/users/list/invite-users.tsx
💤 Files with no reviewable changes (1)
  • web/sdk/admin/views/users/list/invite-users.module.css

Comment on lines +174 to +196
{isLoading ? (
<Skeleton height="80px" />
) : (
<Field
label="Emails"
error={errors?.emails?.message || errors?.emails?.[0]?.message}>
<Controller
name="emails"
control={control}
render={({ field }) => {
const { value, ...rest } = field;
return (
<TextArea
{...rest}
value={Array.isArray(value) ? value.join(", ") : (value ?? "") as string}
placeholder="abc@example.com, xyz@example.com"
className={styles["invite-users-emails-textarea"]}
/>
);
}}
/>
</Field>
)}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Emails field unnecessarily gated behind the loading skeleton, and inconsistently loses its label.

The Emails textarea has no dependency on the roles/organizations queries, yet it's fully hidden (including its "Emails" label) behind isLoading, unlike the Role/Organization fields where the Field wrapper (and its label) always renders and only the inner control is skeleton-swapped. This:

  • Unnecessarily blocks users from typing emails while unrelated org/role data loads.
  • Causes a jarring "label pop-in" once loading completes, inconsistent with the other two fields.
  • Diverges from the PR's stated intent of adding the skeleton loader "to the Organization select" only.

Consider always rendering the Field (with its label), and only swapping the inner TextArea/Controller for a Skeleton, matching the Role/Organization pattern.

💡 Suggested fix
-              {isLoading ? (
-                <Skeleton height="80px" />
-              ) : (
-                <Field
-                  label="Emails"
-                  error={errors?.emails?.message || errors?.emails?.[0]?.message}>
-                  <Controller
-                    name="emails"
-                    control={control}
-                    render={({ field }) => {
-                      const { value, ...rest } = field;
-                      return (
-                        <TextArea
-                          {...rest}
-                          value={Array.isArray(value) ? value.join(", ") : (value ?? "") as string}
-                          placeholder="abc@example.com, xyz@example.com"
-                          className={styles["invite-users-emails-textarea"]}
-                        />
-                      );
-                    }}
-                  />
-                </Field>
-              )}
+              <Field
+                label="Emails"
+                error={errors?.emails?.message || errors?.emails?.[0]?.message}>
+                {isLoading ? (
+                  <Skeleton height="80px" />
+                ) : (
+                  <Controller
+                    name="emails"
+                    control={control}
+                    render={({ field }) => {
+                      const { value, ...rest } = field;
+                      return (
+                        <TextArea
+                          {...rest}
+                          value={Array.isArray(value) ? value.join(", ") : (value ?? "") as string}
+                          placeholder="abc@example.com, xyz@example.com"
+                          className={styles["invite-users-emails-textarea"]}
+                        />
+                      );
+                    }}
+                  />
+                )}
+              </Field>
📝 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
{isLoading ? (
<Skeleton height="80px" />
) : (
<Field
label="Emails"
error={errors?.emails?.message || errors?.emails?.[0]?.message}>
<Controller
name="emails"
control={control}
render={({ field }) => {
const { value, ...rest } = field;
return (
<TextArea
{...rest}
value={Array.isArray(value) ? value.join(", ") : (value ?? "") as string}
placeholder="abc@example.com, xyz@example.com"
className={styles["invite-users-emails-textarea"]}
/>
);
}}
/>
</Field>
)}
<Field
label="Emails"
error={errors?.emails?.message || errors?.emails?.[0]?.message}>
{isLoading ? (
<Skeleton height="80px" />
) : (
<Controller
name="emails"
control={control}
render={({ field }) => {
const { value, ...rest } = field;
return (
<TextArea
{...rest}
value={Array.isArray(value) ? value.join(", ") : (value ?? "") as string}
placeholder="abc@example.com, xyz@example.com"
className={styles["invite-users-emails-textarea"]}
/>
);
}}
/>
)}
</Field>

@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 28654224331

Coverage remained the same at 44.865%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 37608
Covered Lines: 16873
Line Coverage: 44.87%
Coverage Strength: 12.49 hits per line

💛 - Coveralls

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