Files
TX/artifacts/tx-os/src/locales/en.json
T
riyadhafraa 2bf2f3cd3a Task #147: Add structured permission-change audit (users, groups, apps)
Mirrors the existing role-permission audit pattern with a unified
`permission_audit` table capturing actor, target, prev/new id sets, and
timestamp written in the same transaction as the change.

Schema & API
- New `permission_audit` table (target_kind, target_id, change_kind,
  actor_user_id, previous_ids[], new_ids[], created_at) with index on
  (target_kind, target_id, created_at).
- Transactional audit writes in routes/users.ts (POST/DELETE roles,
  PATCH groupIds), routes/groups.ts (PATCH + add/remove members for
  users/roles/apps), routes/apps.ts (POST/DELETE permissions).
- Cross-entity mirroring: when group membership changes via a group
  endpoint, a user.groups row is also written for each affected user
  (and vice versa via PATCH /users), so each entity's history is
  exhaustive regardless of which editor was used.
- Admin-only GET /users/:id/audit, /groups/:id/audit, /apps/:id/audit
  with limit/offset/actorUserId/from/to filters and the same response
  shape as role audit.
- OpenAPI types + codegen regenerated.

UI
- Reusable PermissionAuditHistory component in admin.tsx wired into
  UserGroupsEditor, GroupDetailEditor (new "history" tab), and the
  editing-app dialog. App history correctly resolves permission ids
  (NOT roles) via useListPermissions.
- Bilingual i18n keys added under admin.{users,groups,apps}.history*
  in en.json + ar.json.

Tests
- New backend tests: user-permission-audit, group-permission-audit,
  app-permission-audit (14 cases — transactional capture, GET filters
  & pagination, admin-only, 404 on unknown id, plus 2 new mirror
  tests covering cross-entity audit visibility). All pass; 35
  adjacent role/groups/users/audit-coverage tests still pass.

Notes
- replit.md updated to list `permission_audit` table.
- Restored opengraph.jpg (an unrelated stray binary diff).
- Code-review comments addressed: cross-entity asymmetry fixed via
  mirroring; opengraph.jpg restored.
- Follow-ups proposed: timeline UI improvements, cascade audit on
  delete/bulk paths, e2e UI test for History sections.

Replit-Task-Id: 4ba8c8de-8dfd-42c7-a3bc-964a1ed3a1a3
2026-04-30 11:58:18 +00:00

1220 lines
46 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",
"appPermissions": {
"title": "Required permissions",
"help": "Users need at least one of these to see the app. Leave empty for no restriction.",
"none": "No permissions required — visible to everyone.",
"selectPlaceholder": "Select a permission…",
"add": "Add",
"removeAria": "Remove permission {{name}}"
},
"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"
},
"review": {
"title": "Review changes",
"subtitle": "Confirm what will be saved for {{username}}.",
"noChanges": "No changes to save",
"groupsAdded": "Groups added",
"groupsRemoved": "Groups removed",
"back": "Back to edit",
"confirm": "Confirm & save"
},
"counts": {
"notes": "{{count}} notes",
"orders": "{{count}} orders",
"conversations": "{{count}} convos",
"messages": "{{count}} messages"
},
"historyTitle": "Permission history",
"historyHint": "Roles and groups assigned to this user. Filter by who changed it or when.",
"historyEmpty": "No permission changes have been recorded for this user yet.",
"historyEmptyFiltered": "No permission changes match the current filters.",
"historyAdded_one": "Added ({{count}}):",
"historyAdded_other": "Added ({{count}}):",
"historyRemoved_one": "Removed ({{count}}):",
"historyRemoved_other": "Removed ({{count}}):",
"historyTotal_one": "{{count}} entry after change",
"historyTotal_other": "{{count}} entries after change",
"history": {
"kind": {
"roles": "Roles",
"groups": "Groups"
}
}
},
"apps": {
"counts": {
"groups": "{{count}} groups",
"restrictions": "{{count}} restrictions",
"opens": "{{count}} opens"
},
"historyTitle": "Permission history",
"historyHint": "Permissions required to use this app. Filter by who changed it or when.",
"historyEmpty": "No permission changes have been recorded for this app yet.",
"historyEmptyFiltered": "No permission changes match the current filters.",
"historyAdded_one": "Added ({{count}}):",
"historyAdded_other": "Added ({{count}}):",
"historyRemoved_one": "Removed ({{count}}):",
"historyRemoved_other": "Removed ({{count}}):",
"historyTotal_one": "{{count}} permission after change",
"historyTotal_other": "{{count}} permissions after change"
},
"services": {
"counts": {
"orders": "{{count}} orders"
}
},
"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.",
"renameWarningTitle": "Renaming \"{{from}}\" to \"{{to}}\"",
"renameWarningBody": "This role's name may be referenced by code, integrations, or audit log queries. Renaming can quietly break those flows. Update any callers before saving.",
"renameUsage": "Currently used by {{users}} users and {{groups}} groups.",
"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.",
"impactTitle_one": "Removing {{count}} permission",
"impactTitle_other": "Removing {{count}} permissions",
"impactLoading": "Calculating impact...",
"impactNoUsers": "No users currently get any of these permissions through this role.",
"impactError": "Could not check who would be affected. Please adjust your selection or try again.",
"impactPerPermission": "{{users}} users would lose this ({{groups}} groups grant the role)",
"impactViaGroups": "via groups: {{groups}}",
"impactTotal_one": "{{count}} user in total would lose at least one permission.",
"impactTotal_other": "{{count}} users in total would lose at least one permission.",
"confirmRemovalTitle": "Confirm permission removal",
"confirmRemovalBody_one": "Saving will remove permissions from {{count}} user. This may revoke their access immediately. Continue?",
"confirmRemovalBody_other": "Saving will remove permissions from {{count}} users. This may revoke their access immediately. Continue?",
"confirmRemovalAction": "Remove permissions",
"historyTitle": "History",
"historyHint": "Permission changes for this role. Use the filters to narrow by who changed it or when.",
"historyEmpty": "No permission changes have been recorded for this role yet.",
"historyEmptyFiltered": "No permission changes match the current filters.",
"historyActorUnknown": "Unknown actor",
"historyAdded_one": "Added ({{count}}):",
"historyAdded_other": "Added ({{count}}):",
"historyRemoved_one": "Removed ({{count}}):",
"historyRemoved_other": "Removed ({{count}}):",
"historyTotal_one": "{{count}} permission after change",
"historyTotal_other": "{{count}} permissions after change",
"historyShowing": "Showing {{shown}} of {{total}}",
"historyLoadMore": "Load more",
"historyFilters": {
"actor": "Changed by",
"allActors": "Anyone",
"from": "From",
"to": "To",
"apply": "Apply",
"reset": "Reset"
},
"historyErrors": {
"invalidDate": "Dates must be in YYYY-MM-DD format.",
"invertedDates": "\"From\" must be on or before \"To\".",
"fixFiltersFirst": "Fix the filters above to load history."
}
},
"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",
"history": "History"
},
"historyTitle": "Permission history",
"historyHint": "Members, roles and apps attached to this group. Filter by who changed it or when.",
"historyEmpty": "No permission changes have been recorded for this group yet.",
"historyEmptyFiltered": "No permission changes match the current filters.",
"historyAdded_one": "Added ({{count}}):",
"historyAdded_other": "Added ({{count}}):",
"historyRemoved_one": "Removed ({{count}}):",
"historyRemoved_other": "Removed ({{count}}):",
"historyTotal_one": "{{count}} entry after change",
"historyTotal_other": "{{count}} entries after change",
"history": {
"kind": {
"users": "Users",
"roles": "Roles",
"apps": "Apps"
}
}
},
"audit": {
"filters": {
"action": "Action",
"allActions": "All actions",
"from": "From",
"to": "To",
"apply": "Apply",
"reset": "Reset",
"forcedOnly": "Forced deletions only",
"forcedOnlyHint": "Show only force-delete entries for groups, users, apps, and services.",
"targetType": "Target type: {{type}}",
"targetWithId": "Target: {{type}} #{{id}}",
"clearTargetFilter": "Clear target filter"
},
"target": "Target: {{type}} #{{id}}",
"targetWithName": "Target: {{type}} #{{id}} · {{name}}",
"showing": "Showing {{shown}} of {{total}}",
"empty": "No audit log entries match these filters.",
"loadMore": "Load more",
"export": {
"button": "Export CSV",
"failed": "Could not export the audit log. Please try again."
},
"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."
},
"unit": {
"member_one": "{{count}} member",
"member_other": "{{count}} members",
"app_one": "{{count}} app",
"app_other": "{{count}} apps",
"role_one": "{{count}} role",
"role_other": "{{count}} roles",
"group_one": "{{count}} group",
"group_other": "{{count}} groups",
"order_one": "{{count}} order",
"order_other": "{{count}} orders",
"change_one": "{{count}} change",
"change_other": "{{count}} changes",
"permission_one": "{{count}} permission",
"permission_other": "{{count}} permissions",
"restriction_one": "{{count}} restriction",
"restriction_other": "{{count}} restrictions",
"note_one": "{{count}} note",
"note_other": "{{count}} notes",
"conversation_one": "{{count}} conversation",
"conversation_other": "{{count}} conversations",
"message_one": "{{count}} message",
"message_other": "{{count}} messages",
"open_one": "{{count}} open",
"open_other": "{{count}} opens"
},
"dependencies": {
"label": "Cleared dependencies",
"chipAriaTargetType": "{{label}} — open audit log filtered by target type \"{{type}}\"",
"chipAriaParent": "{{label}} — open audit log for {{type}} #{{id}}"
},
"summary": {
"app": {
"create": "Created app '{{name}}'",
"update": "Updated app '{{name}}' ({{changes}})",
"rename": "Renamed app '{{previousName}}' to '{{name}}'",
"delete": "Deleted app '{{name}}'",
"forceDelete": "Force-deleted app '{{name}}' (affected {{groups}})"
},
"auth": {
"issueResetLink": "Issued password reset link for @{{username}}"
},
"group": {
"create": "Created group '{{name}}' ({{members}}, {{apps}})",
"update": "Updated group '{{name}}' ({{changes}})",
"rename": "Renamed group '{{previousName}}' to '{{name}}'",
"delete": "Deleted group '{{name}}'",
"forceDelete": "Force-deleted group '{{name}}' ({{members}})",
"userAdd": "Added @{{username}} to group '{{name}}'",
"userAddById": "Added user #{{userId}} to group '{{name}}'",
"userRemove": "Removed @{{username}} from group '{{name}}'",
"userRemoveById": "Removed user #{{userId}} from group '{{name}}'",
"appAdd": "Added app '{{appName}}' to group '{{name}}'",
"appAddById": "Added app #{{appId}} to group '{{name}}'",
"appRemove": "Removed app '{{appName}}' from group '{{name}}'",
"appRemoveById": "Removed app #{{appId}} from group '{{name}}'",
"roleAdd": "Added role '{{roleName}}' to group '{{name}}'",
"roleAddById": "Added role #{{roleId}} to group '{{name}}'",
"roleRemove": "Removed role '{{roleName}}' from group '{{name}}'",
"roleRemoveById": "Removed role #{{roleId}} from group '{{name}}'"
},
"role": {
"create": "Created role '{{name}}'",
"update": "Updated role '{{name}}' ({{changes}})",
"rename": "Renamed role '{{previousName}}' to '{{name}}'",
"delete": "Deleted role '{{name}}'",
"permissionsReplace": "Updated permissions for role '{{name}}' (added {{added}}, removed {{removed}})",
"permissionAdd": "Added permission '{{permission}}' to role '{{name}}'",
"permissionRemove": "Removed permission '{{permission}}' from role '{{name}}'"
},
"service": {
"forceDelete": "Force-deleted service '{{name}}' (affected {{orders}})"
},
"settings": {
"update": "Updated system settings ({{changes}})",
"registrationOpened": "Opened public registration",
"registrationClosed": "Closed public registration",
"registrationOpenedWith": "Opened public registration and {{others}}",
"registrationClosedWith": "Closed public registration and {{others}}"
},
"user": {
"delete": "Deleted user @{{username}}",
"forceDelete": "Force-deleted user @{{username}}"
}
}
},
"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",
"previous": "Previous",
"next": "Next",
"empty": "(empty)"
},
"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"
},
"customize": {
"title": "Customize table",
"hint": "Show or hide columns. Drag column headers to reorder.",
"reset": "Reset",
"dragHint": "Drag a column edge to resize it"
},
"highlight": {
"title": "Highlight current meeting",
"hint": "Outline the meeting whose time is now.",
"reset": "Reset to default"
},
"dragRow": "Drag to reorder",
"editMeetingTitle": "Edit meeting title",
"rowColor": {
"label": "Row color"
},
"rowActions": {
"label": "Row actions",
"back": "Back"
},
"merge": {
"label": "Merge cells",
"editLabel": "Edit merged cell",
"placeholder": "Click to add text",
"meetingAttendees": "Merge meeting + attendees",
"meetingTime": "Merge meeting + attendees + time",
"all": "Merge entire row",
"unmerge": "Unmerge"
},
"schedule": {
"addRow": "Add meeting",
"newMeetingDefaultAr": "اجتماع جديد",
"newMeetingDefaultEn": "New meeting",
"timeStart": "Start time",
"timeEnd": "End time",
"timeOrderError": "End time is before start time",
"addAttendee": "Add attendee",
"addSubheading": "+ subheading",
"addVirtualAttendee": "+ Virtual",
"addInternalAttendee": "+ Internal",
"addExternalAttendee": "+ External",
"newAttendeeAria": "New attendee name",
"editAttendeeAria": "Edit attendee name",
"newSubheadingAria": "New subheading label",
"editSubheadingAria": "Edit subheading label",
"deleteRow": "Delete meeting",
"deleteRowConfirm": "Delete this meeting?\n\n\"{{title}}\"\n\nThis cannot be undone.",
"deleted": "Meeting deleted",
"editToggle": "Edit",
"editToggleAria": "Toggle edit mode",
"editToggleOn": "Edit mode on",
"editToggleOff": "Edit mode off",
"saveToggle": "Save",
"saveToggleAria": "Finish editing and save changes",
"saveToggleOn": "Edit mode on — click to save and exit",
"saveToggleOff": "Edit mode off — click to start editing",
"bulkSelectRow": "Select meeting",
"bulkSelectAll": "Select all",
"bulkSelectionCount": "Selected: {{n}} of {{total}}",
"bulkClearSelection": "Clear selection",
"bulkDeleteSelected": "Delete selected",
"bulkDeleteConfirm": "Delete {{n}} meetings?\n\nThis cannot be undone.",
"bulkDeleteResultAll": "Deleted {{n}} meetings",
"bulkDeletePartial": "Deleted {{ok}} of {{total}} meetings; {{failed}} failed",
"bulkDeleteResultNone": "Failed to delete the selected meetings"
},
"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",
"addSubheading": "Add subheading",
"subheadingName": "Subheading label",
"subheadingBadge": "Subheading",
"remove": "Remove",
"moveUp": "Move up",
"moveDown": "Move down",
"removeAll": "Remove all attendees",
"removeAllConfirm": "Remove all {{count}} attendees?\n\nThe change applies when you save the meeting."
},
"duplicate": "Duplicate",
"duplicateToDate": "Duplicate to date",
"duplicated": "Meeting duplicated",
"duplicateFailed": "Could not duplicate the meeting",
"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",
"assigneeIdPlaceholder": "User ID",
"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",
"reassign": "Reassign / edit",
"reassigned": "Task updated",
"myTasksOnly": "My tasks only",
"myTasksOnlyHelp": "Coordinators only see tasks assigned to them. Lead/admin roles see all tasks."
},
"audit": {
"heading": "Audit Log",
"empty": "No audit entries.",
"filterDate": "Filter by date",
"filterEntity": "Entity",
"filter": {
"from": "From",
"to": "To",
"actorId": "Actor ID"
},
"pageInfo": "Showing",
"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": "PDF Export",
"intro": "Generates a real PDF of the day's schedule on the server using your saved font settings. Each download is archived.",
"downloadPdf": "Download PDF",
"downloadHint": "The PDF is generated using your font preferences (family, size, weight, alignment) with proper Arabic shaping.",
"downloaded": "PDF download started",
"selectDate": "Schedule date",
"archivesHeading": "Archived PDFs",
"noArchives": "No PDFs archived for this date yet.",
"downloadArchive": "Download",
"legacyArchive": "Legacy snapshot"
},
"print": {
"title": "Executive Meetings Schedule",
"no": "#",
"meeting": "Meeting",
"attendees": "Attendees",
"time": "Time",
"location": "Location",
"none": "No meetings for this day.",
"print": "Print",
"back": "Close",
"loading": "Loading…"
},
"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": "Real-time alerts for executive meetings — delivered to the in-app notifications panel as events happen, and emailed to approvers when a request is awaiting review.",
"empty": "No notifications yet.",
"col": {
"meeting": "Meeting",
"user": "User",
"type": "Type",
"scheduledAt": "Scheduled",
"status": "Status"
},
"type": {
"reminder": "Reminder",
"change": "Change notice",
"cancel": "Cancellation notice",
"meeting_created": "Meeting created",
"request_submitted": "Request awaiting review",
"request_approved": "Request approved",
"request_rejected": "Request rejected",
"request_needs_edit": "Request needs edits",
"task_assigned": "Task assigned",
"task_completed": "Task completed"
},
"status": {
"pending": "Pending",
"sent": "Sent",
"skipped": "Skipped",
"failed": "Failed"
}
}
}
}