Files
TX/artifacts/tx-os/src/locales/en.json
T
riyadhafraa e66c69bccc Task #108 — Executive Meetings Phase 2 (RBAC + audit + Zod + i18n)
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.
2026-04-28 07:35:26 +00:00

895 lines
32 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"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"
}
}
}
}