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
+7 -6
View File
@@ -1669,9 +1669,10 @@
"dashboard": "لوحة المعلومات", "dashboard": "لوحة المعلومات",
"bookings": "حجوزات القاعات", "bookings": "حجوزات القاعات",
"external": "اللقاءات الخارجية", "external": "اللقاءات الخارجية",
"gifts": "الإهداءات", "giftsGroup": "الهدايا",
"issues": "طلبات الصرف", "gifts": "المستودع",
"photos": "تصوير", "issues": "الإهداءات",
"photos": "الصور",
"reports": "التقارير", "reports": "التقارير",
"audit": "سجل النشاط", "audit": "سجل النشاط",
"rooms": "القاعات" "rooms": "القاعات"
@@ -1704,7 +1705,7 @@
"pendingBookings": "حجوزات بانتظار الاعتماد", "pendingBookings": "حجوزات بانتظار الاعتماد",
"upcomingExternal": "لقاءات خارجية قادمة", "upcomingExternal": "لقاءات خارجية قادمة",
"giftStock": "إجمالي مخزون الهدايا", "giftStock": "إجمالي مخزون الهدايا",
"pendingIssues": "طلبات صرف معلّقة", "pendingIssues": "إهداءات معلّقة",
"roomsAvailableNow": "القاعات المتاحة الآن", "roomsAvailableNow": "القاعات المتاحة الآن",
"giftsIssuedThisMonth": "الدروع المصروفة هذا الشهر" "giftsIssuedThisMonth": "الدروع المصروفة هذا الشهر"
}, },
@@ -1763,8 +1764,8 @@
"empty": "لا توجد قاعات." "empty": "لا توجد قاعات."
}, },
"issues": { "issues": {
"new": "طلب صرف", "new": "إهداء جديد",
"empty": "لا توجد طلبات صرف.", "empty": "لا توجد إهداءات.",
"recipient": "المستفيد", "recipient": "المستفيد",
"gift": "الصنف", "gift": "الصنف",
"occasion": "المناسبة", "occasion": "المناسبة",
+6 -5
View File
@@ -1542,8 +1542,9 @@
"dashboard": "Dashboard", "dashboard": "Dashboard",
"bookings": "Room Bookings", "bookings": "Room Bookings",
"external": "External Meetings", "external": "External Meetings",
"gifts": "Dedications", "giftsGroup": "Gifts",
"issues": "Issuance Requests", "gifts": "Warehouse",
"issues": "Dedications",
"photos": "Photos", "photos": "Photos",
"reports": "Reports", "reports": "Reports",
"audit": "Activity Log", "audit": "Activity Log",
@@ -1577,7 +1578,7 @@
"pendingBookings": "Bookings awaiting approval", "pendingBookings": "Bookings awaiting approval",
"upcomingExternal": "Upcoming external meetings", "upcomingExternal": "Upcoming external meetings",
"giftStock": "Total gift stock", "giftStock": "Total gift stock",
"pendingIssues": "Pending issuance requests", "pendingIssues": "Pending dedications",
"roomsAvailableNow": "Rooms available now", "roomsAvailableNow": "Rooms available now",
"giftsIssuedThisMonth": "Gifts issued this month" "giftsIssuedThisMonth": "Gifts issued this month"
}, },
@@ -1636,8 +1637,8 @@
"empty": "No rooms." "empty": "No rooms."
}, },
"issues": { "issues": {
"new": "Issuance request", "new": "New dedication",
"empty": "No issuance requests yet.", "empty": "No dedications yet.",
"recipient": "Recipient", "recipient": "Recipient",
"gift": "Item", "gift": "Item",
"occasion": "Occasion", "occasion": "Occasion",
+74 -20
View File
@@ -10,6 +10,9 @@ import {
CalendarClock, CalendarClock,
Handshake, Handshake,
Gift, Gift,
Warehouse,
HandHeart,
ChevronDown,
BarChart3, BarChart3,
ScrollText, ScrollText,
Plus, Plus,
@@ -17,12 +20,10 @@ import {
Trash2, Trash2,
Check, Check,
X, X,
PackageCheck,
CalendarDays, CalendarDays,
List as ListIcon, List as ListIcon,
Link2, Link2,
Copy, Copy,
Camera,
Upload, Upload,
Loader2, Loader2,
Download, Download,
@@ -632,6 +633,7 @@ export default function ProtocolPage() {
const [issueDialog, setIssueDialog] = useState<{ open: boolean }>({ const [issueDialog, setIssueDialog] = useState<{ open: boolean }>({
open: false, open: false,
}); });
const [giftsGroupOpen, setGiftsGroupOpen] = useState(false);
const bookingAction = useMutation({ const bookingAction = useMutation({
mutationFn: (args: { id: number; action: "approve" | "cancel" }) => 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: "rooms", label: t("protocol.tabs.rooms"), icon: DoorOpen },
{ key: "bookings", label: t("protocol.tabs.bookings"), icon: CalendarClock }, { key: "bookings", label: t("protocol.tabs.bookings"), icon: CalendarClock },
{ key: "external", label: t("protocol.tabs.external"), icon: Handshake }, { key: "external", label: t("protocol.tabs.external"), icon: Handshake },
{ key: "gifts", label: t("protocol.tabs.gifts"), icon: Gift }, { key: "gifts", label: t("protocol.tabs.gifts"), icon: Warehouse },
{ key: "issues", label: t("protocol.tabs.issues"), icon: PackageCheck }, { key: "issues", label: t("protocol.tabs.issues"), icon: Gift },
{ key: "photos", label: t("protocol.tabs.photos"), icon: Camera }, { key: "photos", label: t("protocol.tabs.photos"), icon: ImageIcon },
{ key: "reports", label: t("protocol.tabs.reports"), icon: BarChart3 }, { key: "reports", label: t("protocol.tabs.reports"), icon: BarChart3 },
...(c?.canViewAudit ...(c?.canViewAudit
? [{ key: "audit" as TabKey, label: t("protocol.tabs.audit"), icon: ScrollText }] ? [{ 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"> <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"> <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"> <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 }) => ( {TABS.map((item) => {
<button const { key, label, icon: Icon } = item;
key={key} if (key === "issues") return null;
onClick={() => setTab(key)} if (key === "gifts") {
className={cn( const children = TABS.filter(
"flex items-center gap-2 px-3 py-2 rounded-lg text-sm whitespace-nowrap transition-colors md:w-full md:justify-start", (x) => x.key === "gifts" || x.key === "issues",
tab === key );
? "bg-sky-500 text-white" const groupActive = children.some((x) => x.key === tab);
: "text-slate-600 hover:bg-slate-100", const open = giftsGroupOpen || groupActive;
)} return (
> <div key="giftsGroup" className="flex md:flex-col gap-1">
<Icon size={16} className="shrink-0" /> <button
{label} type="button"
</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> </nav>
</aside> </aside>