Skip to content

fix(ui): refine UI components, teams management, and settings dialogs#1038

Merged
Israeltheminer merged 5 commits into
mainfrom
fix/ui-refinements-teams-settings-components
Apr 7, 2026
Merged

fix(ui): refine UI components, teams management, and settings dialogs#1038
Israeltheminer merged 5 commits into
mainfrom
fix/ui-refinements-teams-settings-components

Conversation

@Israeltheminer
Copy link
Copy Markdown
Collaborator

@Israeltheminer Israeltheminer commented Apr 7, 2026

Summary

  • Refine table, dialog, sheet, button, and input component styles for consistency
  • Replace separate team-members-dialog with inline member checklist in create/edit team flows
  • Improve API keys dialog layout and simplify table config hooks
  • Update chat input styling, conversation components, and globals.css
  • Add/update i18n strings for new team management and settings UX

Test plan

  • Verify team create/edit dialogs show member checklist inline
  • Verify API key create dialog displays key with label
  • Verify table header, row hover, and skeleton styles are consistent
  • Verify dialog, sheet, and button styling across settings pages
  • Verify chat input and conversation list render correctly

Summary by CodeRabbit

Release Notes

New Features

  • Team member selection during team creation and editing
  • Team member count and creation timestamps in team list
  • Enhanced API keys table with key display and search functionality
  • Contextual placeholder text in chat input

Style & Improvements

  • Updated color scheme for visual consistency
  • Responsive spacing adjustments across settings panels and dialogs
  • Increased input field and button heights for better usability
  • Enhanced focus states for improved accessibility
  • Unified background styling for clearer visual hierarchy

- Refine table, dialog, sheet, button, and input component styles
- Replace team-members-dialog with inline member checklist in create/edit flows
- Improve API keys dialog layout and simplify table config
- Update chat input styling and conversation components
- Clean up globals.css and update i18n strings
Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 7, 2026

📝 Walkthrough

Walkthrough

This PR consolidates styling updates across the platform UI by replacing bg-secondary/20 backgrounds with bg-muted throughout multiple components, adds responsive padding updates (replacing fixed px-6 py-4 with p-4 sm:px-6 sm:py-4), increases component heights for buttons, inputs, and table headers, refactors the teams management feature by removing the dedicated TeamMembersDialog component and integrating member selection into team creation/edit dialogs via a new TeamMemberChecklist component, restructures API keys table configuration and related dialogs, updates theme variables in globals.css, and includes comprehensive internationalization updates for teams, API keys, and chat features.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes across the PR: UI refinements for components, team management improvements, and settings dialog updates.

✏️ 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 fix/ui-refinements-teams-settings-components

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: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
services/platform/app/features/settings/teams/components/team-delete-dialog.tsx (1)

62-71: ⚠️ Potential issue | 🟠 Major

Restore the agent-binding check in the delete dialog.

The translation key teamHasAgentsWarning exists in services/platform/messages/en.json (line 641) with the message: "This team has agents assigned to it. Reassign or remove agents from this team before deleting." This strongly indicates a previous guard was in place and should be restored.

The DeleteDialog component fully supports the warning prop and disableDelete flag, and the pattern is already in use elsewhere (e.g., DeleteMemberDialog). The data model shows agentBindings indexed by teamId still exist in the schema, so the guard remains relevant.

Add a check for team-bound agents before rendering the delete button. Use the warning message from translations to inform the user they must reassign or remove agents first.

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

In
`@services/platform/app/features/settings/teams/components/team-delete-dialog.tsx`
around lines 62 - 71, Restore the guard that prevents deleting teams which have
agents: compute a boolean (e.g., hasAgents) from the team/agent-bindings data
(for example check team.agentBindings?.length > 0 or
agentBindingsByTeamId[team.id]?.length > 0) and pass it into the DeleteDialog
via disableDelete={hasAgents} and
warning={tSettings('teams.teamHasAgentsWarning')}; keep existing props (open,
onOpenChange, title, description, isDeleting, onDelete) and only add the warning
and disableDelete props so users see the translation message and cannot delete a
team with agents still assigned.
services/platform/app/features/conversations/components/conversations-list.tsx (1)

261-264: ⚠️ Potential issue | 🟡 Minor

Hardcoded fallback text should be internationalized.

The 'Unknown' fallback text on line 263 should use a translation key for consistency with the coding guidelines.

Proposed fix
                 <Heading
                   level={3}
                   size="sm"
                   weight="semibold"
                   tracking="tight"
                   truncate
                   className="flex-1"
                 >
                   {conversation.customer?.name ||
                     conversation?.title ||
-                    'Unknown'}
+                    t('customer.unknown')}
                 </Heading>

As per coding guidelines: "Do not hardcode text in UI components. Use translation hooks/functions instead for user-facing UI."

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

In
`@services/platform/app/features/conversations/components/conversations-list.tsx`
around lines 261 - 264, The hardcoded fallback 'Unknown' in the
ConversationsList heading should be replaced with a translated string; update
the rendering that currently uses conversation.customer?.name ||
conversation?.title || 'Unknown' to call your i18n translation (e.g.,
t('unknown') or useTranslation().t('conversations.unknown')) so the fallback
uses a translation key, and ensure the component (ConversationsList) imports and
invokes the translation hook/provider before use.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@services/platform/app/components/ui/data-display/table.tsx`:
- Line 86: Replace the invalid Tailwind classes in the table cell class string:
change `rounded-rt-xl` to `rounded-tr-xl` and `rounded-lt-xl` to `rounded-tl-xl`
inside the class array used in the Table component (the string containing 'h-10
px-3 text-left align-middle font-medium text-muted-foreground
[&:has([role=checkbox])]:pr-0 last:rounded-rt-xl first:rounded-lt-xl
text-nowrap'); update those two tokens so they use the correct `-tr-` and `-tl-`
variants.

In `@services/platform/app/features/chat/components/chat-input.tsx`:
- Around line 257-259: The upload-spinner chip is using hardcoded hex colors in
the container's className and the Loader's className; replace those hex values
with semantic theme tokens (e.g., Tailwind or design-system token classes) so
colors adapt to light/dark modes — update the container element's className
(currently containing border-[`#E5E7EB`] bg-[`#F3F4F6`]) to use semantic classes
like border-[token] and bg-[token], and change the Loader's className (currently
text-[`#9CA3AF`]) to a semantic text/token class; ensure you use the app’s
established token names so the spinner follows theme tokens across components
like chat-input.tsx and the Loader component.

In
`@services/platform/app/features/settings/teams/components/team-create-dialog.tsx`:
- Around line 91-106: The current flow builds memberIds from selectedMemberIds
and calls addMember via Promise.all which can reject and leave a created team
with partial membership; replace the Promise.all call in the member-assignment
block with Promise.allSettled over memberIds.map(userId => addMember(...)), then
inspect settled results to separate successful adds from failures, log or
collect failed userIds/errors, and surface a contextual toast: e.g., "Team
created; X members added, Y failed" (or a specific error list) while ensuring
the createTeam success path remains distinct from member-assignment failures;
also handle the no-member branch (authClient.getSession / userId addMember)
similarly to capture single-add failures and show accurate feedback.

In
`@services/platform/app/features/settings/teams/components/team-edit-dialog.tsx`:
- Around line 14-18: Move the local type declaration TeamMemberItem so it is not
interleaved with imports: keep all import statements (useAddTeamMember,
useRemoveTeamMember, useTeamMembers, Team, TeamMemberChecklist) grouped at the
top of team-edit-dialog.tsx, then place the TeamMemberItem type definition
immediately after the imports (before its usage in the component), ensuring
import ordering and file organization follow the coding guidelines.
- Around line 62-69: The code is using forbidden type assertions like "as
TeamMemberItem[]" in the team-edit-dialog component; instead update the source
hook useTeamMembers (in queries.ts) to return the precise typed shape (e.g.,
Array<{ _id: string; userId: string }> | undefined) so consumers can use
teamMembers without casting, then remove the local TeamMemberItem type and the
three casts (used in the useEffect that sets selectedMemberIds and
initialMemberIdsRef, and the other two occurrences at lines 122 and 170);
alternatively implement a narrow type guard for teamMembers and use it before
mapping, but the preferred fix is to export the correct return type from
useTeamMembers and update usages (setSelectedMemberIds, initialMemberIdsRef, and
any map over teamMembers) to rely on that proper type.
- Around line 131-145: Replace the Promise.all usage that runs
addTeamMember.mutateAsync and removeTeamMember.mutateAsync in parallel with
Promise.allSettled so partial failures are captured and handled (log or surface
failed results and only show success when all necessary ops succeeded);
additionally add a guard in the selection handler (the handleToggleMember /
setSelectedMemberIds logic) to prevent removing the last member (e.g., if
selected size <= 1, return previous set) so UI cannot trigger the "Cannot remove
the last team member" server error.

In
`@services/platform/app/features/settings/teams/components/team-member-checklist.tsx`:
- Around line 28-33: Replace hardcoded user-facing strings in the
TeamMemberChecklist component with i18n keys: in the isLoading branch (the
"Loading..." paragraph) and where "Unknown" is rendered (around the 68–70
section). Import and use the project's translation hook/function (e.g.,
useTranslation or t) and replace the literals with calls like t('team.loading')
and t('team.unknown') (or the appropriate key names), ensuring the keys are
added to the locale files.

In
`@services/platform/app/features/settings/teams/hooks/use-teams-table-config.tsx`:
- Around line 85-104: The formatRelativeTime function returns hardcoded English
strings and duplicates date formatting logic; remove or replace it with the
shared i18n-aware utilities by using the useFormatDate() hook (in React
components) or the formatDate() utility from lib/utils/date/format to produce a
localized relative time string, or move relative-time logic into the shared date
utils and call that instead; update any references to formatRelativeTime in this
file to call the chosen i18n-aware function/hook (e.g., useFormatDate or the
new/shared relative formatter) so all user-facing text is translated and
centralized.
- Around line 38-48: The members and createdAt columns are showing defaults
because the listOrgTeams query and Team type lack memberCount/createdAt and code
is using an unsafe cast in the cell renderer; update the listOrgTeams
GraphQL/REST query to include memberCount and createdAt and extend the Team type
accordingly (remove the "as Team & { memberCount?: number }" cast and rely on
the updated type), then update the members cell and createdAt cell to read those
real properties; replace the custom formatRelativeTime implementation with the
existing useFormatDate().formatRelative hook and remove hardcoded English
strings in formatRelativeTime, using translation functions (tSettings) where
needed (refer to useFormatDate, formatRelative, listOrgTeams, Team, and the cell
renderers).

In `@services/platform/app/globals.css`:
- Line 106: The :root CSS variable --muted currently uses a very dark neutral (0
0% 9.02%), causing bg-muted to render nearly black in light mode; change the
--muted value defined in :root in globals.css back to a light neutral (a high
lightness percentage) while leaving the existing .dark { --muted: 0 0% 9.02%; }
unchanged so dark-mode keeps the 9.02% token; this will restore light-mode
panels that rely on bg-muted (and places like the message improvement preview
moved from bg-secondary/20) to display correctly.

---

Outside diff comments:
In
`@services/platform/app/features/conversations/components/conversations-list.tsx`:
- Around line 261-264: The hardcoded fallback 'Unknown' in the ConversationsList
heading should be replaced with a translated string; update the rendering that
currently uses conversation.customer?.name || conversation?.title || 'Unknown'
to call your i18n translation (e.g., t('unknown') or
useTranslation().t('conversations.unknown')) so the fallback uses a translation
key, and ensure the component (ConversationsList) imports and invokes the
translation hook/provider before use.

In
`@services/platform/app/features/settings/teams/components/team-delete-dialog.tsx`:
- Around line 62-71: Restore the guard that prevents deleting teams which have
agents: compute a boolean (e.g., hasAgents) from the team/agent-bindings data
(for example check team.agentBindings?.length > 0 or
agentBindingsByTeamId[team.id]?.length > 0) and pass it into the DeleteDialog
via disableDelete={hasAgents} and
warning={tSettings('teams.teamHasAgentsWarning')}; keep existing props (open,
onOpenChange, title, description, isDeleting, onDelete) and only add the warning
and disableDelete props so users see the translation message and cannot delete a
team with agents still assigned.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ae71b043-6a18-4f36-8694-a5512ec4db8a

📥 Commits

Reviewing files that changed from the base of the PR and between 058c549 and b607f47.

📒 Files selected for processing (39)
  • services/platform/app/components/ui/data-display/table.tsx
  • services/platform/app/components/ui/data-table/data-table-skeleton.tsx
  • services/platform/app/components/ui/data-table/data-table.tsx
  • services/platform/app/components/ui/dialog/dialog.tsx
  • services/platform/app/components/ui/dialog/item-preview.tsx
  • services/platform/app/components/ui/forms/input.tsx
  • services/platform/app/components/ui/overlays/sheet.tsx
  • services/platform/app/components/ui/primitives/button.tsx
  • services/platform/app/components/user-button.tsx
  • services/platform/app/features/automations/components/automation-assistant/chat-input.tsx
  • services/platform/app/features/chat/components/chat-input.tsx
  • services/platform/app/features/chat/components/chat-interface.tsx
  • services/platform/app/features/conversations/components/conversation-header.tsx
  • services/platform/app/features/conversations/components/conversations-list.tsx
  • services/platform/app/features/conversations/components/conversations-skeleton.tsx
  • services/platform/app/features/conversations/components/message-improvement-dialog.tsx
  • services/platform/app/features/customers/components/customer-import-form.tsx
  • services/platform/app/features/settings/api-keys/components/api-key-create-dialog.tsx
  • services/platform/app/features/settings/api-keys/components/api-key-revoke-dialog.tsx
  • services/platform/app/features/settings/api-keys/components/api-keys-table.tsx
  • services/platform/app/features/settings/api-keys/hooks/use-api-keys-table-config.tsx
  • services/platform/app/features/settings/integrations/components/integration-panel.tsx
  • services/platform/app/features/settings/integrations/components/integration-upload/steps/upload-step.tsx
  • services/platform/app/features/settings/integrations/components/integrations.tsx
  • services/platform/app/features/settings/integrations/components/sso-config-dialog.tsx
  • services/platform/app/features/settings/organization/components/member-table.tsx
  • services/platform/app/features/settings/providers/components/providers-table.tsx
  • services/platform/app/features/settings/teams/components/__tests__/team-members-dialog.test.tsx
  • services/platform/app/features/settings/teams/components/team-create-dialog.tsx
  • services/platform/app/features/settings/teams/components/team-delete-dialog.tsx
  • services/platform/app/features/settings/teams/components/team-edit-dialog.tsx
  • services/platform/app/features/settings/teams/components/team-member-checklist.tsx
  • services/platform/app/features/settings/teams/components/team-members-dialog.tsx
  • services/platform/app/features/settings/teams/components/team-row-actions.tsx
  • services/platform/app/features/settings/teams/components/teams-table.tsx
  • services/platform/app/features/settings/teams/hooks/use-teams-table-config.tsx
  • services/platform/app/globals.css
  • services/platform/app/routes/dashboard/$id/chat.tsx
  • services/platform/messages/en.json
💤 Files with no reviewable changes (2)
  • services/platform/app/features/settings/teams/components/tests/team-members-dialog.test.tsx
  • services/platform/app/features/settings/teams/components/team-members-dialog.tsx

scope={scope}
className={cn(
'h-8 px-3 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 last:rounded-rt-xl first:rounded-lt-xl text-nowrap',
'h-10 px-3 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 last:rounded-rt-xl first:rounded-lt-xl text-nowrap',
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify if rounded-rt-xl and rounded-lt-xl are valid Tailwind classes or custom utilities

# Check for any custom utility definitions in the codebase
rg -n "rounded-rt|rounded-lt" --type css

# Check Tailwind config for custom utilities
fd -e js -e ts -e json "tailwind" --exec cat {} 2>/dev/null | head -100

Repository: tale-project/tale

Length of output: 474


🏁 Script executed:

# Find and check globals.css for custom theme definitions
fd -name "globals.css" -o -name "globals.scss" | xargs cat 2>/dev/null | head -200

Repository: tale-project/tale

Length of output: 291


🏁 Script executed:

# Check the actual file mentioned in the review
cat -n services/platform/app/components/ui/data-display/table.tsx 2>/dev/null | sed -n '80,95p'

Repository: tale-project/tale

Length of output: 620


🏁 Script executed:

# Search for usage of rounded-rt or rounded-lt classes across the codebase
rg "rounded-rt|rounded-lt" --type tsx --type ts --type jsx --type js

Repository: tale-project/tale

Length of output: 88


🏁 Script executed:

# Verify standard Tailwind naming conventions by checking if rounded-tr and rounded-tl are used
rg "rounded-tr|rounded-tl" --type tsx --type ts --type jsx --type js | head -20

Repository: tale-project/tale

Length of output: 88


🏁 Script executed:

# Search for these classes with correct rg syntax
rg "rounded-rt|rounded-lt" -t css -t js

Repository: tale-project/tale

Length of output: 43


🏁 Script executed:

# Find globals.css
fd "globals.css" -o "global.css"

Repository: tale-project/tale

Length of output: 200


🏁 Script executed:

# Check if there are any custom utilities in the entire codebase mentioning these classes
rg "rounded-rt-xl|rounded-lt-xl"

Repository: tale-project/tale

Length of output: 276


🏁 Script executed:

# Check for rounded-tr and rounded-tl usage to see which naming convention is used elsewhere
rg "rounded-tr|rounded-tl"

Repository: tale-project/tale

Length of output: 222


Typo in Tailwind border-radius classes.

The classes rounded-rt-xl and rounded-lt-xl are invalid. Standard Tailwind uses rounded-tr-xl (top-right) and rounded-tl-xl (top-left).

🔧 Proposed fix
-      'h-10 px-3 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 last:rounded-rt-xl first:rounded-lt-xl text-nowrap',
+      'h-10 px-3 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 last:rounded-tr-xl first:rounded-tl-xl text-nowrap',
📝 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
'h-10 px-3 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 last:rounded-rt-xl first:rounded-lt-xl text-nowrap',
'h-10 px-3 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 last:rounded-tr-xl first:rounded-tl-xl text-nowrap',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/platform/app/components/ui/data-display/table.tsx` at line 86,
Replace the invalid Tailwind classes in the table cell class string: change
`rounded-rt-xl` to `rounded-tr-xl` and `rounded-lt-xl` to `rounded-tl-xl` inside
the class array used in the Table component (the string containing 'h-10 px-3
text-left align-middle font-medium text-muted-foreground
[&:has([role=checkbox])]:pr-0 last:rounded-rt-xl first:rounded-lt-xl
text-nowrap'); update those two tokens so they use the correct `-tr-` and `-tl-`
variants.

Comment on lines +257 to +259
className="flex size-9 items-center justify-center overflow-hidden rounded-lg border border-[#E5E7EB] bg-[#F3F4F6]"
>
<LoaderCircleIcon className="size-4 animate-spin" />
<Loader className="size-4 animate-spin text-[#9CA3AF]" />
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid hardcoded grayscale hex colors in the upload spinner chip.

Using fixed #E5E7EB / #F3F4F6/#9CA3AF`` breaks theme-token consistency and can degrade dark-mode contrast. Use semantic tokens so this stays correct across themes.

🎯 Suggested fix
-                  className="flex size-9 items-center justify-center overflow-hidden rounded-lg border border-[`#E5E7EB`] bg-[`#F3F4F6`]"
+                  className="border-border bg-muted flex size-9 items-center justify-center overflow-hidden rounded-lg border"
                 >
-                  <Loader className="size-4 animate-spin text-[`#9CA3AF`]" />
+                  <Loader className="text-muted-foreground size-4 animate-spin" />
                 </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/platform/app/features/chat/components/chat-input.tsx` around lines
257 - 259, The upload-spinner chip is using hardcoded hex colors in the
container's className and the Loader's className; replace those hex values with
semantic theme tokens (e.g., Tailwind or design-system token classes) so colors
adapt to light/dark modes — update the container element's className (currently
containing border-[`#E5E7EB`] bg-[`#F3F4F6`]) to use semantic classes like
border-[token] and bg-[token], and change the Loader's className (currently
text-[`#9CA3AF`]) to a semantic text/token class; ensure you use the app’s
established token names so the spinner follows theme tokens across components
like chat-input.tsx and the Loader component.

Comment on lines +91 to 106
// Add selected members to the team
const memberIds = Array.from(selectedMemberIds);
if (memberIds.length === 0) {
// If no members selected, add the current user as default
const session = await authClient.getSession();
const userId = session.data?.user?.id;
if (userId) {
await addMember({ teamId, userId, organizationId });
}
} else {
await Promise.all(
memberIds.map((userId) =>
addMember({ teamId, userId, organizationId }),
),
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Partial failure risk: team created but member assignment may fail.

If Promise.all on line 101 rejects (e.g., one addMember call fails due to "User is already a member" or authorization issues), the team has already been created (line 77-89) but will have incomplete membership. The generic "teamCreateFailed" toast is misleading since the team was actually created.

Consider:

  1. Using Promise.allSettled to continue despite individual failures
  2. Providing a more accurate error message distinguishing team creation vs member assignment failures
  3. Showing partial success feedback when some members couldn't be added
🔧 Suggested approach using Promise.allSettled
-      await Promise.all(
-        memberIds.map((userId) =>
-          addMember({ teamId, userId, organizationId }),
-        ),
-      );
+      const results = await Promise.allSettled(
+        memberIds.map((userId) =>
+          addMember({ teamId, userId, organizationId }),
+        ),
+      );
+      const failedCount = results.filter((r) => r.status === 'rejected').length;
+      if (failedCount > 0) {
+        console.warn(`Failed to add ${failedCount} of ${memberIds.length} members`);
+        // Consider showing a partial success toast
+      }
📝 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
// Add selected members to the team
const memberIds = Array.from(selectedMemberIds);
if (memberIds.length === 0) {
// If no members selected, add the current user as default
const session = await authClient.getSession();
const userId = session.data?.user?.id;
if (userId) {
await addMember({ teamId, userId, organizationId });
}
} else {
await Promise.all(
memberIds.map((userId) =>
addMember({ teamId, userId, organizationId }),
),
);
}
// Add selected members to the team
const memberIds = Array.from(selectedMemberIds);
if (memberIds.length === 0) {
// If no members selected, add the current user as default
const session = await authClient.getSession();
const userId = session.data?.user?.id;
if (userId) {
await addMember({ teamId, userId, organizationId });
}
} else {
const results = await Promise.allSettled(
memberIds.map((userId) =>
addMember({ teamId, userId, organizationId }),
),
);
const failedCount = results.filter((r) => r.status === 'rejected').length;
if (failedCount > 0) {
console.warn(`Failed to add ${failedCount} of ${memberIds.length} members`);
// Consider showing a partial success toast
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@services/platform/app/features/settings/teams/components/team-create-dialog.tsx`
around lines 91 - 106, The current flow builds memberIds from selectedMemberIds
and calls addMember via Promise.all which can reject and leave a created team
with partial membership; replace the Promise.all call in the member-assignment
block with Promise.allSettled over memberIds.map(userId => addMember(...)), then
inspect settled results to separate successful adds from failures, log or
collect failed userIds/errors, and surface a contextual toast: e.g., "Team
created; X members added, Y failed" (or a specific error list) while ensuring
the createTeam success path remains distinct from member-assignment failures;
also handle the no-member branch (authClient.getSession / userId addMember)
similarly to capture single-add failures and show accurate feedback.

Comment on lines +14 to +18
import { useAddTeamMember, useRemoveTeamMember } from '../hooks/mutations';
import { useTeamMembers, type Team } from '../hooks/queries';

type TeamMemberItem = { _id: string; userId: string };
import { TeamMemberChecklist } from './team-member-checklist';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Import ordering issue: type definition mixed with imports.

The local type definition TeamMemberItem on line 17 is placed between imports (lines 14-15 and line 18). Per coding guidelines, imports should be at the top, properly sorted. Move the type definition after all imports.

♻️ Suggested fix
 import { useAddTeamMember, useRemoveTeamMember } from '../hooks/mutations';
 import { useTeamMembers, type Team } from '../hooks/queries';
-
-type TeamMemberItem = { _id: string; userId: string };
 import { TeamMemberChecklist } from './team-member-checklist';
+
+type TeamMemberItem = { _id: string; userId: string };
📝 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
import { useAddTeamMember, useRemoveTeamMember } from '../hooks/mutations';
import { useTeamMembers, type Team } from '../hooks/queries';
type TeamMemberItem = { _id: string; userId: string };
import { TeamMemberChecklist } from './team-member-checklist';
import { useAddTeamMember, useRemoveTeamMember } from '../hooks/mutations';
import { useTeamMembers, type Team } from '../hooks/queries';
import { TeamMemberChecklist } from './team-member-checklist';
type TeamMemberItem = { _id: string; userId: string };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@services/platform/app/features/settings/teams/components/team-edit-dialog.tsx`
around lines 14 - 18, Move the local type declaration TeamMemberItem so it is
not interleaved with imports: keep all import statements (useAddTeamMember,
useRemoveTeamMember, useTeamMembers, Team, TeamMemberChecklist) grouped at the
top of team-edit-dialog.tsx, then place the TeamMemberItem type definition
immediately after the imports (before its usage in the component), ensuring
import ordering and file organization follow the coding guidelines.

Comment on lines +62 to +69
useEffect(() => {
if (teamMembers && open) {
const members = teamMembers as TeamMemberItem[];
const memberIds = new Set(members.map((m) => m.userId));
setSelectedMemberIds(memberIds);
initialMemberIdsRef.current = memberIds;
}
}, [teamMembers, open]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Type casts with as violate coding guidelines.

Lines 64, 122, and 170 use as TeamMemberItem[] which is explicitly prohibited. The proper fix is to type the return value of useTeamMembers correctly or use a type guard.

🔧 Suggested approach

Define the proper return type in useTeamMembers hook so consumers don't need to cast:

// In queries.ts
export function useTeamMembers(teamId: string): {
  teamMembers: Array<{ _id: string; userId: string }> | undefined;
  // ... other fields
}

Then remove the casts and the local TeamMemberItem type.

Also applies to: 122-122, 170-170

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

In
`@services/platform/app/features/settings/teams/components/team-edit-dialog.tsx`
around lines 62 - 69, The code is using forbidden type assertions like "as
TeamMemberItem[]" in the team-edit-dialog component; instead update the source
hook useTeamMembers (in queries.ts) to return the precise typed shape (e.g.,
Array<{ _id: string; userId: string }> | undefined) so consumers can use
teamMembers without casting, then remove the local TeamMemberItem type and the
three casts (used in the useEffect that sets selectedMemberIds and
initialMemberIdsRef, and the other two occurrences at lines 122 and 170);
alternatively implement a narrow type guard for teamMembers and use it before
mapping, but the preferred fix is to export the correct return type from
useTeamMembers and update usages (setSelectedMemberIds, initialMemberIdsRef, and
any map over teamMembers) to rely on that proper type.

Comment on lines +131 to +145
await Promise.all([
...toAdd.map((userId) =>
addTeamMember.mutateAsync({
teamId: team.id,
userId,
organizationId,
}),
),
...toRemove.map((m) =>
removeTeamMember.mutateAsync({
teamMemberId: m._id,
organizationId,
}),
),
]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Partial failure risk with parallel mutations.

Promise.all will reject if any addTeamMember or removeTeamMember call fails, leaving membership in an inconsistent state. The removeMember mutation (context snippet 1) throws on several conditions including "Cannot remove the last team member".

Additionally, if a user deselects all members, the removal operations will fail with the "last member" error, but this isn't prevented in the UI.

🔧 Suggested improvements
  1. Use Promise.allSettled to handle partial failures gracefully:
-      await Promise.all([
-        ...toAdd.map((userId) =>
-          addTeamMember.mutateAsync({ ... }),
-        ),
-        ...toRemove.map((m) =>
-          removeTeamMember.mutateAsync({ ... }),
-        ),
-      ]);
+      const results = await Promise.allSettled([
+        ...toAdd.map((userId) =>
+          addTeamMember.mutateAsync({ ... }),
+        ),
+        ...toRemove.map((m) =>
+          removeTeamMember.mutateAsync({ ... }),
+        ),
+      ]);
+      const failures = results.filter((r) => r.status === 'rejected');
+      if (failures.length > 0) {
+        console.warn('Some membership changes failed:', failures);
+      }
  1. Add validation to prevent deselecting all members:
const handleToggleMember = useCallback((userId: string) => {
  setSelectedMemberIds((prev) => {
    const next = new Set(prev);
    if (next.has(userId)) {
      if (next.size <= 1) return prev; // Prevent removing last member
      next.delete(userId);
    } else {
      next.add(userId);
    }
    return next;
  });
}, []);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@services/platform/app/features/settings/teams/components/team-edit-dialog.tsx`
around lines 131 - 145, Replace the Promise.all usage that runs
addTeamMember.mutateAsync and removeTeamMember.mutateAsync in parallel with
Promise.allSettled so partial failures are captured and handled (log or surface
failed results and only show success when all necessary ops succeeded);
additionally add a guard in the selection handler (the handleToggleMember /
setSelectedMemberIds logic) to prevent removing the last member (e.g., if
selected size <= 1, return previous set) so UI cannot trigger the "Cannot remove
the last team member" server error.

Comment on lines +28 to +33
if (isLoading) {
return (
<div className="flex items-center justify-center py-4">
<p className="text-muted-foreground text-sm">Loading...</p>
</div>
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Replace hardcoded UI strings with translations.

"Loading..." and "Unknown" are user-facing text and should come from i18n keys.

🌐 Suggested i18n-safe update
-        <p className="text-muted-foreground text-sm">Loading...</p>
+        <p className="text-muted-foreground text-sm">
+          {tSettings('teams.loadingMembers')}
+        </p>
...
-                  {member.displayName || member.email || 'Unknown'}
+                  {member.displayName ||
+                    member.email ||
+                    tSettings('teams.unknownMember')}
As per coding guidelines: "Do not hardcode text in UI components. Use translation hooks/functions instead for user-facing UI."

Also applies to: 68-70

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

In
`@services/platform/app/features/settings/teams/components/team-member-checklist.tsx`
around lines 28 - 33, Replace hardcoded user-facing strings in the
TeamMemberChecklist component with i18n keys: in the isLoading branch (the
"Loading..." paragraph) and where "Unknown" is rendered (around the 68–70
section). Import and use the project's translation hook/function (e.g.,
useTranslation or t) and replace the literals with calls like t('team.loading')
and t('team.unknown') (or the appropriate key names), ensuring the keys are
added to the locale files.

Comment on lines +38 to +48
id: 'members',
header: tSettings('teams.columns.members'),
cell: ({ row }) => {
const team = row.original as Team & { memberCount?: number };
const count = team.memberCount ?? 0;
return (
<Text as="span" variant="muted">
{tSettings('teams.memberCount', { count })}
</Text>
);
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the Team type and getMyTeams query return shape
echo "=== Team type definition ==="
rg -n "export type Team" services/platform/app/features/settings/teams/hooks/queries.ts -A 2

echo -e "\n=== getMyTeams query returns ==="
ast-grep --pattern 'export const getMyTeams = query({
  $$$
  returns: $_,
  $$$
})'

Repository: tale-project/tale

Length of output: 778


🏁 Script executed:

# Check formatRelativeTime implementation and the created column
cat -n services/platform/app/features/settings/teams/hooks/use-teams-table-config.tsx | sed -n '50,110p'

Repository: tale-project/tale

Length of output: 2155


🏁 Script executed:

# Check if useFormatDate or formatDate is available and used elsewhere
rg "useFormatDate|formatDate" services/platform/app --type tsx -B 1 -A 1 | head -40

Repository: tale-project/tale

Length of output: 88


🏁 Script executed:

# Search for useFormatDate or formatDate usage with correct file type
rg "useFormatDate|formatDate" services/platform/app -A 1 | head -50

Repository: tale-project/tale

Length of output: 3428


🏁 Script executed:

# Check if there's a standard date formatting utility being used
find services/platform/app -name "*format*" -type f | grep -E "\.(ts|tsx)$"

Repository: tale-project/tale

Length of output: 257


🏁 Script executed:

# Check what formatDateSmart does - it might support relative formatting
cat -n services/platform/app/hooks/use-format-date.ts

Repository: tale-project/tale

Length of output: 3737


🏁 Script executed:

# Check if memberCount or createdAt are being added to teams somewhere before being passed to the table
rg "memberCount|createdAt" services/platform/app/features/settings/teams -B 3 -A 3

Repository: tale-project/tale

Length of output: 3931


🏁 Script executed:

# Check if the Team type is being extended or enriched elsewhere
rg "Team &" services/platform/app/features/settings/teams -B 2 -A 2

Repository: tale-project/tale

Length of output: 1253


🏁 Script executed:

# Check the entire use-teams-table-config.tsx file to see where teams data comes from
head -50 services/platform/app/features/settings/teams/hooks/use-teams-table-config.tsx

Repository: tale-project/tale

Length of output: 1383


🏁 Script executed:

# Check if there's any parent component or hook that enriches the teams data before it reaches the table
rg "useTeamsTableConfig" services/platform/app/features/settings/teams -B 5 -A 5

Repository: tale-project/tale

Length of output: 3599


🏁 Script executed:

# Look for where the teams data query is called
rg "useQuery.*getMyTeams" services/platform/app/features/settings/teams -B 3 -A 5

Repository: tale-project/tale

Length of output: 43


🏁 Script executed:

# Check where TeamsTable is used to see how teams data is passed
rg "TeamsTable" services/platform/app/features/settings/teams -B 3 -A 3

Repository: tale-project/tale

Length of output: 3555


🏁 Script executed:

# Check the full teams-table.tsx to see how teams are handled
cat -n services/platform/app/features/settings/teams/components/teams-table.tsx | head -80

Repository: tale-project/tale

Length of output: 2156


🏁 Script executed:

# Find where TeamsTable component is used
rg "TeamsTable" services/platform/app/features/settings/teams -l

Repository: tale-project/tale

Length of output: 212


🏁 Script executed:

# Look for the main page/container component
find services/platform/app/features/settings/teams -name "page.tsx" -o -name "index.tsx" | xargs cat -n

Repository: tale-project/tale

Length of output: 43


🏁 Script executed:

# List all files in the teams directory
find services/platform/app/features/settings/teams -type f -name "*.tsx" -o -name "*.ts" | head -20

Repository: tale-project/tale

Length of output: 903


🏁 Script executed:

# Search for where teams data comes from - check parent components
rg "getMyTeams\|useQuery.*teams" services/platform/app/features/settings -B 2 -A 5

Repository: tale-project/tale

Length of output: 43


🏁 Script executed:

# Check the queries.ts file in teams hooks
cat -n services/platform/app/features/settings/teams/hooks/queries.ts

Repository: tale-project/tale

Length of output: 1780


🏁 Script executed:

# Check if there are other queries that might return memberCount or createdAt for teams
rg "listOrgTeams" services/platform/convex -A 10

Repository: tale-project/tale

Length of output: 9478


🏁 Script executed:

# Check which query hook is actually used - search for useTeams or useOrgTeams in settings/teams
rg "useTeams\|useOrgTeams" services/platform/app/features/settings/teams -B 2 -A 2

Repository: tale-project/tale

Length of output: 43


🏁 Script executed:

# Find the main settings/teams page component
find services/platform/app/features/settings -name "*.tsx" | xargs grep -l "TeamsTable"

Repository: tale-project/tale

Length of output: 212


🏁 Script executed:

# Check settings folder structure
ls -la services/platform/app/features/settings/

Repository: tale-project/tale

Length of output: 702


🏁 Script executed:

# Look in settings/components
ls -la services/platform/app/features/settings/components/

Repository: tale-project/tale

Length of output: 247


🏁 Script executed:

# Find the page that uses TeamsTable
find services/platform/app -path "*/settings/*" -name "*.tsx" | xargs grep -l "TeamsTable"

Repository: tale-project/tale

Length of output: 274


🏁 Script executed:

# Check the main teams page
cat -n services/platform/app/routes/dashboard/'$id'/settings/teams.tsx

Repository: tale-project/tale

Length of output: 1700


🏁 Script executed:

# Confirm that useOrgTeams is indeed the hook used, check if it maps/enriches data
cat -n services/platform/app/features/settings/teams/hooks/queries.ts | grep -A 10 "useOrgTeams"

Repository: tale-project/tale

Length of output: 416


Multiple issues: Missing query fields, hardcoded text, and type casting violations.

The Team type (returned by listOrgTeams query) only includes id and name. The memberCount and createdAt properties don't exist, so these columns will always display default values (0 and null).

Additionally:

  • formatRelativeTime contains hardcoded English strings ("year", "month", "just now", etc.) instead of using translation functions—violates i18n guideline.
  • Should use useFormatDate().formatRelative from the existing hook instead of implementing custom date formatting.
  • Type casts (as Team & { memberCount?: number }) violate the no-casting guideline. If these fields are needed, update the listOrgTeams query to return them.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@services/platform/app/features/settings/teams/hooks/use-teams-table-config.tsx`
around lines 38 - 48, The members and createdAt columns are showing defaults
because the listOrgTeams query and Team type lack memberCount/createdAt and code
is using an unsafe cast in the cell renderer; update the listOrgTeams
GraphQL/REST query to include memberCount and createdAt and extend the Team type
accordingly (remove the "as Team & { memberCount?: number }" cast and rely on
the updated type), then update the members cell and createdAt cell to read those
real properties; replace the custom formatRelativeTime implementation with the
existing useFormatDate().formatRelative hook and remove hardcoded English
strings in formatRelativeTime, using translation functions (tSettings) where
needed (refer to useFormatDate, formatRelative, listOrgTeams, Team, and the cell
renderers).

Comment on lines +85 to +104
function formatRelativeTime(timestamp: number): string {
const now = Date.now();
const diff = now - timestamp;
const seconds = Math.floor(diff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
const weeks = Math.floor(days / 7);
const months = Math.floor(days / 30);
const years = Math.floor(days / 365);

if (years > 0) return `${years} ${years === 1 ? 'year' : 'years'} ago`;
if (months > 0) return `${months} ${months === 1 ? 'month' : 'months'} ago`;
if (weeks > 0) return `${weeks} ${weeks === 1 ? 'week' : 'weeks'} ago`;
if (days > 0) return `${days} ${days === 1 ? 'day' : 'days'} ago`;
if (hours > 0) return `${hours} ${hours === 1 ? 'hour' : 'hours'} ago`;
if (minutes > 0)
return `${minutes} ${minutes === 1 ? 'minute' : 'minutes'} ago`;
return 'just now';
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Hardcoded English text violates i18n requirements.

The formatRelativeTime function returns hardcoded strings like "year", "month", "just now", etc. Per coding guidelines, all user-facing text must use translation hooks.

Additionally, this duplicates functionality that should use the existing useFormatDate() hook or formatDate() utility from lib/utils/date/format. As per coding guidelines: "Do not use toLocaleDateString(), toLocaleTimeString(), or toLocaleString(). Use useFormatDate() hook (React) or formatDate() from lib/utils/date/format instead."

🔧 Suggested approach

Remove the custom formatRelativeTime function and use the existing date formatting utilities with i18n support. If a relative time format doesn't exist, consider adding it to the shared date utilities rather than implementing it locally with hardcoded strings.

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

In
`@services/platform/app/features/settings/teams/hooks/use-teams-table-config.tsx`
around lines 85 - 104, The formatRelativeTime function returns hardcoded English
strings and duplicates date formatting logic; remove or replace it with the
shared i18n-aware utilities by using the useFormatDate() hook (in React
components) or the formatDate() utility from lib/utils/date/format to produce a
localized relative time string, or move relative-time logic into the shared date
utils and call that instead; update any references to formatRelativeTime in this
file to call the chosen i18n-aware function/hook (e.g., useFormatDate or the
new/shared relative formatter) so all user-facing text is translated and
centralized.

Comment thread services/platform/app/globals.css Outdated
--secondary: 220 9% 65%;
--secondary-foreground: 240 5.9% 10%;
--muted: 240 4.8% 95.9%;
--muted: 0 0% 9.02%;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Restore a light neutral for --muted.

--muted: 0 0% 9.02% makes bg-muted nearly black in light mode. Because this PR also moves several surfaces from bg-secondary/20 to bg-muted, light-theme panels now render as dark blocks, and places like the message improvement preview end up with dark content on a dark background. Keep the 9.02% token in .dark, but use a light neutral in :root.

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

In `@services/platform/app/globals.css` at line 106, The :root CSS variable
--muted currently uses a very dark neutral (0 0% 9.02%), causing bg-muted to
render nearly black in light mode; change the --muted value defined in :root in
globals.css back to a light neutral (a high lightness percentage) while leaving
the existing .dark { --muted: 0 0% 9.02%; } unchanged so dark-mode keeps the
9.02% token; this will restore light-mode panels that rely on bg-muted (and
places like the message improvement preview moved from bg-secondary/20) to
display correctly.

…alog

ConvexItemOf resolves to object without codegen in CI, so define the
needed shape locally.
- Fix rounded-rt-xl/rounded-lt-xl typos to rounded-tr-xl/rounded-tl-xl
- Replace hardcoded hex colors in chat upload spinner with semantic tokens
- Restore light-mode --muted CSS variable (was nearly black)
- Fix import ordering in team-edit-dialog, move type after imports
- Use Promise.allSettled for team member mutations (create + edit)
- Prevent removing last team member in edit dialog
- Replace hardcoded "Loading..."/"Unknown" with i18n keys
- Remove non-functional memberCount/createdAt columns from teams table
- Remove custom formatRelativeTime in favor of existing date utilities
@Israeltheminer Israeltheminer merged commit cca21a9 into main Apr 7, 2026
24 checks passed
@Israeltheminer Israeltheminer deleted the fix/ui-refinements-teams-settings-components branch April 7, 2026 21:02
larryro added a commit that referenced this pull request Apr 8, 2026
The --input CSS variable was changed to pure white in #1038 for Input
background fill, which broke border visibility on Textarea (border-input)
and Switch track (bg-input) in light mode. Use --border instead, which
remains a visible gray in both light and dark modes.
larryro added a commit that referenced this pull request Apr 8, 2026
The --input CSS variable was changed to pure white in #1038 for Input
background fill, which broke border visibility on Textarea (border-input)
and Switch track (bg-input) in light mode. Use --border instead, which
remains a visible gray in both light and dark modes.
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.

1 participant