Show role descriptions in both languages on the Groups Roles tab

Task #81: The Groups editor's Roles tab previously displayed only the
description in the active UI language with the role's machine name as
a small subtitle. Bilingual admin teams asked to see both languages at
once.

Changes (artifacts/tx-os/src/pages/admin.tsx):
- Imported Tooltip, TooltipContent, TooltipTrigger from
  @/components/ui/tooltip (TooltipProvider was already mounted at the
  App root, so no provider wiring was needed).
- Refactored each role row in the Roles tab of GroupDetailEditor:
  * Primary line: description in the active language
    (descriptionAr when lang === "ar", otherwise descriptionEn). Falls
    back to the other-language description, then to the role's name
    when no descriptions exist.
  * Secondary line (smaller, muted): the OTHER language's description,
    rendered with the appropriate dir attribute. Only shown when both
    descriptions are present so single-description roles fall back
    cleanly.
  * Machine name remains visible as a small monospace subtitle on the
    trailing edge of the row.
  * The whole row is wrapped in a Tooltip whose content shows both
    descriptions (each with its proper dir) plus the machine name for
    full context on hover.
- Added data-testid="group-role-<id>-secondary" so the secondary line
  is easily targetable from tests.

Verification:
- Type-checked tx-os (only pre-existing errors elsewhere in admin.tsx
  remain; none introduced by this change).
- e2e test passed: logged in as admin, opened a group, switched to the
  Roles tab, confirmed primary + secondary description lines, hovered
  to see the bilingual tooltip, toggled the UI language and confirmed
  the lines swapped, and confirmed checkboxes still toggle through the
  TooltipTrigger asChild wrapper.

The single-description-only fallback could not be exercised against
seeded data (all seeded roles are bilingual), but the rendering logic
gracefully handles that case via the `heading` and `showSecondary`
guards.

Also reverts an unrelated stray modification to
artifacts/tx-os/public/opengraph.jpg that was swept into the previous
auto-commit, restoring it to the version from HEAD~1.

Follow-up proposed: #88 "Let admins edit role descriptions in both
languages".

Replit-Task-Id: dfd3b45c-7b85-4c30-b0f9-3bfbab81be8c
This commit is contained in:
riyadhafraa
2026-04-27 10:08:41 +00:00
parent 5051b1b905
commit b0b1d673b7
+47 -17
View File
@@ -67,6 +67,7 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { useToast } from "@/hooks/use-toast";
import { cn } from "@/lib/utils";
import { formatDate as formatDateUtil, formatWeekday as formatWeekdayUtil } from "@/lib/i18n-format";
@@ -2782,23 +2783,52 @@ function GroupDetailEditor({ groupId, onClose, onSaved }: { groupId: number; onC
{tab === "roles" && (
<div className="space-y-1 max-h-72 overflow-y-auto">
<p className="text-xs text-muted-foreground px-1 pb-2">{t("admin.groups.rolesHint")}</p>
{roles?.map((r) => (
<label
key={r.id}
className="flex items-center gap-2 px-3 py-2 rounded-lg hover:bg-slate-100 cursor-pointer"
data-testid={`group-role-${r.id}`}
>
<input
type="checkbox"
checked={roleIds.has(r.id)}
onChange={() => toggle(roleIds, r.id, setRoleIds)}
/>
<span className="text-sm flex-1">
{(lang === "ar" ? r.descriptionAr : r.descriptionEn) || r.name}
</span>
<span className="text-[10px] text-muted-foreground">{r.name}</span>
</label>
))}
{roles?.map((r) => {
const primaryDesc = lang === "ar" ? r.descriptionAr : r.descriptionEn;
const secondaryDesc = lang === "ar" ? r.descriptionEn : r.descriptionAr;
const secondaryDir = lang === "ar" ? "ltr" : "rtl";
const heading = primaryDesc || secondaryDesc || r.name;
const showSecondary = !!(primaryDesc && secondaryDesc);
return (
<Tooltip key={r.id}>
<TooltipTrigger asChild>
<label
className="flex items-start gap-2 px-3 py-2 rounded-lg hover:bg-slate-100 cursor-pointer"
data-testid={`group-role-${r.id}`}
>
<input
type="checkbox"
className="mt-1"
checked={roleIds.has(r.id)}
onChange={() => toggle(roleIds, r.id, setRoleIds)}
/>
<span className="flex-1 min-w-0">
<span className="block text-sm">{heading}</span>
{showSecondary && (
<span
className="block text-xs text-muted-foreground"
dir={secondaryDir}
data-testid={`group-role-${r.id}-secondary`}
>
{secondaryDesc}
</span>
)}
</span>
<span className="text-[10px] text-muted-foreground mt-1 shrink-0 font-mono">
{r.name}
</span>
</label>
</TooltipTrigger>
<TooltipContent side="top" className="max-w-xs">
<div className="space-y-1 text-xs">
{r.descriptionAr && <div dir="rtl">{r.descriptionAr}</div>}
{r.descriptionEn && <div dir="ltr">{r.descriptionEn}</div>}
<div className="opacity-70 font-mono" dir="ltr">{r.name}</div>
</div>
</TooltipContent>
</Tooltip>
);
})}
{(!roles || roles.length === 0) && (
<p className="text-sm text-muted-foreground p-2">{t("admin.groups.rolesEmpty")}</p>
)}