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": "لوحة المعلومات",
"bookings": "حجوزات القاعات",
"external": "اللقاءات الخارجية",
"gifts": "الإهداءات",
"issues": "طلبات الصرف",
"photos": "تصوير",
"giftsGroup": "الهدايا",
"gifts": "المستودع",
"issues": "الإهداءات",
"photos": "الصور",
"reports": "التقارير",
"audit": "سجل النشاط",
"rooms": "القاعات"
@@ -1704,7 +1705,7 @@
"pendingBookings": "حجوزات بانتظار الاعتماد",
"upcomingExternal": "لقاءات خارجية قادمة",
"giftStock": "إجمالي مخزون الهدايا",
"pendingIssues": "طلبات صرف معلّقة",
"pendingIssues": "إهداءات معلّقة",
"roomsAvailableNow": "القاعات المتاحة الآن",
"giftsIssuedThisMonth": "الدروع المصروفة هذا الشهر"
},
@@ -1763,8 +1764,8 @@
"empty": "لا توجد قاعات."
},
"issues": {
"new": "طلب صرف",
"empty": "لا توجد طلبات صرف.",
"new": "إهداء جديد",
"empty": "لا توجد إهداءات.",
"recipient": "المستفيد",
"gift": "الصنف",
"occasion": "المناسبة",
+6 -5
View File
@@ -1542,8 +1542,9 @@
"dashboard": "Dashboard",
"bookings": "Room Bookings",
"external": "External Meetings",
"gifts": "Dedications",
"issues": "Issuance Requests",
"giftsGroup": "Gifts",
"gifts": "Warehouse",
"issues": "Dedications",
"photos": "Photos",
"reports": "Reports",
"audit": "Activity Log",
@@ -1577,7 +1578,7 @@
"pendingBookings": "Bookings awaiting approval",
"upcomingExternal": "Upcoming external meetings",
"giftStock": "Total gift stock",
"pendingIssues": "Pending issuance requests",
"pendingIssues": "Pending dedications",
"roomsAvailableNow": "Rooms available now",
"giftsIssuedThisMonth": "Gifts issued this month"
},
@@ -1636,8 +1637,8 @@
"empty": "No rooms."
},
"issues": {
"new": "Issuance request",
"empty": "No issuance requests yet.",
"new": "New dedication",
"empty": "No dedications yet.",
"recipient": "Recipient",
"gift": "Item",
"occasion": "Occasion",
+61 -7
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,7 +853,58 @@ 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 }) => (
{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)}
@@ -865,7 +918,8 @@ export default function ProtocolPage() {
<Icon size={16} className="shrink-0" />
{label}
</button>
))}
);
})}
</nav>
</aside>