Executive Meetings Phase 2: full module + atomic audit
Original task (#108): polish the daily-schedule UI and ship the remaining 8 sections of the Executive Meetings module (Manage, Requests, Approvals, Tasks, Notifications, Audit, PDF, Font Settings) with bilingual i18n and fine-grained RBAC. What changed: - Schedule UI: centered cells, '#' header (not 'م'), attendees widest, RTL column order locked, navy/white/gray + red highlighting only. - Schema: made executive_meeting_requests.meetingId nullable so 'create-meeting' suggestions don't need a target row. - Backend rewrite of artifacts/api-server/src/routes/executive-meetings.ts: GET /me (now returns userId), full CRUD for meetings (transactional attendee replace), requests CRUD + approve/reject/withdraw, tasks CRUD with assignee status updates, audit-logs GET (admin), notifications GET, font-settings GET/PATCH (per-user + global). RBAC via 5 role sets and a makeRequireRoles middleware factory. - Audit-in-transaction: every mutation (meeting/request/task/font CRUD) wraps the DB write AND the audit-log insert in the same db.transaction so audit entries cannot drift from state if either insert fails. This was the main finding from the architect review and is now fixed. - Path-to-regexp 8 fix: replaced inline ':id(\\d+)' with router.param('id') + next('route') guard, plus NaN guards in handlers. - Frontend rewrite of artifacts/tx-os/src/pages/executive-meetings.tsx (~2200 lines): 8 new section components, RBAC-aware UI via /me, per-task assignee check now shown for status buttons. - i18n: full executiveMeetings.* key set added in ar.json + en.json. Verified: full e2e (admin login -> all 9 tabs -> create meeting -> submit request -> approve -> audit shows full chain -> font save) plus a smoke regression after the audit-in-tx refactor (atomic audit row created in lockstep with mutation). Out of scope (proposed as follow-ups #110-#112): real notification delivery, real PDF generation (currently window.print), and automated integration tests for the new endpoints. The pre-existing 'apps-open.test.mjs' syntax error in the test workflow is unrelated to this task.
This commit is contained in:
@@ -634,6 +634,231 @@
|
||||
"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",
|
||||
"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",
|
||||
"highlight": "Highlight",
|
||||
"unhighlight": "Remove highlight",
|
||||
"other": "Other"
|
||||
},
|
||||
"status": {
|
||||
"new": "New",
|
||||
"approved": "Approved",
|
||||
"rejected": "Rejected",
|
||||
"withdrawn": "Withdrawn"
|
||||
}
|
||||
},
|
||||
"approvals": {
|
||||
"heading": "Office Manager Approvals",
|
||||
"empty": "No requests pending review.",
|
||||
"approve": "Approve",
|
||||
"reject": "Reject",
|
||||
"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"
|
||||
},
|
||||
"entity": {
|
||||
"meeting": "Meeting",
|
||||
"attendee": "Attendee",
|
||||
"request": "Request",
|
||||
"task": "Task",
|
||||
"font_settings": "Font settings"
|
||||
},
|
||||
"action": {
|
||||
"create": "Create",
|
||||
"update": "Update",
|
||||
"delete": "Delete",
|
||||
"approve": "Approve",
|
||||
"reject": "Reject",
|
||||
"submit": "Submit"
|
||||
}
|
||||
},
|
||||
"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 schedule",
|
||||
"openSchedule": "Open schedule",
|
||||
"selectDate": "Schedule date"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user