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:
@@ -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": "المناسبة",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user