Task #668: Rename Protocol side-menu items (bilingual AR/EN)

Relabeled three Protocol (العلاقات العامة والمراسم) menu items to match real usage:
- photos: "تصوير" -> "الصور" / "Photos"
- gifts (item catalog where أصناف are added): "الإهداءات" -> "المستودع" / "Warehouse"
- issues (giving gifts out): "طلبات الصرف" -> "الإهداءات" / "Dedications"

Swapped menu icons to match new meanings: gifts->Warehouse, issues->Gift,
photos->ImageIcon. Removed now-unused imports (PackageCheck, Camera).

Grouped المستودع + الإهداءات under one expandable submenu (parent label
"الهدايا"/"Gifts", new i18n key protocol.tabs.giftsGroup, HandHeart icon,
ChevronDown toggle). Group auto-expands when a child tab is active; state
giftsGroupOpen. RTL-aware (ms-auto chevron, md:ps-6 child indent).

Aligned in-screen wording with new names in both locales:
dashboard.pendingIssues, issues.new, issues.empty reworded from
issuance/صرف to dedication/إهداء.

Deviation: submenu grouping and parent-group naming ("الهدايا") were an
interpretation of the user's "sub-menu" request (user_query was unavailable
during planning). Kept scope tight: remaining secondary "issuance/صرف"
strings in reports/actions/dashboard stats (e.g. giftsIssuedThisMonth,
actions.issued, reports.byGift) were left unchanged and proposed as a
follow-up.

Unchanged (deliberately): internal tab keys (gifts/issues/photos) and URL
slugs (gifts, gift-issues, photos) so deep links keep working. No feature,
API, or data changes.

Verified: pnpm --filter @workspace/tx-os run typecheck passes; architect
code review passed after issues icon corrected to Gift.
This commit is contained in:
Replit Agent
2026-07-07 11:58:20 +00:00
parent 164c9224c0
commit 2c244ae6e1
3 changed files with 87 additions and 31 deletions
+74 -20
View File
@@ -10,6 +10,9 @@ import {
CalendarClock,
Handshake,
Gift,
Warehouse,
HandHeart,
ChevronDown,
BarChart3,
ScrollText,
Plus,
@@ -17,12 +20,10 @@ import {
Trash2,
Check,
X,
PackageCheck,
CalendarDays,
List as ListIcon,
Link2,
Copy,
Camera,
Upload,
Loader2,
Download,
@@ -632,6 +633,7 @@ export default function ProtocolPage() {
const [issueDialog, setIssueDialog] = useState<{ open: boolean }>({
open: false,
});
const [giftsGroupOpen, setGiftsGroupOpen] = useState(false);
const bookingAction = useMutation({
mutationFn: (args: { id: number; action: "approve" | "cancel" }) =>
@@ -712,9 +714,9 @@ export default function ProtocolPage() {
{ key: "rooms", label: t("protocol.tabs.rooms"), icon: DoorOpen },
{ key: "bookings", label: t("protocol.tabs.bookings"), icon: CalendarClock },
{ key: "external", label: t("protocol.tabs.external"), icon: Handshake },
{ key: "gifts", label: t("protocol.tabs.gifts"), icon: Gift },
{ key: "issues", label: t("protocol.tabs.issues"), icon: PackageCheck },
{ key: "photos", label: t("protocol.tabs.photos"), icon: Camera },
{ key: "gifts", label: t("protocol.tabs.gifts"), icon: Warehouse },
{ key: "issues", label: t("protocol.tabs.issues"), icon: Gift },
{ key: "photos", label: t("protocol.tabs.photos"), icon: ImageIcon },
{ key: "reports", label: t("protocol.tabs.reports"), icon: BarChart3 },
...(c?.canViewAudit
? [{ key: "audit" as TabKey, label: t("protocol.tabs.audit"), icon: ScrollText }]
@@ -851,21 +853,73 @@ export default function ProtocolPage() {
<div className="max-w-6xl mx-auto px-4 py-5 flex flex-col md:flex-row gap-4 md:gap-6">
<aside className="md:w-56 shrink-0">
<nav className="flex md:flex-col gap-1 overflow-x-auto md:overflow-visible pb-2 md:pb-0 md:sticky md:top-20">
{TABS.map(({ key, label, icon: Icon }) => (
<button
key={key}
onClick={() => setTab(key)}
className={cn(
"flex items-center gap-2 px-3 py-2 rounded-lg text-sm whitespace-nowrap transition-colors md:w-full md:justify-start",
tab === key
? "bg-sky-500 text-white"
: "text-slate-600 hover:bg-slate-100",
)}
>
<Icon size={16} className="shrink-0" />
{label}
</button>
))}
{TABS.map((item) => {
const { key, label, icon: Icon } = item;
if (key === "issues") return null;
if (key === "gifts") {
const children = TABS.filter(
(x) => x.key === "gifts" || x.key === "issues",
);
const groupActive = children.some((x) => x.key === tab);
const open = giftsGroupOpen || groupActive;
return (
<div key="giftsGroup" className="flex md:flex-col gap-1">
<button
type="button"
onClick={() => setGiftsGroupOpen((v) => !v)}
aria-expanded={open}
className={cn(
"flex items-center gap-2 px-3 py-2 rounded-lg text-sm whitespace-nowrap transition-colors md:w-full md:justify-start",
groupActive
? "text-sky-600 font-medium"
: "text-slate-600 hover:bg-slate-100",
)}
>
<HandHeart size={16} className="shrink-0" />
{t("protocol.tabs.giftsGroup")}
<ChevronDown
size={14}
className={cn(
"ms-auto shrink-0 transition-transform",
open && "rotate-180",
)}
/>
</button>
{open &&
children.map(({ key: ck, label: cl, icon: CIcon }) => (
<button
key={ck}
onClick={() => setTab(ck)}
className={cn(
"flex items-center gap-2 px-3 py-2 rounded-lg text-sm whitespace-nowrap transition-colors md:w-full md:justify-start md:ps-6",
tab === ck
? "bg-sky-500 text-white"
: "text-slate-600 hover:bg-slate-100",
)}
>
<CIcon size={16} className="shrink-0" />
{cl}
</button>
))}
</div>
);
}
return (
<button
key={key}
onClick={() => setTab(key)}
className={cn(
"flex items-center gap-2 px-3 py-2 rounded-lg text-sm whitespace-nowrap transition-colors md:w-full md:justify-start",
tab === key
? "bg-sky-500 text-white"
: "text-slate-600 hover:bg-slate-100",
)}
>
<Icon size={16} className="shrink-0" />
{label}
</button>
);
})}
</nav>
</aside>