@promptbook/cli 0.114.0-14 → 0.114.0-18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/apps/agents-server/next.config.ts +6 -0
  2. package/apps/agents-server/src/app/admin/_components/AdminFilterFields.tsx +84 -0
  3. package/apps/agents-server/src/app/admin/_components/AdminMetricCard.tsx +36 -0
  4. package/apps/agents-server/src/app/admin/_components/AdminTaskManagerTabs.tsx +97 -0
  5. package/apps/agents-server/src/app/admin/planned-messages/PlannedMessageManagerClient.tsx +103 -0
  6. package/apps/agents-server/src/app/admin/planned-messages/PlannedMessageManagerEditDialog.tsx +232 -0
  7. package/apps/agents-server/src/app/admin/planned-messages/PlannedMessageManagerFiltersCard.tsx +195 -0
  8. package/apps/agents-server/src/app/admin/planned-messages/PlannedMessageManagerRow.tsx +189 -0
  9. package/apps/agents-server/src/app/admin/planned-messages/PlannedMessageManagerSummaryMetrics.tsx +60 -0
  10. package/apps/agents-server/src/app/admin/planned-messages/PlannedMessageManagerTableCard.tsx +147 -0
  11. package/apps/agents-server/src/app/admin/planned-messages/page.tsx +17 -0
  12. package/apps/agents-server/src/app/admin/planned-messages/plannedMessageEditForm.ts +186 -0
  13. package/apps/agents-server/src/app/admin/planned-messages/plannedMessageManagerDialogs.ts +46 -0
  14. package/apps/agents-server/src/app/admin/planned-messages/plannedMessageManagerPresentation.tsx +188 -0
  15. package/apps/agents-server/src/app/admin/planned-messages/resolvePlannedMessageSortValue.ts +115 -0
  16. package/apps/agents-server/src/app/admin/planned-messages/usePlannedMessageManagerData.ts +116 -0
  17. package/apps/agents-server/src/app/admin/planned-messages/usePlannedMessageManagerState.ts +289 -0
  18. package/apps/agents-server/src/app/admin/task-manager/TaskManagerClient.tsx +7 -2
  19. package/apps/agents-server/src/app/admin/task-manager/TaskManagerFiltersCard.tsx +7 -73
  20. package/apps/agents-server/src/app/admin/task-manager/TaskManagerSummaryMetrics.tsx +5 -30
  21. package/apps/agents-server/src/app/admin/task-manager/[taskId]/TaskManagerTaskDetailClient.tsx +1 -1
  22. package/apps/agents-server/src/app/agents/[agentName]/api/book/reference-diagnostics/route.ts +30 -7
  23. package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +1 -0
  24. package/apps/agents-server/src/app/agents/[agentName]/api/book/test.http +3 -3
  25. package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorMissingReferences.tsx +106 -12
  26. package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +3 -0
  27. package/apps/agents-server/src/app/agents/[agentName]/book/useBookEditorDiagnostics.ts +12 -0
  28. package/apps/agents-server/src/app/agents/[agentName]/book/useBookEditorWrapper.tsx +2 -0
  29. package/apps/agents-server/src/app/agents/[agentName]/chat/AgentGoalChatPlannedMessages.tsx +46 -3
  30. package/apps/agents-server/src/app/api/admin/default-agents/reinstate/route.ts +5 -1
  31. package/apps/agents-server/src/app/api/admin/planned-messages/[timeoutId]/route.ts +80 -0
  32. package/apps/agents-server/src/app/api/admin/planned-messages/route.ts +19 -0
  33. package/apps/agents-server/src/app/api/internal/agent-goal-chat-planned-messages/route.ts +30 -2
  34. package/apps/agents-server/src/components/DefaultAgents/ReinstateBundledAgentsButton.tsx +7 -0
  35. package/apps/agents-server/src/components/DefaultAgents/coreAgentsAdminRoute.ts +6 -0
  36. package/apps/agents-server/src/components/Header/buildHeaderSystemMenuItems.ts +7 -0
  37. package/apps/agents-server/src/components/Homepage/DefaultAgentsStatusNotice.tsx +1 -7
  38. package/apps/agents-server/src/database/migrations/2026-08-1800-user-chat-timeout-schedule.sql +11 -0
  39. package/apps/agents-server/src/instrumentation-node.ts +18 -0
  40. package/apps/agents-server/src/languages/ServerTranslationKeys.ts +4 -0
  41. package/apps/agents-server/src/languages/translations/czech.yaml +4 -0
  42. package/apps/agents-server/src/languages/translations/english.yaml +4 -0
  43. package/apps/agents-server/src/tools/agentGoalChatTimeoutToolFunctions.ts +62 -15
  44. package/apps/agents-server/src/tools/agentGoalChatTimeoutTools.ts +63 -9
  45. package/apps/agents-server/src/utils/adminEntityLookups/loadAgentNamesByPermanentId.ts +45 -0
  46. package/apps/agents-server/src/utils/adminEntityLookups/loadUsernamesByUserId.ts +36 -0
  47. package/apps/agents-server/src/utils/adminEntityLookups.ts +2 -0
  48. package/apps/agents-server/src/utils/agentGoalChat/agentGoalChatPlannedMessageActions.ts +299 -52
  49. package/apps/agents-server/src/utils/agentGoalChat/createAgentGoalChatNoteContent.ts +45 -17
  50. package/apps/agents-server/src/utils/agentGoalChat.ts +1 -0
  51. package/apps/agents-server/src/utils/agentProjects/agentProjectIdentity.ts +46 -0
  52. package/apps/agents-server/src/utils/agentProjects/agentProjectRuntimeAutostartScheduler.ts +162 -0
  53. package/apps/agents-server/src/utils/agentProjects/agentProjectRuntimeDesiredState.ts +252 -0
  54. package/apps/agents-server/src/utils/agentProjects/agentProjectRuntimeDisplay.ts +6 -3
  55. package/apps/agents-server/src/utils/agentProjects/agentProjectRuntimeDomains.ts +2 -44
  56. package/apps/agents-server/src/utils/agentProjects/agentProjectRuntimePaths.ts +25 -0
  57. package/apps/agents-server/src/utils/agentProjects/agentProjectRuntimePm2.ts +4 -3
  58. package/apps/agents-server/src/utils/agentProjects/agentProjectRuntimeRegistry.ts +77 -24
  59. package/apps/agents-server/src/utils/agentProjects/agentProjectStateMutationQueue.ts +58 -0
  60. package/apps/agents-server/src/utils/agentProjects/listAllLocalAgentProjectIdentities.ts +48 -0
  61. package/apps/agents-server/src/utils/agentProjects/resolveAgentProjectRuntimeEnvironmentFlag.ts +23 -0
  62. package/apps/agents-server/src/utils/agentProjects/resolveAgentProjectRuntimeStatus.ts +73 -0
  63. package/apps/agents-server/src/utils/agentProjects/startDefaultAgentProjectRuntimes.ts +110 -0
  64. package/apps/agents-server/src/utils/agentReferenceResolver/MissingCoreAgentRecovery.ts +19 -0
  65. package/apps/agents-server/src/utils/agentReferenceResolver/bookScopedAgentReferences.ts +7 -0
  66. package/apps/agents-server/src/utils/agentReferenceResolver/createAgentInheritanceDiagnostics.ts +457 -0
  67. package/apps/agents-server/src/utils/agentReferenceResolver/createUnresolvedAgentReferenceDiagnostics.ts +27 -0
  68. package/apps/agents-server/src/utils/agentReferenceResolver/resolveCoreAgentAwareMissingReferences.ts +156 -0
  69. package/apps/agents-server/src/utils/chatMessageChips/createAnsweredMessageChipToolCalls.ts +15 -3
  70. package/apps/agents-server/src/utils/chatMessageChips/createPlannedMessageChipToolCalls.ts +8 -3
  71. package/apps/agents-server/src/utils/chatMessageChips/createTouchedExternalSourceChipToolCalls.ts +39 -0
  72. package/apps/agents-server/src/utils/chatMessageChips/createTouchedProjectChipResult.ts +45 -0
  73. package/apps/agents-server/src/utils/chatMessageChips/createTouchedProjectChipToolCalls.ts +58 -24
  74. package/apps/agents-server/src/utils/cronExpression/CronExpression.ts +63 -0
  75. package/apps/agents-server/src/utils/cronExpression/normalizeCronExpression.ts +34 -0
  76. package/apps/agents-server/src/utils/cronExpression/normalizeCronExpressionWhitespace.ts +11 -0
  77. package/apps/agents-server/src/utils/cronExpression/parseCronExpression.ts +41 -0
  78. package/apps/agents-server/src/utils/cronExpression/parseCronExpressionField.ts +140 -0
  79. package/apps/agents-server/src/utils/cronExpression/resolveNextCronRun.ts +100 -0
  80. package/apps/agents-server/src/utils/cronExpression/throwInvalidCronExpressionField.ts +20 -0
  81. package/apps/agents-server/src/utils/cronExpression.ts +10 -0
  82. package/apps/agents-server/src/utils/explicitFromCommitment.ts +130 -0
  83. package/apps/agents-server/src/utils/getAdminChatTasksResponse/getAdminChatTasks/loadAdminChatTaskFallbackData.ts +3 -77
  84. package/apps/agents-server/src/utils/localChatRunner/applyLocalAgentPlannedMessageCommands.ts +53 -4
  85. package/apps/agents-server/src/utils/localChatRunner/prepareLocalAgentPlannedMessagesSidecar.ts +15 -7
  86. package/apps/agents-server/src/utils/localChatRunner/processLocalUserChatJob.ts +8 -0
  87. package/apps/agents-server/src/utils/manGoOnboarding/manGoOnboardingAgentBooks.ts +5 -11
  88. package/apps/agents-server/src/utils/manGoOnboarding/manGoOnboardingAgentRuntime.ts +2 -2
  89. package/apps/agents-server/src/utils/managementApi/managementApiSchemas.ts +2 -2
  90. package/apps/agents-server/src/utils/plannedMessageManager/cancelManagedPlannedMessage.ts +35 -0
  91. package/apps/agents-server/src/utils/plannedMessageManager/collectPlannedMessageAgentOptions.ts +56 -0
  92. package/apps/agents-server/src/utils/plannedMessageManager/createPlannedMessageManagerCounters.ts +104 -0
  93. package/apps/agents-server/src/utils/plannedMessageManager/filterPlannedMessages.ts +191 -0
  94. package/apps/agents-server/src/utils/plannedMessageManager/getPlannedMessageManagerResponse.ts +55 -0
  95. package/apps/agents-server/src/utils/plannedMessageManager/isPlannedMessageStillPlanned.ts +18 -0
  96. package/apps/agents-server/src/utils/plannedMessageManager/loadPlannedMessageManagerRecord.ts +32 -0
  97. package/apps/agents-server/src/utils/plannedMessageManager/mapPlannedMessageManagerRecord.ts +73 -0
  98. package/apps/agents-server/src/utils/plannedMessageManager/parsePlannedMessageManagerUpdateRequest.ts +76 -0
  99. package/apps/agents-server/src/utils/plannedMessageManager/resolvePlannedMessageEndReason.ts +59 -0
  100. package/apps/agents-server/src/utils/plannedMessageManager/resolvePlannedMessageLifecycle.ts +99 -0
  101. package/apps/agents-server/src/utils/plannedMessageManager/resolvePlannedMessageRecurrenceKind.ts +40 -0
  102. package/apps/agents-server/src/utils/plannedMessageManager/updateManagedPlannedMessage.ts +121 -0
  103. package/apps/agents-server/src/utils/plannedMessagesAdmin.ts +200 -0
  104. package/apps/agents-server/src/utils/resolveInheritedAgentSource.ts +114 -95
  105. package/apps/agents-server/src/utils/userChatClient.ts +4 -0
  106. package/apps/agents-server/src/utils/userChatTimeout/UserChatTimeoutRecord.ts +37 -1
  107. package/apps/agents-server/src/utils/userChatTimeout/agentScopedTimeoutBulkActions.ts +11 -17
  108. package/apps/agents-server/src/utils/userChatTimeout/createTimeoutWakeUpMessage.ts +37 -13
  109. package/apps/agents-server/src/utils/userChatTimeout/plannedMessageSchedule/PlannedMessageSchedule.ts +52 -0
  110. package/apps/agents-server/src/utils/userChatTimeout/plannedMessageSchedule/hasPlannedMessageRecurrence.ts +13 -0
  111. package/apps/agents-server/src/utils/userChatTimeout/plannedMessageSchedule/isPlannedMessageScheduleFinished.ts +30 -0
  112. package/apps/agents-server/src/utils/userChatTimeout/plannedMessageSchedule/normalizePlannedMessageScheduleValues.ts +62 -0
  113. package/apps/agents-server/src/utils/userChatTimeout/plannedMessageSchedule/parsePlannedMessageSchedule.ts +202 -0
  114. package/apps/agents-server/src/utils/userChatTimeout/plannedMessageSchedule/resolvePlannedMessageDueAt.ts +121 -0
  115. package/apps/agents-server/src/utils/userChatTimeout/plannedMessageSchedule.ts +12 -0
  116. package/apps/agents-server/src/utils/userChatTimeout/setAgentScopedUserChatTimeoutPausedState.ts +66 -0
  117. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/createUserChatTimeout.ts +76 -5
  118. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/listAllUserChatTimeouts.ts +37 -0
  119. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/mapUserChatTimeoutRow.ts +9 -0
  120. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/repeatFiredUserChatTimeout.ts +19 -9
  121. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/updateAgentScopedUserChatTimeout.ts +39 -6
  122. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore.ts +1 -0
  123. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutWorker.ts +110 -11
  124. package/apps/agents-server/src/utils/userChatTimeout.ts +16 -0
  125. package/apps/agents-server/src/utils/vpsSelfUpdate/vpsSelfUpdateCron.ts +23 -316
  126. package/apps/agents-server/tests/e2e/support/AgentManagementApi.ts +3 -3
  127. package/esm/index.es.js +814 -58
  128. package/esm/index.es.js.map +1 -1
  129. package/esm/scripts/run-agent-messages/git/commitAgentProjectChanges.d.ts +15 -0
  130. package/esm/scripts/run-agent-messages/git/commitAnsweredMessageProjectChanges.d.ts +24 -0
  131. package/esm/scripts/run-agent-messages/git/ensureAgentProjectGitRepository.d.ts +20 -0
  132. package/esm/scripts/run-agent-messages/git/readAgentProjectCommitChange.d.ts +11 -0
  133. package/esm/scripts/run-agent-messages/git/runAgentProjectGitCommand.d.ts +29 -0
  134. package/esm/scripts/run-agent-messages/messages/buildAgentGoalChatPromptSection.d.ts +3 -3
  135. package/esm/scripts/run-agent-messages/messages/listAgentProjectDirectoryNames.d.ts +7 -0
  136. package/esm/scripts/run-agent-messages/messages/resolveAnsweredMessageTouches.d.ts +29 -0
  137. package/esm/src/book-3.0/AgentMessageRunReport.d.ts +18 -0
  138. package/esm/src/book-3.0/AgentPlannedMessagesSidecar.d.ts +38 -5
  139. package/esm/src/book-3.0/describeAgentPlannedMessageSchedule.d.ts +31 -0
  140. package/esm/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.d.ts +1 -9
  141. package/esm/src/book-components/BookEditor/createDuplicateFromCommitmentDiagnostics.d.ts +28 -0
  142. package/esm/src/book-components/BookEditor/createDuplicateFromCommitmentDiagnostics.test.d.ts +1 -0
  143. package/esm/src/book-components/Chat/Chat/renderAgentProjectDiff.d.ts +13 -0
  144. package/esm/src/book-components/Chat/Chat/renderAgentProjectToolCallDetails.d.ts +16 -0
  145. package/esm/src/book-components/Chat/utils/agentProjectToolCall.d.ts +39 -0
  146. package/esm/src/book-components/Chat/utils/agentProjectToolCall.test.d.ts +1 -0
  147. package/esm/src/book-components/Chat/utils/externalSourceToolCall.d.ts +38 -0
  148. package/esm/src/book-components/Chat/utils/timeoutToolCallPresentation.d.ts +6 -0
  149. package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +15 -0
  150. package/esm/src/utils/agent-message-runtime/AgentMessageProjectChange.d.ts +76 -0
  151. package/esm/src/utils/agent-message-runtime/AgentMessageTouchedExternalSource.d.ts +45 -0
  152. package/esm/src/utils/agent-message-runtime/parseAgentMessageRuntimeLogEvents.d.ts +3 -0
  153. package/esm/src/utils/agent-message-runtime/resolveAgentMessageTouchedExternalSources.d.ts +18 -0
  154. package/esm/src/utils/agent-message-runtime/resolveAgentMessageTouchedExternalSources.test.d.ts +1 -0
  155. package/esm/src/utils/random/$generateBookBoilerplate.test.d.ts +1 -0
  156. package/esm/src/version.d.ts +1 -1
  157. package/package.json +1 -1
  158. package/src/book-2.0/agent-source/string_book.ts +1 -1
  159. package/src/book-2.0/book-language-documentation/czechBookLanguageManualDictionary.ts +6 -1
  160. package/src/book-2.0/book-language-documentation/englishBookLanguageManualDictionary.ts +6 -1
  161. package/src/book-3.0/AgentMessageRunReport.ts +59 -0
  162. package/src/book-3.0/AgentPlannedMessagesSidecar.ts +64 -9
  163. package/src/book-3.0/describeAgentPlannedMessageSchedule.ts +94 -0
  164. package/src/book-components/BookEditor/BookEditorMonaco.tsx +2 -0
  165. package/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.ts +3 -11
  166. package/src/book-components/BookEditor/createDuplicateFromCommitmentDiagnostics.ts +71 -0
  167. package/src/book-components/Chat/Chat/Chat.module.css +165 -18
  168. package/src/book-components/Chat/Chat/ChatMessageMap.tsx +0 -1
  169. package/src/book-components/Chat/Chat/renderAgentProjectDiff.tsx +85 -0
  170. package/src/book-components/Chat/Chat/renderAgentProjectToolCallDetails.tsx +206 -0
  171. package/src/book-components/Chat/Chat/renderToolCallDetails.tsx +8 -0
  172. package/src/book-components/Chat/MarkdownContent/MarkdownContent.tsx +2 -3
  173. package/src/book-components/Chat/utils/agentProjectToolCall.ts +82 -5
  174. package/src/book-components/Chat/utils/externalSourceToolCall.ts +65 -0
  175. package/src/book-components/Chat/utils/getToolCallChipletInfo.ts +36 -0
  176. package/src/book-components/Chat/utils/renderMarkdown.ts +3 -10
  177. package/src/book-components/Chat/utils/timeoutToolCallPresentation.ts +12 -1
  178. package/src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.ts +3 -1
  179. package/src/cli/cli-commands/common/harness/$checkHarnessInstallation.ts +3 -1
  180. package/src/cli/cli-commands/common/harnessUpdateCliOptions.ts +1 -4
  181. package/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.ts +47 -3
  182. package/src/commands/FORMAT/formatCommandParser.ts +4 -2
  183. package/src/commitments/FROM/FROM.ts +13 -0
  184. package/src/other/templates/getTemplatesPipelineCollection.ts +793 -813
  185. package/src/utils/agent-message-runtime/AgentMessageProjectChange.ts +183 -0
  186. package/src/utils/agent-message-runtime/AgentMessageTouchedExternalSource.ts +80 -0
  187. package/src/utils/agent-message-runtime/parseAgentMessageRuntimeLogEvents.ts +3 -0
  188. package/src/utils/agent-message-runtime/resolveAgentMessageRuntimeActivity.ts +2 -4
  189. package/src/utils/agent-message-runtime/resolveAgentMessageTouchedExternalSources.ts +341 -0
  190. package/src/utils/random/$generateBookBoilerplate.ts +1 -1
  191. package/src/version.ts +2 -2
  192. package/src/versions.txt +4 -0
  193. package/umd/index.umd.js +813 -57
  194. package/umd/index.umd.js.map +1 -1
  195. package/umd/scripts/run-agent-messages/git/commitAgentProjectChanges.d.ts +15 -0
  196. package/umd/scripts/run-agent-messages/git/commitAnsweredMessageProjectChanges.d.ts +24 -0
  197. package/umd/scripts/run-agent-messages/git/ensureAgentProjectGitRepository.d.ts +20 -0
  198. package/umd/scripts/run-agent-messages/git/readAgentProjectCommitChange.d.ts +11 -0
  199. package/umd/scripts/run-agent-messages/git/runAgentProjectGitCommand.d.ts +29 -0
  200. package/umd/scripts/run-agent-messages/messages/buildAgentGoalChatPromptSection.d.ts +3 -3
  201. package/umd/scripts/run-agent-messages/messages/listAgentProjectDirectoryNames.d.ts +7 -0
  202. package/umd/scripts/run-agent-messages/messages/resolveAnsweredMessageTouches.d.ts +29 -0
  203. package/umd/src/book-3.0/AgentMessageRunReport.d.ts +18 -0
  204. package/umd/src/book-3.0/AgentPlannedMessagesSidecar.d.ts +38 -5
  205. package/umd/src/book-3.0/describeAgentPlannedMessageSchedule.d.ts +31 -0
  206. package/umd/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.d.ts +1 -9
  207. package/umd/src/book-components/BookEditor/createDuplicateFromCommitmentDiagnostics.d.ts +28 -0
  208. package/umd/src/book-components/BookEditor/createDuplicateFromCommitmentDiagnostics.test.d.ts +1 -0
  209. package/umd/src/book-components/Chat/Chat/renderAgentProjectDiff.d.ts +13 -0
  210. package/umd/src/book-components/Chat/Chat/renderAgentProjectToolCallDetails.d.ts +16 -0
  211. package/umd/src/book-components/Chat/utils/agentProjectToolCall.d.ts +39 -0
  212. package/umd/src/book-components/Chat/utils/agentProjectToolCall.test.d.ts +1 -0
  213. package/umd/src/book-components/Chat/utils/externalSourceToolCall.d.ts +38 -0
  214. package/umd/src/book-components/Chat/utils/timeoutToolCallPresentation.d.ts +6 -0
  215. package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +15 -0
  216. package/umd/src/utils/agent-message-runtime/AgentMessageProjectChange.d.ts +76 -0
  217. package/umd/src/utils/agent-message-runtime/AgentMessageTouchedExternalSource.d.ts +45 -0
  218. package/umd/src/utils/agent-message-runtime/parseAgentMessageRuntimeLogEvents.d.ts +3 -0
  219. package/umd/src/utils/agent-message-runtime/resolveAgentMessageTouchedExternalSources.d.ts +18 -0
  220. package/umd/src/utils/agent-message-runtime/resolveAgentMessageTouchedExternalSources.test.d.ts +1 -0
  221. package/umd/src/utils/random/$generateBookBoilerplate.test.d.ts +1 -0
  222. package/umd/src/version.d.ts +1 -1
  223. package/apps/agents-server/src/app/admin/task-manager/TaskManagerScopeTabs.tsx +0 -74
  224. package/esm/scripts/run-agent-messages/messages/resolveTouchedAgentProjects.d.ts +0 -15
  225. package/umd/scripts/run-agent-messages/messages/resolveTouchedAgentProjects.d.ts +0 -15
@@ -0,0 +1,195 @@
1
+ 'use client';
2
+
3
+ import { resolvePlannedMessageAgentOptionLabel } from '@/src/utils/plannedMessageManager/collectPlannedMessageAgentOptions';
4
+ import { resolvePlannedMessageManagerViewCount } from '@/src/utils/plannedMessageManager/createPlannedMessageManagerCounters';
5
+ import type { PlannedMessageManagerView } from '@/src/utils/plannedMessagesAdmin';
6
+ import { Card } from '../../../components/Homepage/Card';
7
+ import { AdminFilterField, AdminFilterSelectField, type AdminFilterSelectOption } from '../_components/AdminFilterFields';
8
+ import { ANY_PLANNED_MESSAGE_FILTER_VALUE, type UsePlannedMessageManagerStateResult } from './usePlannedMessageManagerState';
9
+
10
+ /**
11
+ * One view offered by the planned-message manager.
12
+ *
13
+ * @private function of PlannedMessageManagerFiltersCard
14
+ */
15
+ type PlannedMessageViewOption = {
16
+ readonly id: PlannedMessageManagerView;
17
+ readonly label: string;
18
+ readonly description: string;
19
+ };
20
+
21
+ /**
22
+ * Views offered by the planned-message manager, in the order a plan moves through them.
23
+ *
24
+ * @private function of PlannedMessageManagerFiltersCard
25
+ */
26
+ const PLANNED_MESSAGE_VIEW_OPTIONS: ReadonlyArray<PlannedMessageViewOption> = [
27
+ { id: 'active', label: 'Active', description: 'Everything still planned' },
28
+ { id: 'scheduled', label: 'Scheduled', description: 'Wake-up ahead' },
29
+ { id: 'not-started', label: 'Not started', description: 'Starts later' },
30
+ { id: 'ongoing', label: 'Ongoing', description: 'Waking an agent now' },
31
+ { id: 'paused', label: 'Paused', description: 'Held back' },
32
+ { id: 'finished', label: 'Finished', description: 'Cancelled or over' },
33
+ { id: 'all', label: 'All', description: 'Whole history' },
34
+ ];
35
+
36
+ /**
37
+ * Frequency filter options.
38
+ *
39
+ * @private function of PlannedMessageManagerFiltersCard
40
+ */
41
+ const PLANNED_MESSAGE_RECURRENCE_OPTIONS: ReadonlyArray<AdminFilterSelectOption> = [
42
+ { value: ANY_PLANNED_MESSAGE_FILTER_VALUE, label: 'Any frequency' },
43
+ { value: 'INTERVAL', label: 'Repeats on an interval' },
44
+ { value: 'CRON', label: 'Repeats on a cron' },
45
+ { value: 'ONCE', label: 'Wakes up once' },
46
+ ];
47
+
48
+ /**
49
+ * Last-run filter options.
50
+ *
51
+ * @private function of PlannedMessageManagerFiltersCard
52
+ */
53
+ const PLANNED_MESSAGE_LAST_RUN_OPTIONS: ReadonlyArray<AdminFilterSelectOption> = [
54
+ { value: ANY_PLANNED_MESSAGE_FILTER_VALUE, label: 'Any last run' },
55
+ { value: 'never', label: 'Never ran' },
56
+ { value: 'hour', label: 'Last hour' },
57
+ { value: 'day', label: 'Last 24 hours' },
58
+ { value: 'week', label: 'Last 7 days' },
59
+ { value: 'older', label: 'Older than 7 days' },
60
+ ];
61
+
62
+ /**
63
+ * Page-size options.
64
+ *
65
+ * @private function of PlannedMessageManagerFiltersCard
66
+ */
67
+ const PLANNED_MESSAGE_PAGE_SIZE_OPTIONS: ReadonlyArray<AdminFilterSelectOption> = [
68
+ { value: '25', label: '25' },
69
+ { value: '50', label: '50' },
70
+ { value: '100', label: '100' },
71
+ ];
72
+
73
+ /**
74
+ * Auto-refresh presets.
75
+ *
76
+ * @private function of PlannedMessageManagerFiltersCard
77
+ */
78
+ const PLANNED_MESSAGE_POLL_INTERVAL_OPTIONS: ReadonlyArray<AdminFilterSelectOption> = [
79
+ { value: '5000', label: '5s' },
80
+ { value: '10000', label: '10s' },
81
+ { value: '30000', label: '30s' },
82
+ { value: '0', label: 'Off' },
83
+ ];
84
+
85
+ /**
86
+ * Props for the planned-message filter card.
87
+ *
88
+ * @private function of PlannedMessageManagerClient
89
+ */
90
+ type PlannedMessageManagerFiltersCardProps = {
91
+ readonly state: UsePlannedMessageManagerStateResult;
92
+ readonly lastRefreshedLabel: string;
93
+ };
94
+
95
+ /**
96
+ * Renders the planned-message views and the parameters the listing is narrowed by.
97
+ *
98
+ * @private function of PlannedMessageManagerClient
99
+ */
100
+ export function PlannedMessageManagerFiltersCard({ state, lastRefreshedLabel }: PlannedMessageManagerFiltersCardProps) {
101
+ const agentSelectOptions: ReadonlyArray<AdminFilterSelectOption> = [
102
+ { value: ANY_PLANNED_MESSAGE_FILTER_VALUE, label: 'Every agent' },
103
+ ...state.agentOptions.map((agentOption) => ({
104
+ value: agentOption.agentPermanentId,
105
+ label: `${resolvePlannedMessageAgentOptionLabel(agentOption)} (${agentOption.plannedMessageCount})`,
106
+ })),
107
+ ];
108
+
109
+ return (
110
+ <Card className="hover:border-gray-200 hover:shadow-md">
111
+ <div className="flex flex-col gap-4">
112
+ <div className="flex flex-wrap gap-2">
113
+ {PLANNED_MESSAGE_VIEW_OPTIONS.map((viewOption) => (
114
+ <button
115
+ key={viewOption.id}
116
+ type="button"
117
+ onClick={() => state.selectView(viewOption.id)}
118
+ className={`rounded-xl border px-4 py-2 text-left transition ${
119
+ viewOption.id === state.filters.view
120
+ ? 'border-blue-500 bg-blue-50 text-blue-700 shadow-sm'
121
+ : 'border-gray-200 bg-white text-gray-700 hover:border-gray-300 hover:bg-gray-50'
122
+ }`}
123
+ >
124
+ <div className="text-sm font-semibold">
125
+ {viewOption.label} ({resolvePlannedMessageManagerViewCount(state.counters, viewOption.id)})
126
+ </div>
127
+ <div className="text-[11px] text-current/70">{viewOption.description}</div>
128
+ </button>
129
+ ))}
130
+ </div>
131
+
132
+ <div className="grid gap-4 lg:grid-cols-[minmax(0,2fr)_repeat(5,minmax(0,1fr))]">
133
+ <AdminFilterField label="Search" htmlFor="planned-message-search">
134
+ <input
135
+ id="planned-message-search"
136
+ type="text"
137
+ value={state.searchInput}
138
+ onChange={(event) => state.updateSearchInput(event.target.value)}
139
+ placeholder="Message / timeout id / agent / chat id / user"
140
+ className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
141
+ />
142
+ </AdminFilterField>
143
+
144
+ <AdminFilterSelectField
145
+ id="planned-message-agent"
146
+ label="Agent"
147
+ value={state.filters.agentPermanentId}
148
+ onChange={state.updateAgentPermanentId}
149
+ options={agentSelectOptions}
150
+ />
151
+
152
+ <AdminFilterSelectField
153
+ id="planned-message-recurrence"
154
+ label="Frequency"
155
+ value={state.filters.recurrence}
156
+ onChange={state.updateRecurrence}
157
+ options={PLANNED_MESSAGE_RECURRENCE_OPTIONS}
158
+ />
159
+
160
+ <AdminFilterSelectField
161
+ id="planned-message-last-run"
162
+ label="Last run"
163
+ value={state.filters.lastRun}
164
+ onChange={state.updateLastRun}
165
+ options={PLANNED_MESSAGE_LAST_RUN_OPTIONS}
166
+ />
167
+
168
+ <AdminFilterSelectField
169
+ id="planned-message-page-size"
170
+ label="Page size"
171
+ value={String(state.pageSize)}
172
+ onChange={state.updatePageSize}
173
+ options={PLANNED_MESSAGE_PAGE_SIZE_OPTIONS}
174
+ />
175
+
176
+ <AdminFilterSelectField
177
+ id="planned-message-poll-interval"
178
+ label="Auto-refresh"
179
+ value={String(state.data.pollIntervalMs)}
180
+ onChange={state.data.updatePollIntervalMs}
181
+ options={PLANNED_MESSAGE_POLL_INTERVAL_OPTIONS}
182
+ />
183
+ </div>
184
+
185
+ <div className="flex flex-wrap items-center justify-between gap-2 text-xs text-gray-500">
186
+ <span>{lastRefreshedLabel}</span>
187
+ <span>
188
+ A planned message is listed until it is cancelled or its plan is over, so a finished plan stays
189
+ readable here.
190
+ </span>
191
+ </div>
192
+ </div>
193
+ </Card>
194
+ );
195
+ }
@@ -0,0 +1,189 @@
1
+ 'use client';
2
+
3
+ import type { ServerLanguageCode } from '@/src/languages/ServerLanguageRegistry';
4
+ import { buildAgentProfileHref, buildExistingAgentChatHref } from '@/src/utils/agentRouting/agentRouteHrefs';
5
+ import { isPlannedMessageStillPlanned } from '@/src/utils/plannedMessageManager/isPlannedMessageStillPlanned';
6
+ import type { PlannedMessageManagerRecord } from '@/src/utils/plannedMessagesAdmin';
7
+ import { ArrowUpRight, MessageSquareText } from 'lucide-react';
8
+ import Link from 'next/link';
9
+ import type { PlannedMessageActionKind } from './usePlannedMessageManagerState';
10
+ import {
11
+ describePlannedMessagePlan,
12
+ formatPlannedMessageDateTime,
13
+ formatPlannedMessageEndReason,
14
+ formatPlannedMessageFrequency,
15
+ formatPlannedMessageRunCount,
16
+ PlannedMessageGoalChatBadge,
17
+ PlannedMessageLifecycleBadge,
18
+ resolvePlannedMessageText,
19
+ } from './plannedMessageManagerPresentation';
20
+
21
+ /**
22
+ * Props for one planned-message table row.
23
+ *
24
+ * @private function of PlannedMessageManagerTableCard
25
+ */
26
+ type PlannedMessageManagerRowProps = {
27
+ readonly plannedMessage: PlannedMessageManagerRecord;
28
+ readonly language: ServerLanguageCode;
29
+ readonly busyTimeoutId: string | null;
30
+ readonly busyAction: PlannedMessageActionKind | null;
31
+ readonly onEdit: (plannedMessage: PlannedMessageManagerRecord) => void;
32
+ readonly onTogglePaused: (plannedMessage: PlannedMessageManagerRecord) => Promise<void>;
33
+ readonly onCancel: (plannedMessage: PlannedMessageManagerRecord) => Promise<void>;
34
+ };
35
+
36
+ /**
37
+ * Shared classes of the small row action buttons.
38
+ *
39
+ * @private function of PlannedMessageManagerRow
40
+ */
41
+ const PLANNED_MESSAGE_ACTION_BUTTON_CLASS_NAME =
42
+ 'rounded-md border px-2.5 py-1 text-[11px] font-semibold disabled:cursor-not-allowed disabled:opacity-50';
43
+
44
+ /**
45
+ * Renders one planned message with its plan, its state, and what can be done to it.
46
+ *
47
+ * @private function of PlannedMessageManagerTableCard
48
+ */
49
+ export function PlannedMessageManagerRow({
50
+ plannedMessage,
51
+ language,
52
+ busyTimeoutId,
53
+ busyAction,
54
+ onEdit,
55
+ onTogglePaused,
56
+ onCancel,
57
+ }: PlannedMessageManagerRowProps) {
58
+ const isStillPlanned = isPlannedMessageStillPlanned(plannedMessage.lifecycle);
59
+ const isFiringNow = plannedMessage.lifecycle === 'ONGOING';
60
+ const isBusy = busyTimeoutId === plannedMessage.timeoutId;
61
+ const isPaused = plannedMessage.pausedAt !== null;
62
+ const endReasonLabel = formatPlannedMessageEndReason(plannedMessage.endReason);
63
+
64
+ /**
65
+ * Says whether one action of this very row is running right now.
66
+ */
67
+ function isRunningAction(action: PlannedMessageActionKind): boolean {
68
+ return isBusy && busyAction === action;
69
+ }
70
+
71
+ return (
72
+ <tr className="align-top">
73
+ <td className="px-4 py-3">
74
+ <div className="max-w-md text-[12px] font-medium text-gray-900">
75
+ {resolvePlannedMessageText(plannedMessage)}
76
+ </div>
77
+ <div className="mt-1 flex flex-wrap items-center gap-2">
78
+ <span className="break-all font-mono text-[11px] text-gray-500">{plannedMessage.timeoutId}</span>
79
+ <PlannedMessageGoalChatBadge plannedMessage={plannedMessage} />
80
+ </div>
81
+ </td>
82
+
83
+ <td className="px-4 py-3">
84
+ <div className="text-[12px] font-medium text-gray-900">
85
+ {plannedMessage.agentName || plannedMessage.agentPermanentId}
86
+ </div>
87
+ <div className="break-all text-[11px] text-gray-500">{plannedMessage.agentPermanentId}</div>
88
+ <div className="mt-1 text-[11px] text-gray-500">
89
+ {plannedMessage.username || `#${plannedMessage.userId}`}
90
+ </div>
91
+ <div className="mt-2 flex flex-wrap gap-2">
92
+ <Link
93
+ href={buildAgentProfileHref(plannedMessage.agentPermanentId)}
94
+ className="inline-flex items-center gap-1 rounded-md border border-blue-200 bg-blue-50 px-2.5 py-1 text-[11px] font-semibold text-blue-700 hover:bg-blue-100"
95
+ title="Open the agent this planned message belongs to"
96
+ >
97
+ <ArrowUpRight className="h-3.5 w-3.5" />
98
+ Open agent
99
+ </Link>
100
+ <Link
101
+ href={buildExistingAgentChatHref(plannedMessage.agentPermanentId, plannedMessage.chatId)}
102
+ className="inline-flex items-center gap-1 rounded-md border border-gray-200 bg-white px-2.5 py-1 text-[11px] font-semibold text-gray-700 hover:bg-gray-50"
103
+ title="Open the chat this planned message wakes up"
104
+ >
105
+ <MessageSquareText className="h-3.5 w-3.5" />
106
+ Open chat
107
+ </Link>
108
+ </div>
109
+ </td>
110
+
111
+ <td className="px-4 py-3">
112
+ <div className="text-[12px] text-gray-900">{formatPlannedMessageFrequency(plannedMessage)}</div>
113
+ <div className="mt-1 max-w-xs text-[11px] text-gray-500">
114
+ {describePlannedMessagePlan(plannedMessage)}
115
+ </div>
116
+ </td>
117
+
118
+ <td className="px-4 py-3 text-[11px] text-gray-800">
119
+ {isStillPlanned ? formatPlannedMessageDateTime(plannedMessage.dueAt, language) : '-'}
120
+ {plannedMessage.startsAt !== null && (
121
+ <div className="text-[11px] text-gray-500">
122
+ from {formatPlannedMessageDateTime(plannedMessage.startsAt, language)}
123
+ </div>
124
+ )}
125
+ {plannedMessage.endsAt !== null && (
126
+ <div className="text-[11px] text-gray-500">
127
+ until {formatPlannedMessageDateTime(plannedMessage.endsAt, language)}
128
+ </div>
129
+ )}
130
+ </td>
131
+
132
+ <td className="px-4 py-3 text-[11px] text-gray-800">
133
+ {formatPlannedMessageDateTime(plannedMessage.lastFiredAt, language)}
134
+ </td>
135
+
136
+ <td className="px-4 py-3 text-[11px] text-gray-800">{formatPlannedMessageRunCount(plannedMessage)}</td>
137
+
138
+ <td className="px-4 py-3">
139
+ <PlannedMessageLifecycleBadge lifecycle={plannedMessage.lifecycle} />
140
+ {endReasonLabel !== null && <div className="mt-1 text-[11px] text-gray-500">{endReasonLabel}</div>}
141
+ {plannedMessage.failureReason !== null && (
142
+ <div className="mt-1 max-w-xs break-words text-[11px] text-rose-700">
143
+ {plannedMessage.failureReason}
144
+ </div>
145
+ )}
146
+ </td>
147
+
148
+ <td className="px-4 py-3">
149
+ <div className="flex flex-wrap justify-end gap-2">
150
+ <button
151
+ type="button"
152
+ onClick={() => onEdit(plannedMessage)}
153
+ disabled={!isStillPlanned || isFiringNow || isBusy}
154
+ className={`${PLANNED_MESSAGE_ACTION_BUTTON_CLASS_NAME} border-gray-300 bg-white text-gray-700 hover:bg-gray-50`}
155
+ title={
156
+ isFiringNow
157
+ ? 'This planned message is waking its agent right now, so its plan cannot be changed'
158
+ : 'Change the plan or the text of this planned message'
159
+ }
160
+ >
161
+ Edit
162
+ </button>
163
+ <button
164
+ type="button"
165
+ onClick={() => void onTogglePaused(plannedMessage)}
166
+ disabled={!isStillPlanned || isFiringNow || isBusy}
167
+ className={`${PLANNED_MESSAGE_ACTION_BUTTON_CLASS_NAME} border-orange-200 bg-orange-50 text-orange-700 hover:bg-orange-100`}
168
+ title={
169
+ isPaused
170
+ ? 'Let this planned message wake its agent again'
171
+ : 'Hold this planned message back without cancelling it'
172
+ }
173
+ >
174
+ {isRunningAction('pause') ? 'Working…' : isPaused ? 'Resume' : 'Pause'}
175
+ </button>
176
+ <button
177
+ type="button"
178
+ onClick={() => void onCancel(plannedMessage)}
179
+ disabled={!isStillPlanned || isBusy}
180
+ className={`${PLANNED_MESSAGE_ACTION_BUTTON_CLASS_NAME} border-red-200 bg-red-50 text-red-700 hover:bg-red-100`}
181
+ title="Cancel this planned message so it never wakes its agent again"
182
+ >
183
+ {isRunningAction('cancel') ? 'Cancelling…' : 'Cancel'}
184
+ </button>
185
+ </div>
186
+ </td>
187
+ </tr>
188
+ );
189
+ }
@@ -0,0 +1,60 @@
1
+ 'use client';
2
+
3
+ import type { ServerLanguageCode } from '@/src/languages/ServerLanguageRegistry';
4
+ import type { PlannedMessageManagerCounters } from '@/src/utils/plannedMessageManager/createPlannedMessageManagerCounters';
5
+ import { AdminMetricCard } from '../_components/AdminMetricCard';
6
+ import { formatPlannedMessageDateTime } from './plannedMessageManagerPresentation';
7
+
8
+ /**
9
+ * Props for the planned-message summary metrics grid.
10
+ *
11
+ * @private function of PlannedMessageManagerClient
12
+ */
13
+ type PlannedMessageManagerSummaryMetricsProps = {
14
+ readonly counters: PlannedMessageManagerCounters;
15
+ readonly isLoading: boolean;
16
+ readonly language: ServerLanguageCode;
17
+ };
18
+
19
+ /**
20
+ * Renders the summary counters above the planned-message table.
21
+ *
22
+ * @private function of PlannedMessageManagerClient
23
+ */
24
+ export function PlannedMessageManagerSummaryMetrics({
25
+ counters,
26
+ isLoading,
27
+ language,
28
+ }: PlannedMessageManagerSummaryMetricsProps) {
29
+ /**
30
+ * Formats one counter, keeping the loading placeholder until the first listing arrives.
31
+ */
32
+ function formatCounter(count: number): string {
33
+ return isLoading ? '...' : count.toLocaleString();
34
+ }
35
+
36
+ return (
37
+ <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
38
+ <AdminMetricCard
39
+ label="Ongoing"
40
+ value={formatCounter(counters.countByLifecycle.ONGOING)}
41
+ caption="Waking their agent right now"
42
+ />
43
+ <AdminMetricCard
44
+ label="Scheduled"
45
+ value={formatCounter(counters.countByLifecycle.SCHEDULED + counters.countByLifecycle.NOT_STARTED)}
46
+ caption="Wake-ups still ahead"
47
+ />
48
+ <AdminMetricCard
49
+ label="Paused"
50
+ value={formatCounter(counters.countByLifecycle.PAUSED)}
51
+ caption="Held back until resumed"
52
+ />
53
+ <AdminMetricCard
54
+ label="Next wake-up"
55
+ value={isLoading ? '...' : formatPlannedMessageDateTime(counters.nextDueAt, language)}
56
+ caption="Earliest planned wake-up"
57
+ />
58
+ </div>
59
+ );
60
+ }
@@ -0,0 +1,147 @@
1
+ 'use client';
2
+
3
+ import type { ServerLanguageCode } from '@/src/languages/ServerLanguageRegistry';
4
+ import type { PlannedMessageManagerSortField } from '@/src/utils/plannedMessagesAdmin';
5
+ import { Card } from '../../../components/Homepage/Card';
6
+ import { AdminSortableTableHeaderCell } from '../_components/AdminSortableTableHeaderCell';
7
+ import { PlannedMessageManagerRow } from './PlannedMessageManagerRow';
8
+ import type { UsePlannedMessageManagerStateResult } from './usePlannedMessageManagerState';
9
+
10
+ /**
11
+ * Sortable columns of the planned-message table, with the label used in their sort tooltip.
12
+ *
13
+ * @private function of PlannedMessageManagerTableCard
14
+ */
15
+ const PLANNED_MESSAGE_SORT_COLUMNS: ReadonlyArray<{
16
+ readonly sortBy: PlannedMessageManagerSortField;
17
+ readonly heading: string;
18
+ readonly label: string;
19
+ }> = [
20
+ { sortBy: 'plannedMessage', heading: 'Planned message', label: 'planned message' },
21
+ { sortBy: 'agent', heading: 'Agent', label: 'agent' },
22
+ { sortBy: 'frequency', heading: 'Frequency', label: 'frequency' },
23
+ { sortBy: 'nextRun', heading: 'Next run', label: 'next run' },
24
+ { sortBy: 'lastRun', heading: 'Last run', label: 'last run' },
25
+ { sortBy: 'runs', heading: 'Runs', label: 'runs' },
26
+ { sortBy: 'state', heading: 'State', label: 'state' },
27
+ ];
28
+
29
+ /**
30
+ * Props for the planned-message table card.
31
+ *
32
+ * @private function of PlannedMessageManagerClient
33
+ */
34
+ type PlannedMessageManagerTableCardProps = {
35
+ readonly language: ServerLanguageCode;
36
+ readonly state: UsePlannedMessageManagerStateResult;
37
+ };
38
+
39
+ /**
40
+ * Renders the planned-message table with its loading, empty, and paging states.
41
+ *
42
+ * @private function of PlannedMessageManagerClient
43
+ */
44
+ export function PlannedMessageManagerTableCard({ language, state }: PlannedMessageManagerTableCardProps) {
45
+ const firstVisibleIndex = Math.min((state.page - 1) * state.pageSize + 1, state.matchedCount);
46
+ const lastVisibleIndex = Math.min(state.page * state.pageSize, state.matchedCount);
47
+
48
+ return (
49
+ <Card className="hover:border-gray-200 hover:shadow-md">
50
+ <div className="flex items-center justify-between gap-4">
51
+ <div>
52
+ <h2 className="text-lg font-medium text-gray-900">
53
+ Planned messages ({state.matchedCount.toLocaleString()})
54
+ </h2>
55
+ <p className="mt-1 text-sm text-gray-500">
56
+ Every wake-up an agent planned for itself or for one of its chats.
57
+ </p>
58
+ </div>
59
+ {state.data.isRefreshing ? (
60
+ <span className="text-xs font-medium text-blue-600">Refreshing…</span>
61
+ ) : null}
62
+ </div>
63
+
64
+ {state.data.isLoading ? (
65
+ <div className="py-10 text-center text-sm text-gray-500">Loading planned messages…</div>
66
+ ) : state.visiblePlannedMessages.length === 0 ? (
67
+ <div className="py-10 text-center text-sm text-gray-500">
68
+ No planned message matches the selected filters.
69
+ </div>
70
+ ) : (
71
+ <div className="mt-4 overflow-x-auto">
72
+ <table className="min-w-full divide-y divide-gray-200 text-xs">
73
+ <thead className="bg-gray-50 text-gray-500">
74
+ <tr>
75
+ {PLANNED_MESSAGE_SORT_COLUMNS.map((column) => (
76
+ <AdminSortableTableHeaderCell
77
+ key={column.sortBy}
78
+ className="px-4 py-3 text-left font-semibold"
79
+ label={column.label}
80
+ sortBy={column.sortBy}
81
+ activeSortBy={state.sortBy}
82
+ sortOrder={state.sortOrder}
83
+ onSortChange={state.handleSortChange}
84
+ >
85
+ {column.heading}
86
+ </AdminSortableTableHeaderCell>
87
+ ))}
88
+ <th className="px-4 py-3 text-right font-semibold">Actions</th>
89
+ </tr>
90
+ </thead>
91
+ <tbody className="divide-y divide-gray-200 bg-white">
92
+ {state.visiblePlannedMessages.map((plannedMessage) => (
93
+ <PlannedMessageManagerRow
94
+ key={plannedMessage.timeoutId}
95
+ plannedMessage={plannedMessage}
96
+ language={language}
97
+ busyTimeoutId={state.busyTimeoutId}
98
+ busyAction={state.busyAction}
99
+ onEdit={state.openPlannedMessageEditor}
100
+ onTogglePaused={state.togglePlannedMessagePaused}
101
+ onCancel={state.cancelPlannedMessage}
102
+ />
103
+ ))}
104
+ </tbody>
105
+ </table>
106
+ </div>
107
+ )}
108
+
109
+ <div className="mt-4 flex flex-col items-center justify-between gap-3 border-t border-gray-100 pt-4 text-xs text-gray-600 md:flex-row">
110
+ <div>
111
+ {state.matchedCount > 0 ? (
112
+ <>
113
+ Showing <span className="font-semibold">{firstVisibleIndex}</span> –{' '}
114
+ <span className="font-semibold">{lastVisibleIndex}</span> of{' '}
115
+ <span className="font-semibold">{state.matchedCount}</span> planned messages
116
+ </>
117
+ ) : (
118
+ 'No planned messages'
119
+ )}
120
+ </div>
121
+
122
+ <div className="flex items-center gap-2">
123
+ <button
124
+ type="button"
125
+ onClick={state.goToPreviousPage}
126
+ disabled={state.isPreviousPageDisabled}
127
+ className="rounded-md border border-gray-300 px-3 py-1.5 font-medium text-gray-700 disabled:cursor-not-allowed disabled:opacity-50"
128
+ >
129
+ Previous
130
+ </button>
131
+ <span>
132
+ Page <span className="font-semibold">{state.page}</span> of{' '}
133
+ <span className="font-semibold">{state.totalPages}</span>
134
+ </span>
135
+ <button
136
+ type="button"
137
+ onClick={state.goToNextPage}
138
+ disabled={state.isNextPageDisabled}
139
+ className="rounded-md border border-gray-300 px-3 py-1.5 font-medium text-gray-700 disabled:cursor-not-allowed disabled:opacity-50"
140
+ >
141
+ Next
142
+ </button>
143
+ </div>
144
+ </div>
145
+ </Card>
146
+ );
147
+ }
@@ -0,0 +1,17 @@
1
+ import { ForbiddenPage } from '../../../components/ForbiddenPage/ForbiddenPage';
2
+ import { isUserAdmin } from '../../../utils/isUserAdmin';
3
+ import { isUserGlobalAdmin } from '../../../utils/isUserGlobalAdmin';
4
+ import { PlannedMessageManagerClient } from './PlannedMessageManagerClient';
5
+
6
+ /**
7
+ * Admin page managing every planned message (durable chat timeout) of the server.
8
+ */
9
+ export default async function AdminPlannedMessagesPage() {
10
+ const [isAdmin, isSuperAdmin] = await Promise.all([isUserAdmin(), isUserGlobalAdmin()]);
11
+
12
+ if (!isAdmin) {
13
+ return <ForbiddenPage />;
14
+ }
15
+
16
+ return <PlannedMessageManagerClient isSuperAdmin={isSuperAdmin} />;
17
+ }