e66c69bccc
Schedule UI / shell:
- Centered cells, locked RTL column order # | الاجتماع | الحضور | الوقت,
attendees column widest, tighter padding, navy/white/gray + red highlight
only. Header label "م" → "#" (ar.json).
Schema (lib/db/src/schema/executive-meetings.ts):
- executive_meeting_requests.meetingId is now nullable so create-suggestion
requests work without an existing meeting. db push applied.
Backend (artifacts/api-server/src/routes/executive-meetings.ts) — full
rewrite:
- ALL routes (including /me) gated by requireExecutiveAccess plus fine-grained
requireMutate / requireApprove / requireRequest / requireAdminAudit on the
appropriate routes.
- Zod schemas validate every mutating body via parseBody().
- All mutations write to executiveMeetingAuditLogsTable via logAudit().
- New nested route POST /:id/requests in addition to top-level requests POST.
- Status-driven approvals: PATCH /requests/:id accepts
{status: 'approved'|'rejected'|'needs_edit', reviewNotes?} or
{action:'withdraw'} for the requester.
- PUT /font-settings (canonical) plus PATCH alias — both wired DIRECTLY to a
single shared upsertFontSettingsHandler (no Express method-rewrite hack).
- GET /tasks supports date and assigneeId filters.
- GET /notifications now accepts ?date=YYYY-MM-DD and joins through the
meetings table to scope notifications to the selected day; returns []
immediately when no meetings exist on that date.
- Removed all `as never` casts via $inferInsert/Partial typing.
Auth middleware (artifacts/api-server/src/middlewares/auth.ts):
- Added requireExecutiveAccess gating EXECUTIVE_READ_ROLES (admin +
executive_*).
Frontend (artifacts/tx-os/src/pages/executive-meetings.tsx):
- Tab-level RBAC: SECTIONS.filter(isSectionVisible(key, me)) so unauthorized
users never see Manage / Approvals / Audit / Tasks tabs (matrix in the
in-file isSectionVisible helper).
- Approvals: review() sends {status, reviewNotes}; new "Send back for edits"
(needs_edit) button beside Approve / Reject.
- Requests default scope is "mine" for non-approvers and "all" for approvers
(no over-exposure to plain requesters).
- Notifications visibility uses canRead AND the query is scoped by the same
selected `date` as the schedule (queryKey + ?date=… included).
- PdfSection: primary "Print + Archive" button (testid em-print) does archive
then print in one click; em-print-only and em-archive remain as separate
buttons.
- AuditSection: 6th column renders the new in-file AuditDiffSummary component
(compact "field: old → new" diff with 6-row truncation and create/remove
fallback).
- New tWithFallback(t, key, fallback) helper used in audit + notifications
cells (avoids strict TFunction overload errors); apiJson rewritten to
extract body/headers separately and stringify rawBody, eliminating the
"Record<string, unknown> not assignable to BodyInit" TS errors. tsc
--noEmit on executive-meetings.tsx is now clean.
- Print page (executive-meetings-print.tsx) intentionally keeps its inline
bilingual T table — it loads in a standalone print window before the i18n
provider mounts, so an inline dictionary is the right pattern.
i18n (en.json + ar.json):
- New keys executiveMeetings.approvals.{needsEdit, needsEditDone},
.audit.col.changes, .audit.{created, removed, moreChanges},
.audit.entity.pdf_archive,
.audit.action.{approved, rejected, needs_edit, withdraw, apply, duplicate,
replace_attendees, done},
.pdf.{print = "Print + archive" / "طباعة وأرشفة", printOnly,
archivedAndPrinted}.
Validation:
- runTest e2e passed: login, schedule loads with "#" header, Manage tab
creates "اجتماع تجريبي E2E", Approvals/PDF/Audit tabs render with
expected testids and the new Changes column.
- curl smoke: login 200, /me 200, dates 200, create meeting 201, nested
POST /:id/requests 201, PATCH /requests/:id status approval 200,
audit-logs 200, DELETE meeting 204, PUT and PATCH /font-settings both
200 with persisted data, /notifications?date=YYYY-MM-DD filters
correctly.
- tsc --noEmit on artifacts/tx-os/src/pages/executive-meetings.tsx is
clean (pre-existing admin.tsx errors are unrelated to this task).
- Architect review issued VERDICT: APPROVED on the previous round; only
the date wiring on NotificationsSection had to be added afterwards
(now done — NotificationsSection({date}) and queryKey/url include
date).
Validation-round-2 fixes (this commit):
- GET /executive-meetings/requests: added server-side least-privilege so
only APPROVE_ROLES + ADMIN_AUDIT_ROLES see other users' requests; every
other executive role is force-scoped to requestedBy = self regardless
of the client's `mine` flag (closes the RBAC overexposure flag).
- isSectionVisible("tasks") now also returns true for canSubmitRequest,
so executive_coordinator sees the Tasks tab (coordinators execute the
follow-ups generated from approved requests).
- Requests "new request" dropdown now exposes ALL twelve backend request
types: create, edit, delete, reschedule, add_attendee, remove_attendee,
change_location, cancel_meeting, note, highlight, unhighlight, other.
- i18n parity: added en.json + ar.json keys for the five missing request
types (add_attendee, remove_attendee, change_location, cancel_meeting,
note) and the two missing statuses (needs_edit, done) under
executiveMeetings.requests.{type,status}.
- Fixed wrong key reference: reschedule form's date row was using
manage.field.date (does not exist) — switched to manage.field.meetingDate.
Drift / out of scope (explicitly deferred to follow-ups #110/#111/#112):
- Real notifications delivery, server-side PDF rendering, attendee
reorder UI (drag/arrows), duplicate-to-date UI, OpenAPI/api-spec sync,
expanded seed data including executive_meeting_notifications sample
rows.
895 lines
32 KiB
JSON
895 lines
32 KiB
JSON
{
|
||
"nav": {
|
||
"home": "Home",
|
||
"services": "Services",
|
||
"chat": "Chat",
|
||
"notifications": "Notifications",
|
||
"admin": "Admin Dashboard",
|
||
"logout": "Logout"
|
||
},
|
||
"auth": {
|
||
"login": "Login",
|
||
"register": "Register",
|
||
"username": "Username",
|
||
"email": "Email",
|
||
"password": "Password",
|
||
"loginButton": "Login",
|
||
"registerButton": "Register",
|
||
"welcomeBack": "Welcome Back",
|
||
"createAccount": "Create Account",
|
||
"registrationClosed": "Public registration is currently closed. Please contact the administrator.",
|
||
"displayNameAr": "Name (Arabic)",
|
||
"displayNameEn": "Name (English)",
|
||
"subtitle": "Sign in to your account to continue",
|
||
"signingIn": "Signing in...",
|
||
"noAccount": "Don't have an account?",
|
||
"showPassword": "Show password",
|
||
"hidePassword": "Hide password",
|
||
"errRequired": "Please enter your username and password.",
|
||
"errInvalid": "Invalid username or password.",
|
||
"forgotPassword": "Forgot password?",
|
||
"forgot": {
|
||
"title": "Reset your password",
|
||
"subtitle": "Enter your username or email and we'll send you a link to reset your password.",
|
||
"identifier": "Username or email",
|
||
"identifierPlaceholder": "you@example.com",
|
||
"submit": "Send reset link",
|
||
"sending": "Sending...",
|
||
"errRequired": "Please enter your username or email.",
|
||
"errGeneric": "Something went wrong. Please try again.",
|
||
"successMessage": "Your request has been received. An administrator will share a reset link with you shortly.",
|
||
"adminNotice": "Email delivery is not configured for this workspace yet, so an administrator will issue a one-time reset link manually.",
|
||
"backToLogin": "Back to sign in"
|
||
},
|
||
"reset": {
|
||
"title": "Set a new password",
|
||
"subtitle": "Choose a new password for your account.",
|
||
"newPassword": "New password",
|
||
"confirmPassword": "Confirm password",
|
||
"submit": "Update password",
|
||
"saving": "Saving...",
|
||
"errMinLength": "Password must be at least 6 characters.",
|
||
"errMismatch": "Passwords do not match.",
|
||
"errGeneric": "Could not reset password. The link may have expired.",
|
||
"invalidToken": "This reset link is invalid or has expired.",
|
||
"requestNew": "Request a new link",
|
||
"successMessage": "Your password has been updated. You can now sign in with your new password.",
|
||
"goToLogin": "Go to sign in"
|
||
},
|
||
"heroTitle": "Welcome to your unified workspace.",
|
||
"heroSubtitle": "Sign in to manage everything from one place.",
|
||
"feat1": "All your tools in one dashboard",
|
||
"feat2": "Real-time updates",
|
||
"feat3": "Secure session-based access",
|
||
"rights": "All rights reserved."
|
||
},
|
||
"home": {
|
||
"myApps": "My Apps",
|
||
"noApps": "No apps yet",
|
||
"noAppsHint": "Apps you have access to will appear here.",
|
||
"greeting": {
|
||
"morning": "Good morning, {{name}}",
|
||
"afternoon": "Good afternoon, {{name}}",
|
||
"evening": "Good evening, {{name}}"
|
||
},
|
||
"stats": {
|
||
"apps": "Apps",
|
||
"services": "Services",
|
||
"messages": "Messages",
|
||
"alerts": "Alerts"
|
||
},
|
||
"clockStyle": {
|
||
"label": "Clock style",
|
||
"showWidget": "Show wall clock on home screen",
|
||
"showTopbar": "Show clock in top bar",
|
||
"hideWidget": "Hide clock",
|
||
"hourFormat": {
|
||
"label": "Hour format",
|
||
"h12": "12-hour (AM/PM)",
|
||
"h24": "24-hour"
|
||
},
|
||
"options": {
|
||
"full": "Full (time, weekday, Hijri & Gregorian)",
|
||
"digital": "Digital with seconds",
|
||
"digital-no-seconds": "Digital without seconds",
|
||
"analog": "Analog clock",
|
||
"minimal": "Minimal (time only)"
|
||
}
|
||
}
|
||
},
|
||
"services": {
|
||
"title": "Services",
|
||
"available": "Available",
|
||
"unavailable": "Unavailable",
|
||
"price": "Price",
|
||
"noServices": "No services available",
|
||
"category": "Category",
|
||
"free": "Free",
|
||
"order": "Order",
|
||
"orderModalTitle": "Order: {{name}}",
|
||
"orderNotes": "Notes (optional)",
|
||
"orderNotesPlaceholder": "Any extra notes?",
|
||
"orderNotesCounter": "{{count}}/500",
|
||
"placeOrder": "Send",
|
||
"placing": "Sending...",
|
||
"orderPlaced": "Order placed",
|
||
"orderFailed": "Could not place the order",
|
||
"myOrdersLink": "My Orders"
|
||
},
|
||
"myOrders": {
|
||
"title": "My Orders",
|
||
"loadError": "Could not load your orders. Please try again.",
|
||
"retry": "Retry",
|
||
"back": "Back",
|
||
"empty": "No orders yet",
|
||
"emptyHint": "Start from the services catalog.",
|
||
"browseServices": "Browse services",
|
||
"notesLabel": "Notes:",
|
||
"cancel": "Cancel order",
|
||
"cancelConfirmTitle": "Cancel this order?",
|
||
"cancelConfirmBody": "You'll have a few seconds to undo this.",
|
||
"cancelConfirm": "Yes, cancel",
|
||
"keep": "Keep it",
|
||
"cancelled": "Order cancelled",
|
||
"cancelFailed": "Could not cancel the order",
|
||
"restoreFailed": "Could not restore the order",
|
||
"delete": "Delete",
|
||
"reorder": "Reorder",
|
||
"deleteConfirmTitle": "Delete this order?",
|
||
"deleteConfirmBody": "This will permanently remove the order from your history.",
|
||
"deleteConfirm": "Yes, delete",
|
||
"deleted": "Order deleted",
|
||
"deleteFailed": "Could not delete the order",
|
||
"undo": "Undo",
|
||
"undone": "Restored",
|
||
"clearFinished_one": "Clear 1 finished",
|
||
"clearFinished_other": "Clear {{count}} finished",
|
||
"clearConfirmTitle": "Clear finished orders?",
|
||
"clearConfirmBody_one": "1 finished order will be permanently removed from your history.",
|
||
"clearConfirmBody_other": "{{count}} finished orders will be permanently removed from your history.",
|
||
"clearConfirm": "Yes, clear",
|
||
"clearedCount_one": "1 order deleted",
|
||
"clearedCount_other": "{{count}} orders deleted",
|
||
"clearedPartial": "{{ok}} deleted, {{fail}} failed"
|
||
},
|
||
"orderStatus": {
|
||
"pending": "Awaiting receiver",
|
||
"received": "Received",
|
||
"preparing": "Preparing",
|
||
"completed": "Completed",
|
||
"cancelled": "Cancelled"
|
||
},
|
||
"incomingOrders": {
|
||
"title": "Incoming Orders",
|
||
"linkLabel": "Incoming",
|
||
"subtitleUnclaimed": "Awaiting receiver",
|
||
"subtitleMine": "My active orders",
|
||
"empty": "No incoming orders right now",
|
||
"emptyHint": "New orders will show up here as they arrive.",
|
||
"loadError": "Could not load incoming orders.",
|
||
"retry": "Retry",
|
||
"noAccess": "You don't have permission to receive orders.",
|
||
"back": "Back",
|
||
"ageJustNow": "just now",
|
||
"ageMinutes": "{{count}}m ago",
|
||
"ageHours": "{{count}}h ago",
|
||
"ageDays": "{{count}}d ago",
|
||
"from": "From",
|
||
"confirmReceipt": "Confirm Receipt",
|
||
"confirming": "Confirming...",
|
||
"markPreparing": "Mark Preparing",
|
||
"markCompleted": "Mark Completed",
|
||
"cancel": "Cancel",
|
||
"alreadyClaimed": "Already claimed by someone else",
|
||
"orderUnavailable": "This order is no longer available",
|
||
"actionFailed": "Could not complete the action",
|
||
"claimed": "Order received",
|
||
"marked": "Status updated",
|
||
"cancelled": "Order cancelled",
|
||
"undo": "Undo",
|
||
"undone": "Restored",
|
||
"restoreFailed": "Could not restore the order"
|
||
},
|
||
"chat": {
|
||
"title": "Chat",
|
||
"newMessage": "New Message",
|
||
"typeMessage": "Type a message...",
|
||
"send": "Send",
|
||
"noConversations": "No conversations",
|
||
"createConversation": "New Conversation",
|
||
"groupChat": "Group",
|
||
"directMessage": "Direct Message",
|
||
"participants": "Participants",
|
||
"searchUsers": "Search users",
|
||
"modeDirect": "Direct",
|
||
"modeGroup": "Group",
|
||
"groupNameAr": "Group name (Arabic)",
|
||
"groupNameEn": "Group name (English)",
|
||
"groupNamePlaceholderAr": "مثلاً: فريق المبيعات",
|
||
"groupNamePlaceholderEn": "e.g. Sales team",
|
||
"searchUsersPlaceholder": "Search by name or username",
|
||
"participantsCount_one": "{{count}} person selected",
|
||
"participantsCount_other": "{{count}} people selected",
|
||
"participantsCount_zero": "No one selected",
|
||
"noUsersFound": "No matching users",
|
||
"uploadAvatar": "Upload picture",
|
||
"replaceAvatar": "Replace picture",
|
||
"removeAvatar": "Remove picture",
|
||
"changeAvatar": "Change group picture",
|
||
"uploadingAvatar": "Uploading… {{progress}}%",
|
||
"avatarUploadFailed": "Could not upload picture",
|
||
"validation": {
|
||
"needGroupName": "Enter a group name in Arabic or English.",
|
||
"minTwoMembers": "Pick at least 2 people for a group.",
|
||
"pickOnePerson": "Pick one person to chat with."
|
||
},
|
||
"create": "Create",
|
||
"settings": {
|
||
"title": "Group settings",
|
||
"openSettings": "Open group settings",
|
||
"groupName": "Group name",
|
||
"saveName": "Save name",
|
||
"nameSaved": "Group name updated",
|
||
"saveFailed": "Could not save changes",
|
||
"needGroupName": "Enter a group name in Arabic or English.",
|
||
"members_one": "{{count}} member",
|
||
"members_other": "{{count}} members",
|
||
"addMembers": "Add members",
|
||
"addSelected": "Add",
|
||
"membersAdded": "Members added",
|
||
"addFailed": "Could not add members",
|
||
"removeMember": "Remove member",
|
||
"removeFailed": "Could not remove member",
|
||
"admin": "Admin",
|
||
"you": "(you)",
|
||
"viewOnly": "Only the group admin can change the name or members."
|
||
},
|
||
"filterAll": "Active",
|
||
"filterArchived": "Archived",
|
||
"noArchived": "No archived chats",
|
||
"actions": {
|
||
"title": "Chat options",
|
||
"openActions": "Open chat options",
|
||
"mute": "Mute notifications",
|
||
"unmute": "Unmute notifications",
|
||
"archive": "Archive chat",
|
||
"unarchive": "Unarchive chat",
|
||
"leave": "Leave group",
|
||
"leaveConfirmTitle": "Leave this group?",
|
||
"leaveConfirmBody": "You will be removed from \"{{name}}\" and stop receiving its messages.",
|
||
"successorTitle": "Choose the next admin",
|
||
"successorHelp": "You're the only admin. Pick who should take over, or let us promote the longest-tenured member.",
|
||
"successorAuto": "Auto (longest-tenured member)",
|
||
"muted": "Notifications muted",
|
||
"unmuted": "Notifications on",
|
||
"archived": "Chat archived",
|
||
"unarchived": "Chat moved back to active",
|
||
"left": "You left the group",
|
||
"actionFailed": "Could not complete action",
|
||
"mutedLabel": "Muted",
|
||
"youAreAdminTitle": "You are now the admin",
|
||
"youAreAdminDescription": "You're now in charge of \"{{name}}\".",
|
||
"newAdminBadge": "New admin",
|
||
"newAdminLabel": "New admin: {{name}}"
|
||
},
|
||
"system": {
|
||
"someone": "Someone",
|
||
"listSeparator": ", ",
|
||
"groupRenamed": "{{actor}} renamed the group to \"{{name}}\"",
|
||
"membersAdded_one": "{{actor}} added {{names}}",
|
||
"membersAdded_other": "{{actor}} added {{names}}",
|
||
"memberRemoved": "{{actor}} removed {{target}}",
|
||
"memberLeft": "{{actor}} left the group",
|
||
"adminPromoted": "{{target}} is now a group admin",
|
||
"adminPromotedAfterLeave": "{{target}} was promoted to admin after the previous admin left"
|
||
}
|
||
},
|
||
"notifications": {
|
||
"title": "Notifications",
|
||
"markAllRead": "Mark All Read",
|
||
"noNotifications": "No new notifications",
|
||
"unread": "Unread"
|
||
},
|
||
"admin": {
|
||
"title": "Admin Dashboard",
|
||
"manageApps": "Manage Apps",
|
||
"manageServices": "Manage Services",
|
||
"manageUsers": "Manage Users",
|
||
"appDisabled": "Disabled",
|
||
"addApp": "Add App",
|
||
"addService": "Add Service",
|
||
"addUser": "Add User",
|
||
"editApp": "Edit App",
|
||
"editService": "Edit Service",
|
||
"editUser": "Edit User",
|
||
"deleteConfirm": "Are you sure you want to delete?",
|
||
"deleteApp": {
|
||
"title": "Delete app \"{{name}}\"?",
|
||
"warning": "This app is still in use. Deleting it will affect the following:",
|
||
"forceHint": "This will revoke access for everyone using it and remove its history. This cannot be undone.",
|
||
"emptyBody": "Are you sure you want to delete this app?",
|
||
"groupCount": "{{count}} group(s) currently grant this app",
|
||
"restrictionCount": "{{count}} permission rule(s) restricting it",
|
||
"openCount": "{{count}} open event(s) in history",
|
||
"confirm": "Delete",
|
||
"anyway": "Delete anyway"
|
||
},
|
||
"deleteService": {
|
||
"title": "Delete service \"{{name}}\"?",
|
||
"warning": "This service still has orders. Deleting it will affect the following:",
|
||
"forceHint": "This will permanently remove every related order. This cannot be undone.",
|
||
"emptyBody": "Are you sure you want to delete this service?",
|
||
"orderCount": "{{count}} order(s)",
|
||
"confirm": "Delete",
|
||
"anyway": "Delete anyway"
|
||
},
|
||
"deleteUser": {
|
||
"title": "Delete user \"{{name}}\"?",
|
||
"warning": "This user owns data. Deleting them will affect the following:",
|
||
"forceHint": "This will permanently remove their notes, orders, conversations they started, and messages they sent. This cannot be undone.",
|
||
"emptyBody": "Are you sure you want to delete this user?",
|
||
"noteCount": "{{count}} note(s)",
|
||
"orderCount": "{{count}} order(s)",
|
||
"conversationCount": "{{count}} conversation(s) created",
|
||
"messageCount": "{{count}} message(s) sent",
|
||
"confirm": "Delete",
|
||
"anyway": "Delete anyway"
|
||
},
|
||
"activate": "Activate",
|
||
"deactivate": "Deactivate",
|
||
"appName": "App Name",
|
||
"appNameAr": "App Name (Arabic)",
|
||
"appNameEn": "App Name (English)",
|
||
"appSlug": "Slug",
|
||
"appRoute": "Route",
|
||
"appIcon": "Icon",
|
||
"appColor": "Color",
|
||
"appSortOrder": "Sort Order",
|
||
"serviceName": "Service Name",
|
||
"serviceNameAr": "Service Name (Arabic)",
|
||
"serviceNameEn": "Service Name (English)",
|
||
"serviceDescriptionAr": "Description (Arabic)",
|
||
"serviceDescriptionEn": "Description (English)",
|
||
"servicePrice": "Price",
|
||
"serviceAvailable": "Available?",
|
||
"serviceImageUrl": "Image URL",
|
||
"serviceImage": "Service Image",
|
||
"uploadImage": "Upload Image",
|
||
"replaceImage": "Replace Image",
|
||
"uploading": "Uploading...",
|
||
"uploadFailed": "Image upload failed",
|
||
"users": "Users",
|
||
"username": "Username",
|
||
"email": "Email",
|
||
"password": "Password",
|
||
"roles": "Roles",
|
||
"active": "Active",
|
||
"issueResetLink": "Issue password reset link",
|
||
"orderReceiverRole": "Order Receiver",
|
||
"resetLinkModal": {
|
||
"title": "Password reset link for {{username}}",
|
||
"description": "Share this one-time link with the user. It can only be used once.",
|
||
"expires": "Expires: {{time}}",
|
||
"copy": "Copy link",
|
||
"copied": "Link copied to clipboard"
|
||
},
|
||
"siteSettings": "Site Settings",
|
||
"siteNameAr": "Site Name (Arabic)",
|
||
"siteNameEn": "Site Name (English)",
|
||
"registrationOpen": "Allow public registration",
|
||
"registrationOpenHint": "When off, only admins can create new users.",
|
||
"footerTextAr": "Footer Text (Arabic)",
|
||
"footerTextEn": "Footer Text (English)",
|
||
"nav": {
|
||
"dashboard": "Dashboard",
|
||
"apps": "Apps",
|
||
"services": "Services",
|
||
"users": "Users",
|
||
"groups": "Groups",
|
||
"roles": "Roles",
|
||
"auditLog": "Audit log",
|
||
"userManagement": "User Management",
|
||
"settings": "Settings",
|
||
"menu": "Menu"
|
||
},
|
||
"users": {
|
||
"searchPlaceholder": "Search by username or email...",
|
||
"allGroups": "All groups",
|
||
"allStatuses": "All statuses",
|
||
"statusActive": "Active",
|
||
"statusInactive": "Inactive",
|
||
"showing": "Showing {{count}} of {{total}}",
|
||
"empty": "No users match your filters.",
|
||
"editGroups": "Edit groups",
|
||
"editUserGroups": "Edit groups for {{username}}",
|
||
"col": {
|
||
"username": "Username",
|
||
"email": "Email",
|
||
"displayNameAr": "Name (Arabic)",
|
||
"displayNameEn": "Name (English)",
|
||
"language": "Language",
|
||
"createdAt": "Created",
|
||
"groups": "Groups",
|
||
"actions": "Actions"
|
||
}
|
||
},
|
||
"roles": {
|
||
"searchPlaceholder": "Search roles...",
|
||
"addRole": "Add Role",
|
||
"editRole": "Edit Role",
|
||
"name": "Name",
|
||
"namePlaceholder": "e.g. content_editor",
|
||
"nameHint": "Letters, numbers and underscores only.",
|
||
"descriptionAr": "Description (Arabic)",
|
||
"descriptionEn": "Description (English)",
|
||
"system": "System",
|
||
"empty": "No roles yet.",
|
||
"edit": "Edit role",
|
||
"delete": "Delete role",
|
||
"editSystemNameHint": "System role names cannot be changed.",
|
||
"deleteTitle": "Delete role \"{{name}}\"?",
|
||
"deleteEmptyBody": "This role isn't assigned to anyone. Continue?",
|
||
"deleteConflict": "This role is still in use and cannot be deleted.",
|
||
"deleteConflictHint": "Remove this role from the affected users and groups before deleting it.",
|
||
"deleteConfirm": "Delete",
|
||
"usersCount": "{{count}} users",
|
||
"groupsCount": "{{count}} groups",
|
||
"errorNameTaken": "A role with that name already exists.",
|
||
"errorInvalidName": "Role name must start with a letter and contain only letters, numbers, or underscores.",
|
||
"errorSystemRename": "System role names cannot be changed.",
|
||
"permissions": "Permissions",
|
||
"permissionsHint": "Select which permissions this role grants.",
|
||
"permissionsSystemHint": "System role permissions are read-only.",
|
||
"permissionsEmpty": "No permissions are defined.",
|
||
"errorSystemPermissions": "System role permissions cannot be changed.",
|
||
"errorPermissionsSave": "Could not save the role's permissions."
|
||
},
|
||
"groups": {
|
||
"searchPlaceholder": "Search groups...",
|
||
"addGroup": "Add Group",
|
||
"editGroup": "Edit Group",
|
||
"name": "Name",
|
||
"descriptionAr": "Description (Arabic)",
|
||
"descriptionEn": "Description (English)",
|
||
"system": "System",
|
||
"empty": "No groups yet.",
|
||
"members": "{{count}} members",
|
||
"appsCount": "{{count}} apps",
|
||
"rolesCount": "{{count}} roles",
|
||
"appsHint": "Members of this group will see these apps.",
|
||
"usersHint": "Members of this group.",
|
||
"rolesHint": "Members of this group are automatically granted these roles.",
|
||
"rolesEmpty": "No roles available.",
|
||
"deleteTitle": "Delete group \"{{name}}\"?",
|
||
"deleteWarning": "This group is still in use. Deleting it will revoke access for the following:",
|
||
"deleteForceHint": "Deleting will detach these members and revoke their group-granted apps and roles. This cannot be undone.",
|
||
"deleteEmptyBody": "This group has no members, apps, or roles. Continue?",
|
||
"deleteConfirm": "Delete",
|
||
"deleteAnyway": "Delete anyway",
|
||
"tab": {
|
||
"info": "Info",
|
||
"apps": "Apps",
|
||
"users": "Users",
|
||
"roles": "Roles"
|
||
}
|
||
},
|
||
"audit": {
|
||
"filters": {
|
||
"action": "Action",
|
||
"allActions": "All actions",
|
||
"from": "From",
|
||
"to": "To",
|
||
"apply": "Apply",
|
||
"reset": "Reset"
|
||
},
|
||
"target": "Target: {{type}} #{{id}}",
|
||
"showing": "Showing {{shown}} of {{total}}",
|
||
"empty": "No audit log entries match these filters.",
|
||
"loadMore": "Load more",
|
||
"errors": {
|
||
"invalidDate": "Dates must be in YYYY-MM-DD format.",
|
||
"invertedDates": "\"From\" date must be on or before \"To\" date.",
|
||
"fixFiltersFirst": "Adjust the filters to load entries."
|
||
}
|
||
},
|
||
"dashboardSoon": "Dashboard widgets coming soon.",
|
||
"dashboard": {
|
||
"totalApps": "Total Apps",
|
||
"totalServices": "Total Services",
|
||
"totalUsers": "Total Users",
|
||
"registration": "Registration",
|
||
"open": "Open",
|
||
"closed": "Closed",
|
||
"recentActivity": "Recent Activity",
|
||
"latestUser": "Latest User",
|
||
"latestApp": "Latest App",
|
||
"none": "—",
|
||
"newUsers7d": "New users (7d)",
|
||
"vsPrev7d": "Previous 7d: {{count}}",
|
||
"newUsersInRange": "New users · {{range}}",
|
||
"vsPrevRange": "{{range}}: {{count}}",
|
||
"activeServices": "Active services",
|
||
"inactiveServices": "Inactive: {{count}}",
|
||
"signupsTrend": "Sign-ups",
|
||
"last7Days": "Last 7 days",
|
||
"appOpensTrend": "App opens",
|
||
"appOpensSummary": "{{count}} total · {{delta}}",
|
||
"appOpensSummaryRanged": "{{count}} in {{range}} · {{delta}}",
|
||
"servicesCreatedTrend": "Services added",
|
||
"servicesCreatedSummary": "{{count}} added",
|
||
"servicesCreatedSummaryRanged": "{{count}} in {{range}}",
|
||
"trends": "Trends",
|
||
"rangeSelector": "Time range",
|
||
"range": {
|
||
"7d": "Last 7 days",
|
||
"30d": "Last 30 days",
|
||
"90d": "Last 90 days",
|
||
"custom": "Custom range",
|
||
"customLabel": "{{from}} – {{to}}"
|
||
},
|
||
"prevRange": {
|
||
"7d": "Previous 7 days",
|
||
"30d": "Previous 30 days",
|
||
"90d": "Previous 90 days",
|
||
"custom": "Previous {{count}} days"
|
||
},
|
||
"customRange": {
|
||
"from": "From",
|
||
"to": "To",
|
||
"apply": "Apply",
|
||
"invalid": "Pick a valid date range",
|
||
"loadError": "Couldn't load stats for the selected dates. Please check the range and try again."
|
||
},
|
||
"topApps": "Top apps",
|
||
"topAppsSubtitle": "By opens in {{range}}",
|
||
"mostActiveUsers": "Most active users",
|
||
"mostActiveUsersSubtitle": "By app opens in {{range}}",
|
||
"leaderboardEmpty": "No activity yet",
|
||
"openCount": "{{count}} opens",
|
||
"viewAppDetails": "View details for {{name}}",
|
||
"viewUserDetails": "View details for {{name}}",
|
||
"drillIn": {
|
||
"loading": "Loading…",
|
||
"appTitle": "Recent opens · {{name}}",
|
||
"userTitle": "Recent opens · {{name}}",
|
||
"subtitle": "{{count}} opens in {{range}}",
|
||
"empty": "No opens in this range",
|
||
"loadError": "Couldn't load recent opens.",
|
||
"editApp": "Edit app",
|
||
"viewUser": "View user",
|
||
"loadMore": "Load more",
|
||
"loadMoreError": "Couldn't load more opens.",
|
||
"shownOf": "Showing {{shown}} of {{total}}"
|
||
}
|
||
}
|
||
},
|
||
"notFound": {
|
||
"title": "404 — Page Not Found",
|
||
"description": "The page you are looking for does not exist."
|
||
},
|
||
"common": {
|
||
"save": "Save",
|
||
"cancel": "Cancel",
|
||
"delete": "Delete",
|
||
"edit": "Edit",
|
||
"add": "Add",
|
||
"close": "Close",
|
||
"loading": "Loading...",
|
||
"error": "An error occurred",
|
||
"success": "Success",
|
||
"language": "العربية",
|
||
"appName": "Tx OS"
|
||
},
|
||
"notes": {
|
||
"title": "Notes",
|
||
"searchPlaceholder": "Search notes",
|
||
"labels": "Labels",
|
||
"manageLabels": "Manage labels",
|
||
"newLabel": "New label",
|
||
"noLabelsYet": "No labels yet",
|
||
"allLabels": "All",
|
||
"tabs": { "active": "Notes", "archived": "Archived" },
|
||
"pinned": "Pinned",
|
||
"others": "Others",
|
||
"empty": "Your notes will appear here",
|
||
"emptyArchived": "No archived notes",
|
||
"takeNote": "Take a note...",
|
||
"titlePlaceholder": "Title",
|
||
"contentPlaceholder": "Take a note...",
|
||
"save": "Save",
|
||
"pin": "Pin",
|
||
"archive": "Archive",
|
||
"unarchive": "Unarchive",
|
||
"delete": "Delete",
|
||
"deleteConfirm": "Delete this note?",
|
||
"deleteLabelConfirm": "Delete this label?",
|
||
"editTitle": "Edit note"
|
||
},
|
||
"executiveMeetings": {
|
||
"titleAr": "Executive Meetings Management",
|
||
"titleEn": "إدارة الاجتماعات التنفيذية",
|
||
"scheduleHeading": "Meeting Attendance List",
|
||
"date": "Date:",
|
||
"exportPdf": "Export PDF",
|
||
"fontSettings": "Font Settings",
|
||
"noMeetings": "No meetings scheduled for this day",
|
||
"virtual": "Virtual",
|
||
"virtualAttendance": "Virtual Attendance",
|
||
"internalAttendance": "Internal Attendance",
|
||
"externalAttendance": "External Attendance",
|
||
"placeholderBody": "This section is under development and will be available in upcoming phases.",
|
||
"col": {
|
||
"number": "#",
|
||
"meeting": "Meeting",
|
||
"attendees": "Attendees",
|
||
"time": "Time"
|
||
},
|
||
"nav": {
|
||
"schedule": "Daily Schedule",
|
||
"manage": "Manage Meetings",
|
||
"requests": "Change Requests",
|
||
"approvals": "Office Manager Approvals",
|
||
"tasks": "Coordination Tasks",
|
||
"notifications": "Notifications",
|
||
"audit": "Audit Log",
|
||
"pdf": "PDF Export",
|
||
"fontSettings": "Font Settings"
|
||
},
|
||
"common": {
|
||
"save": "Save",
|
||
"cancel": "Cancel",
|
||
"delete": "Delete",
|
||
"edit": "Edit",
|
||
"add": "Add",
|
||
"submit": "Submit",
|
||
"close": "Close",
|
||
"loading": "Loading...",
|
||
"saved": "Saved",
|
||
"saveFailed": "Save failed",
|
||
"actions": "Actions",
|
||
"noPermission": "You do not have permission for this action",
|
||
"yes": "Yes",
|
||
"no": "No",
|
||
"all": "All",
|
||
"filter": "Filter"
|
||
},
|
||
"manage": {
|
||
"heading": "Manage Meetings",
|
||
"addMeeting": "Add meeting",
|
||
"editMeeting": "Edit meeting",
|
||
"deleteMeeting": "Delete meeting",
|
||
"deleteConfirm": "Permanently delete this meeting?",
|
||
"noMeetings": "No meetings on this day. Click \"Add meeting\" to create one.",
|
||
"field": {
|
||
"titleAr": "Title (Arabic)",
|
||
"titleEn": "Title (English)",
|
||
"meetingDate": "Date",
|
||
"dailyNumber": "Daily number",
|
||
"dailyNumberHint": "Leave blank for auto-numbering",
|
||
"startTime": "Start time",
|
||
"endTime": "End time",
|
||
"location": "Location",
|
||
"meetingUrl": "Meeting URL",
|
||
"platform": "Platform",
|
||
"status": "Status",
|
||
"isHighlighted": "Highlight (red)",
|
||
"notes": "Notes"
|
||
},
|
||
"attendees": {
|
||
"label": "Attendees",
|
||
"name": "Name",
|
||
"title": "Title",
|
||
"id": "Attendee ID",
|
||
"type": "Attendance type",
|
||
"add": "Add attendee",
|
||
"remove": "Remove"
|
||
},
|
||
"platform": {
|
||
"none": "None",
|
||
"webex": "Webex",
|
||
"teams": "Teams",
|
||
"zoom": "Zoom",
|
||
"other": "Other"
|
||
},
|
||
"attendanceType": {
|
||
"internal": "Internal",
|
||
"virtual": "Virtual",
|
||
"external": "External"
|
||
},
|
||
"status": {
|
||
"scheduled": "Scheduled",
|
||
"cancelled": "Cancelled",
|
||
"completed": "Completed",
|
||
"postponed": "Postponed"
|
||
},
|
||
"errors": {
|
||
"titleRequired": "Arabic title is required",
|
||
"dateRequired": "Date is required",
|
||
"duplicateNumber": "That daily number already exists for this date",
|
||
"saveFailed": "Could not save the meeting"
|
||
}
|
||
},
|
||
"requests": {
|
||
"heading": "Change Requests",
|
||
"newRequest": "New request",
|
||
"myRequests": "My requests",
|
||
"allRequests": "All requests",
|
||
"empty": "No requests yet.",
|
||
"filterStatus": "Filter by status",
|
||
"submitFor": "Suggest a change to:",
|
||
"noTargetMeeting": "Propose a brand-new meeting (no link)",
|
||
"details": "Details",
|
||
"detailsPlaceholder": "Explain your suggestion in detail...",
|
||
"submitted": "Request submitted",
|
||
"submitFailed": "Could not submit the request",
|
||
"requestedBy": "Submitted by",
|
||
"requestedAt": "Submitted at",
|
||
"type": {
|
||
"create": "Create meeting",
|
||
"edit": "Edit details",
|
||
"delete": "Delete",
|
||
"reschedule": "Reschedule",
|
||
"add_attendee": "Add attendee",
|
||
"remove_attendee": "Remove attendee",
|
||
"change_location": "Change location",
|
||
"cancel_meeting": "Cancel meeting",
|
||
"note": "Note",
|
||
"highlight": "Highlight",
|
||
"unhighlight": "Remove highlight",
|
||
"other": "Other"
|
||
},
|
||
"status": {
|
||
"new": "New",
|
||
"approved": "Approved",
|
||
"rejected": "Rejected",
|
||
"needs_edit": "Needs edit",
|
||
"withdrawn": "Withdrawn",
|
||
"done": "Done"
|
||
}
|
||
},
|
||
"approvals": {
|
||
"heading": "Office Manager Approvals",
|
||
"empty": "No requests pending review.",
|
||
"approve": "Approve",
|
||
"reject": "Reject",
|
||
"needsEdit": "Send back for edits",
|
||
"needsEditDone": "Sent back to requester",
|
||
"reviewNotes": "Review notes",
|
||
"reviewNotesPlaceholder": "Optional notes for the requester",
|
||
"approved": "Approved",
|
||
"rejected": "Rejected",
|
||
"actionFailed": "Could not complete the review"
|
||
},
|
||
"tasks": {
|
||
"heading": "Coordination Tasks",
|
||
"addTask": "Add task",
|
||
"empty": "No tasks yet.",
|
||
"field": {
|
||
"taskType": "Task type",
|
||
"assignee": "Assignee",
|
||
"dueDate": "Due date",
|
||
"notes": "Notes",
|
||
"meeting": "Meeting",
|
||
"status": "Status"
|
||
},
|
||
"status": {
|
||
"pending": "Pending",
|
||
"in_progress": "In progress",
|
||
"completed": "Completed",
|
||
"cancelled": "Cancelled"
|
||
},
|
||
"markCompleted": "Mark completed",
|
||
"markInProgress": "Start",
|
||
"deleteConfirm": "Delete this task?",
|
||
"saved": "Task saved",
|
||
"deleted": "Task deleted"
|
||
},
|
||
"audit": {
|
||
"heading": "Audit Log",
|
||
"empty": "No audit entries.",
|
||
"filterDate": "Filter by date",
|
||
"filterEntity": "Entity",
|
||
"all": "All",
|
||
"col": {
|
||
"when": "When",
|
||
"actor": "Actor",
|
||
"action": "Action",
|
||
"entity": "Entity",
|
||
"entityId": "ID",
|
||
"diff": "Details",
|
||
"changes": "Changes"
|
||
},
|
||
"created": "Created",
|
||
"removed": "Removed",
|
||
"moreChanges": "more changes",
|
||
"entity": {
|
||
"meeting": "Meeting",
|
||
"attendee": "Attendee",
|
||
"request": "Request",
|
||
"task": "Task",
|
||
"font_settings": "Font settings",
|
||
"pdf_archive": "PDF archive"
|
||
},
|
||
"action": {
|
||
"create": "Create",
|
||
"update": "Update",
|
||
"delete": "Delete",
|
||
"approve": "Approve",
|
||
"approved": "Approved",
|
||
"reject": "Reject",
|
||
"rejected": "Rejected",
|
||
"needs_edit": "Needs edit",
|
||
"withdraw": "Withdrawn",
|
||
"submit": "Submit",
|
||
"apply": "Apply",
|
||
"duplicate": "Duplicate",
|
||
"replace_attendees": "Update attendees",
|
||
"done": "Done"
|
||
}
|
||
},
|
||
"pdf": {
|
||
"heading": "Print / Export",
|
||
"intro": "This view uses the browser print dialog to produce a PDF of the day's schedule. Pick a date and click Print.",
|
||
"print": "Print + archive",
|
||
"printOnly": "Print only",
|
||
"openSchedule": "Open schedule",
|
||
"selectDate": "Schedule date",
|
||
"archiveSnapshot": "Archive snapshot",
|
||
"archived": "Snapshot archived",
|
||
"archivedAndPrinted": "Snapshot archived — print dialog opened",
|
||
"archivesHeading": "Archived snapshots",
|
||
"noArchives": "No snapshots archived for this date yet."
|
||
},
|
||
"fontSettingsPage": {
|
||
"heading": "Font Settings",
|
||
"intro": "These settings apply to the meetings schedule. You can save your personal preference; admins can also save the global default.",
|
||
"scope": "Scope",
|
||
"scopeUser": "My preference",
|
||
"scopeGlobal": "Global (everyone)",
|
||
"fontFamily": "Font family",
|
||
"fontSize": "Font size",
|
||
"fontWeight": "Weight",
|
||
"alignment": "Alignment",
|
||
"weight": {
|
||
"regular": "Regular",
|
||
"medium": "Medium",
|
||
"semibold": "Semibold",
|
||
"bold": "Bold"
|
||
},
|
||
"align": {
|
||
"start": "Start",
|
||
"center": "Center",
|
||
"end": "End"
|
||
},
|
||
"preview": "Preview",
|
||
"previewText": "Sample text 123 نص تجريبي ١٢٣",
|
||
"save": "Save settings",
|
||
"saved": "Saved",
|
||
"reset": "Reset to default"
|
||
},
|
||
"notificationsPage": {
|
||
"heading": "Notifications",
|
||
"intro": "Scheduled reminders and change-notice records for executive meetings. No actual delivery is wired up yet (placeholder).",
|
||
"empty": "No scheduled notifications.",
|
||
"col": {
|
||
"meeting": "Meeting",
|
||
"user": "User",
|
||
"type": "Type",
|
||
"scheduledAt": "Scheduled",
|
||
"status": "Status"
|
||
},
|
||
"type": {
|
||
"reminder": "Reminder",
|
||
"change": "Change notice",
|
||
"cancel": "Cancellation notice"
|
||
},
|
||
"status": {
|
||
"pending": "Pending",
|
||
"sent": "Sent",
|
||
"skipped": "Skipped",
|
||
"failed": "Failed"
|
||
}
|
||
}
|
||
}
|
||
}
|