@parhelia/core 0.1.11873 → 0.1.11955

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 (247) hide show
  1. package/dist/agents-view/AgentsInbox.d.ts +1 -1
  2. package/dist/agents-view/AgentsInbox.js +15 -2
  3. package/dist/agents-view/AgentsInbox.js.map +1 -1
  4. package/dist/agents-view/AgentsSidebar.d.ts +20 -0
  5. package/dist/agents-view/AgentsSidebar.js +21 -0
  6. package/dist/agents-view/AgentsSidebar.js.map +1 -0
  7. package/dist/agents-view/AgentsView.d.ts +6 -7
  8. package/dist/agents-view/AgentsView.js +63 -25
  9. package/dist/agents-view/AgentsView.js.map +1 -1
  10. package/dist/agents-view/AgentsWorkspaceView.d.ts +2 -6
  11. package/dist/agents-view/AgentsWorkspaceView.js +242 -112
  12. package/dist/agents-view/AgentsWorkspaceView.js.map +1 -1
  13. package/dist/components/ui/context-menu.js +24 -9
  14. package/dist/components/ui/context-menu.js.map +1 -1
  15. package/dist/components/ui/select.js +1 -1
  16. package/dist/components/ui/select.js.map +1 -1
  17. package/dist/config/config.js +15 -11
  18. package/dist/config/config.js.map +1 -1
  19. package/dist/editor/ContentTree.js +2 -2
  20. package/dist/editor/ContentTree.js.map +1 -1
  21. package/dist/editor/ContextMenu.js +11 -5
  22. package/dist/editor/ContextMenu.js.map +1 -1
  23. package/dist/editor/FieldListField.js +1 -1
  24. package/dist/editor/FieldListField.js.map +1 -1
  25. package/dist/editor/MainLayout.js.map +1 -1
  26. package/dist/editor/MobileLayout.js +19 -9
  27. package/dist/editor/MobileLayout.js.map +1 -1
  28. package/dist/editor/ai/AgentStatusBadge.d.ts +1 -1
  29. package/dist/editor/ai/AgentStatusBadge.js +18 -2
  30. package/dist/editor/ai/AgentStatusBadge.js.map +1 -1
  31. package/dist/editor/ai/AgentTerminal.js +342 -55
  32. package/dist/editor/ai/AgentTerminal.js.map +1 -1
  33. package/dist/editor/ai/AiResponseMessage.js +46 -4
  34. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  35. package/dist/editor/ai/ContextInfoBar.js +151 -5
  36. package/dist/editor/ai/ContextInfoBar.js.map +1 -1
  37. package/dist/editor/ai/EditOperationsPanel.d.ts +2 -1
  38. package/dist/editor/ai/EditOperationsPanel.js +6 -1
  39. package/dist/editor/ai/EditOperationsPanel.js.map +1 -1
  40. package/dist/editor/ai/dialogs/AgentDialogHandler.js +64 -15
  41. package/dist/editor/ai/dialogs/AgentDialogHandler.js.map +1 -1
  42. package/dist/editor/ai/dialogs/QuestionnaireInline.js +111 -20
  43. package/dist/editor/ai/dialogs/QuestionnaireInline.js.map +1 -1
  44. package/dist/editor/ai/dialogs/agentDialogTypes.d.ts +24 -0
  45. package/dist/editor/ai/dialogs/agentDialogTypes.js.map +1 -1
  46. package/dist/editor/ai/useAgentStatus.d.ts +1 -0
  47. package/dist/editor/ai/useAgentStatus.js +74 -29
  48. package/dist/editor/ai/useAgentStatus.js.map +1 -1
  49. package/dist/editor/client/EditorShell.js +72 -8
  50. package/dist/editor/client/EditorShell.js.map +1 -1
  51. package/dist/editor/client/hooks/useQuota.d.ts +7 -0
  52. package/dist/editor/client/hooks/useQuota.js.map +1 -1
  53. package/dist/editor/client/hooks/useSocketMessageHandler.js +10 -1
  54. package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
  55. package/dist/editor/client/pageModelBuilder.js +3 -30
  56. package/dist/editor/client/pageModelBuilder.js.map +1 -1
  57. package/dist/editor/client/ui/EditorChrome.js +31 -1
  58. package/dist/editor/client/ui/EditorChrome.js.map +1 -1
  59. package/dist/editor/commands/componentCommands.js +106 -6
  60. package/dist/editor/commands/componentCommands.js.map +1 -1
  61. package/dist/editor/commands/itemCommands.d.ts +1 -0
  62. package/dist/editor/commands/itemCommands.js +28 -1
  63. package/dist/editor/commands/itemCommands.js.map +1 -1
  64. package/dist/editor/componentTreeHelper.js +22 -2
  65. package/dist/editor/componentTreeHelper.js.map +1 -1
  66. package/dist/editor/insertMenuItems.d.ts +4 -0
  67. package/dist/editor/insertMenuItems.js +66 -0
  68. package/dist/editor/insertMenuItems.js.map +1 -0
  69. package/dist/editor/menubar/toolbar-sections/UtilityControls.js +1 -1
  70. package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
  71. package/dist/editor/page-editor-chrome/FrameMenus.js +8 -1
  72. package/dist/editor/page-editor-chrome/FrameMenus.js.map +1 -1
  73. package/dist/editor/page-editor-chrome/InlineEditor.js +25 -11
  74. package/dist/editor/page-editor-chrome/InlineEditor.js.map +1 -1
  75. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +32 -17
  76. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
  77. package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +17 -11
  78. package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -1
  79. package/dist/editor/page-viewer/EditorForm.js +6 -5
  80. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  81. package/dist/editor/page-viewer/PageViewerFrame.js +49 -1
  82. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  83. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +5 -0
  84. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
  85. package/dist/editor/pageModel.d.ts +2 -0
  86. package/dist/editor/reviews/CreateReviewConfirmStep.d.ts +16 -0
  87. package/dist/editor/reviews/CreateReviewConfirmStep.js +37 -0
  88. package/dist/editor/reviews/CreateReviewConfirmStep.js.map +1 -0
  89. package/dist/editor/reviews/CreateReviewDetailsStep.d.ts +51 -0
  90. package/dist/editor/reviews/CreateReviewDetailsStep.js +121 -0
  91. package/dist/editor/reviews/CreateReviewDetailsStep.js.map +1 -0
  92. package/dist/editor/reviews/CreateReviewDialog.js +260 -173
  93. package/dist/editor/reviews/CreateReviewDialog.js.map +1 -1
  94. package/dist/editor/reviews/CreateReviewSuccessStep.d.ts +6 -0
  95. package/dist/editor/reviews/CreateReviewSuccessStep.js +8 -0
  96. package/dist/editor/reviews/CreateReviewSuccessStep.js.map +1 -0
  97. package/dist/editor/reviews/DecisionsMatrix.js +96 -25
  98. package/dist/editor/reviews/DecisionsMatrix.js.map +1 -1
  99. package/dist/editor/reviews/MultiReviewManager.js +25 -3
  100. package/dist/editor/reviews/MultiReviewManager.js.map +1 -1
  101. package/dist/editor/reviews/PagesPanel.js +31 -15
  102. package/dist/editor/reviews/PagesPanel.js.map +1 -1
  103. package/dist/editor/reviews/ReviewCard.js +13 -7
  104. package/dist/editor/reviews/ReviewCard.js.map +1 -1
  105. package/dist/editor/reviews/ReviewDetail.js +2 -2
  106. package/dist/editor/reviews/ReviewDetail.js.map +1 -1
  107. package/dist/editor/reviews/ReviewsList.js +7 -3
  108. package/dist/editor/reviews/ReviewsList.js.map +1 -1
  109. package/dist/editor/services/agentService.d.ts +14 -1
  110. package/dist/editor/services/agentService.js +27 -1
  111. package/dist/editor/services/agentService.js.map +1 -1
  112. package/dist/editor/services/aiService.d.ts +39 -1
  113. package/dist/editor/services/aiService.js +12 -2
  114. package/dist/editor/services/aiService.js.map +1 -1
  115. package/dist/editor/services/serviceHelper.d.ts +1 -1
  116. package/dist/editor/services/serviceHelper.js +2 -1
  117. package/dist/editor/services/serviceHelper.js.map +1 -1
  118. package/dist/editor/settings/About.js +1 -1
  119. package/dist/editor/settings/About.js.map +1 -1
  120. package/dist/editor/settings/QuotaInfo.js +202 -4
  121. package/dist/editor/settings/QuotaInfo.js.map +1 -1
  122. package/dist/editor/settings/panels/SearchConfigPanel.js +11 -13
  123. package/dist/editor/settings/panels/SearchConfigPanel.js.map +1 -1
  124. package/dist/editor/settings/status/useStartupChecks.js +2 -1
  125. package/dist/editor/settings/status/useStartupChecks.js.map +1 -1
  126. package/dist/editor/sidebar/ComponentTree.js +26 -20
  127. package/dist/editor/sidebar/ComponentTree.js.map +1 -1
  128. package/dist/editor/sidebar/MobileWorkspacePopover.d.ts +16 -0
  129. package/dist/editor/sidebar/MobileWorkspacePopover.js +35 -0
  130. package/dist/editor/sidebar/MobileWorkspacePopover.js.map +1 -0
  131. package/dist/editor/sidebar/NavigationSidebar.js +30 -14
  132. package/dist/editor/sidebar/NavigationSidebar.js.map +1 -1
  133. package/dist/editor/ui/ItemNameDialogNew.js +8 -8
  134. package/dist/editor/ui/ItemNameDialogNew.js.map +1 -1
  135. package/dist/editor/ui/SharedFolderSelectorDialog.d.ts +11 -0
  136. package/dist/editor/ui/SharedFolderSelectorDialog.js +79 -0
  137. package/dist/editor/ui/SharedFolderSelectorDialog.js.map +1 -0
  138. package/dist/editor/ui/SimpleTabs.js +1 -1
  139. package/dist/editor/ui/SimpleTabs.js.map +1 -1
  140. package/dist/editor/ui/Splitter.js +4 -2
  141. package/dist/editor/ui/Splitter.js.map +1 -1
  142. package/dist/editor/ui/TreeListSelector.d.ts +2 -1
  143. package/dist/editor/ui/TreeListSelector.js +2 -2
  144. package/dist/editor/ui/TreeListSelector.js.map +1 -1
  145. package/dist/editor/utils.js +42 -0
  146. package/dist/editor/utils.js.map +1 -1
  147. package/dist/editor/views/EditorSlot.js +7 -8
  148. package/dist/editor/views/EditorSlot.js.map +1 -1
  149. package/dist/index.d.ts +1 -0
  150. package/dist/index.js +1 -0
  151. package/dist/index.js.map +1 -1
  152. package/dist/revision.d.ts +2 -2
  153. package/dist/revision.js +2 -2
  154. package/dist/setup/wizard/steps/ImportModelDialog.js +24 -22
  155. package/dist/setup/wizard/steps/ImportModelDialog.js.map +1 -1
  156. package/dist/splash-screen/NewPage.js +2 -2
  157. package/dist/splash-screen/NewPage.js.map +1 -1
  158. package/dist/splash-screen/RecentPages.js +1 -1
  159. package/dist/splash-screen/RecentPages.js.map +1 -1
  160. package/dist/task-board/TaskBoardWorkspace.d.ts +1 -0
  161. package/dist/task-board/TaskBoardWorkspace.js +1094 -0
  162. package/dist/task-board/TaskBoardWorkspace.js.map +1 -0
  163. package/dist/task-board/components/AddDependencyDialog.d.ts +8 -0
  164. package/dist/task-board/components/AddDependencyDialog.js +53 -0
  165. package/dist/task-board/components/AddDependencyDialog.js.map +1 -0
  166. package/dist/task-board/components/AssignAgentDialog.d.ts +7 -0
  167. package/dist/task-board/components/AssignAgentDialog.js +96 -0
  168. package/dist/task-board/components/AssignAgentDialog.js.map +1 -0
  169. package/dist/task-board/components/CommentsList.d.ts +3 -0
  170. package/dist/task-board/components/CommentsList.js +36 -0
  171. package/dist/task-board/components/CommentsList.js.map +1 -0
  172. package/dist/task-board/components/CreateProjectDialog.d.ts +7 -0
  173. package/dist/task-board/components/CreateProjectDialog.js +175 -0
  174. package/dist/task-board/components/CreateProjectDialog.js.map +1 -0
  175. package/dist/task-board/components/CreateTaskDialog.d.ts +7 -0
  176. package/dist/task-board/components/CreateTaskDialog.js +76 -0
  177. package/dist/task-board/components/CreateTaskDialog.js.map +1 -0
  178. package/dist/task-board/components/ProjectAgentsPanel.d.ts +4 -0
  179. package/dist/task-board/components/ProjectAgentsPanel.js +159 -0
  180. package/dist/task-board/components/ProjectAgentsPanel.js.map +1 -0
  181. package/dist/task-board/components/ProjectDashboard.d.ts +25 -0
  182. package/dist/task-board/components/ProjectDashboard.js +91 -0
  183. package/dist/task-board/components/ProjectDashboard.js.map +1 -0
  184. package/dist/task-board/components/ProjectList.d.ts +7 -0
  185. package/dist/task-board/components/ProjectList.js +74 -0
  186. package/dist/task-board/components/ProjectList.js.map +1 -0
  187. package/dist/task-board/components/ProjectSettingsDialog.d.ts +8 -0
  188. package/dist/task-board/components/ProjectSettingsDialog.js +146 -0
  189. package/dist/task-board/components/ProjectSettingsDialog.js.map +1 -0
  190. package/dist/task-board/components/TaskAgentPanel.d.ts +10 -0
  191. package/dist/task-board/components/TaskAgentPanel.js +42 -0
  192. package/dist/task-board/components/TaskAgentPanel.js.map +1 -0
  193. package/dist/task-board/components/TaskAssigneePicker.d.ts +12 -0
  194. package/dist/task-board/components/TaskAssigneePicker.js +115 -0
  195. package/dist/task-board/components/TaskAssigneePicker.js.map +1 -0
  196. package/dist/task-board/components/TaskBoardTitlebar.d.ts +1 -0
  197. package/dist/task-board/components/TaskBoardTitlebar.js +60 -0
  198. package/dist/task-board/components/TaskBoardTitlebar.js.map +1 -0
  199. package/dist/task-board/components/TaskCard.d.ts +9 -0
  200. package/dist/task-board/components/TaskCard.js +24 -0
  201. package/dist/task-board/components/TaskCard.js.map +1 -0
  202. package/dist/task-board/components/TaskDetailDialog.d.ts +11 -0
  203. package/dist/task-board/components/TaskDetailDialog.js +8 -0
  204. package/dist/task-board/components/TaskDetailDialog.js.map +1 -0
  205. package/dist/task-board/components/TaskDetailPanel.d.ts +13 -0
  206. package/dist/task-board/components/TaskDetailPanel.js +322 -0
  207. package/dist/task-board/components/TaskDetailPanel.js.map +1 -0
  208. package/dist/task-board/components/TaskRow.d.ts +9 -0
  209. package/dist/task-board/components/TaskRow.js +25 -0
  210. package/dist/task-board/components/TaskRow.js.map +1 -0
  211. package/dist/task-board/index.d.ts +15 -0
  212. package/dist/task-board/index.js +18 -0
  213. package/dist/task-board/index.js.map +1 -0
  214. package/dist/task-board/services/taskService.d.ts +52 -0
  215. package/dist/task-board/services/taskService.js +74 -0
  216. package/dist/task-board/services/taskService.js.map +1 -0
  217. package/dist/task-board/taskAgentConfig.d.ts +7 -0
  218. package/dist/task-board/taskAgentConfig.js +43 -0
  219. package/dist/task-board/taskAgentConfig.js.map +1 -0
  220. package/dist/task-board/taskAgentLink.d.ts +2 -0
  221. package/dist/task-board/taskAgentLink.js +35 -0
  222. package/dist/task-board/taskAgentLink.js.map +1 -0
  223. package/dist/task-board/taskBoardNavStore.d.ts +35 -0
  224. package/dist/task-board/taskBoardNavStore.js +42 -0
  225. package/dist/task-board/taskBoardNavStore.js.map +1 -0
  226. package/dist/task-board/taskExecutionStatus.d.ts +12 -0
  227. package/dist/task-board/taskExecutionStatus.js +96 -0
  228. package/dist/task-board/taskExecutionStatus.js.map +1 -0
  229. package/dist/task-board/taskStatus.d.ts +2 -0
  230. package/dist/task-board/taskStatus.js +26 -0
  231. package/dist/task-board/taskStatus.js.map +1 -0
  232. package/dist/task-board/types.d.ts +169 -0
  233. package/dist/task-board/types.js +2 -0
  234. package/dist/task-board/types.js.map +1 -0
  235. package/dist/task-board/utils/projectHierarchy.d.ts +13 -0
  236. package/dist/task-board/utils/projectHierarchy.js +34 -0
  237. package/dist/task-board/utils/projectHierarchy.js.map +1 -0
  238. package/dist/task-board/views/KanbanView.d.ts +14 -0
  239. package/dist/task-board/views/KanbanView.js +136 -0
  240. package/dist/task-board/views/KanbanView.js.map +1 -0
  241. package/dist/task-board/views/ListView.d.ts +13 -0
  242. package/dist/task-board/views/ListView.js +74 -0
  243. package/dist/task-board/views/ListView.js.map +1 -0
  244. package/dist/task-board/views/PlanningView.d.ts +7 -0
  245. package/dist/task-board/views/PlanningView.js +112 -0
  246. package/dist/task-board/views/PlanningView.js.map +1 -0
  247. package/package.json +1 -1
@@ -3,20 +3,17 @@ import { useState, useEffect, useMemo } from "react";
3
3
  import { Dialog, DialogContent } from "../../components/ui/dialog";
4
4
  import { StyledDialogTitle } from "../../components/ui/styled-dialog-title";
5
5
  import { Button } from "../../components/ui/button";
6
- import { Input } from "../../components/ui/input";
7
- import { Label } from "../../components/ui/label";
8
- import { Checkbox } from "../../components/ui/checkbox";
9
- import { Switch } from "../../components/ui/switch";
10
- import { Plus, X, Loader2, ChevronRight, ChevronLeft, ChevronDown, Settings2, ClipboardCheck, AlertTriangle, } from "lucide-react";
6
+ import { Loader2, ClipboardCheck, MailCheck, CheckCircle2 } from "lucide-react";
11
7
  import { useEditContext } from "../client/editContext";
12
- import { Splitter } from "../ui/Splitter";
13
- import { SimpleIconButton } from "../ui/SimpleIconButton";
14
- import { LanguageSelector } from "../../components/ui/LanguageSelector";
15
- import { PreconfiguredReviewerSelector } from "./PreconfiguredReviewerSelector";
16
- import { TreeListSelector } from "../ui/TreeListSelector";
17
8
  import DialogButtons from "../ui/DialogButtons";
9
+ import { CreateReviewDetailsStep } from "./CreateReviewDetailsStep";
10
+ import { CreateReviewConfirmStep } from "./CreateReviewConfirmStep";
11
+ import { CreateReviewSuccessStep } from "./CreateReviewSuccessStep";
12
+ import { loadAiProfiles } from "../services/aiService";
13
+ import { awaitAgentResponse, closeAgent, startAgent, } from "../services/agentService";
18
14
  export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems, }) {
19
15
  const editContext = useEditContext();
16
+ const [step, setStep] = useState("details");
20
17
  const [title, setTitle] = useState("");
21
18
  const [language, setLanguage] = useState(editContext?.currentItemDescriptor?.language || "en");
22
19
  const [languageMode, setLanguageMode] = useState("single");
@@ -41,6 +38,10 @@ export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems
41
38
  const [selectedInTree, setSelectedInTree] = useState([]);
42
39
  const [selectedFromList, setSelectedFromList] = useState([]);
43
40
  const [advancedSettingsOpen, setAdvancedSettingsOpen] = useState(false);
41
+ const [createdReviewId, setCreatedReviewId] = useState(null);
42
+ const [sentInvitationCount, setSentInvitationCount] = useState(0);
43
+ const [sentInvitationEmails, setSentInvitationEmails] = useState([]);
44
+ const [isGeneratingTitle, setIsGeneratingTitle] = useState(false);
44
45
  const CONTENT_ROOT_ID = "0de95ae4-41ab-4d01-9eb0-67441b7c2450"; // /sitecore/content
45
46
  // Memoize rootItemIds to prevent unnecessary re-renders of ContentTree
46
47
  const rootItemIds = useMemo(() => [CONTENT_ROOT_ID], []);
@@ -90,8 +91,10 @@ export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems
90
91
  // This prevents resetting user changes (like includeSubitems toggles) when context changes
91
92
  const initializeItems = async () => {
92
93
  let currentDescriptor = editContext.currentItemDescriptor;
93
- // If the descriptor is missing name or path, fetch it from the server
94
- if ((!currentDescriptor.name || !currentDescriptor.path) &&
94
+ // If the descriptor is missing name, display name, or path, fetch it from the server
95
+ if ((!currentDescriptor.name ||
96
+ !currentDescriptor.displayName ||
97
+ !currentDescriptor.path) &&
95
98
  editContext.itemsRepository) {
96
99
  try {
97
100
  const stubs = await editContext.itemsRepository.getItemsStubs([
@@ -101,6 +104,7 @@ export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems
101
104
  currentDescriptor = {
102
105
  ...currentDescriptor,
103
106
  name: stubs[0].name || currentDescriptor.name,
107
+ displayName: stubs[0].displayName || currentDescriptor.displayName,
104
108
  path: stubs[0].path || currentDescriptor.path,
105
109
  };
106
110
  }
@@ -111,8 +115,8 @@ export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems
111
115
  }
112
116
  let itemsToSet;
113
117
  if (initialItems) {
114
- // Fetch missing names/paths for initial items as well
115
- const itemsToFetch = initialItems.filter((i) => !i.name || !i.path);
118
+ // Fetch missing names/display names/paths for initial items as well
119
+ const itemsToFetch = initialItems.filter((i) => !i.name || !i.displayName || !i.path);
116
120
  if (itemsToFetch.length > 0 && editContext.itemsRepository) {
117
121
  try {
118
122
  const stubs = await editContext.itemsRepository.getItemsStubs(itemsToFetch);
@@ -123,6 +127,7 @@ export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems
123
127
  descriptor: {
124
128
  ...i,
125
129
  name: stub?.name || i.name,
130
+ displayName: stub?.displayName || i.displayName,
126
131
  path: stub?.path || i.path,
127
132
  },
128
133
  includeSubitems: false,
@@ -167,6 +172,119 @@ export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems
167
172
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
168
173
  return emailRegex.test(email);
169
174
  };
175
+ const extractReviewTitleFromAgentResponse = (content) => {
176
+ if (!content)
177
+ return "";
178
+ const fencedTitleMatch = content.match(/```(?:suggestion|title)\s*\n([\s\S]*?)```/i);
179
+ const fencedGenericMatch = content.match(/```\s*\n([\s\S]*?)```/);
180
+ const raw = (fencedTitleMatch?.[1] || fencedGenericMatch?.[1] || content)
181
+ .trim()
182
+ .replace(/^["']|["']$/g, "");
183
+ if (!raw)
184
+ return "";
185
+ return raw.split("\n")[0]?.trim() || "";
186
+ };
187
+ const buildReviewTitleSuggestionPrompt = () => {
188
+ const languageScope = languageMode === "all"
189
+ ? "all languages"
190
+ : languageMode === "multiple"
191
+ ? selectedLanguages.join(", ")
192
+ : language;
193
+ const itemNames = items
194
+ .slice(0, 5)
195
+ .map((item) => item.descriptor.displayName ||
196
+ item.descriptor.name ||
197
+ item.descriptor.path ||
198
+ item.descriptor.id)
199
+ .filter(Boolean)
200
+ .join(", ");
201
+ const reviewerNames = reviewers
202
+ .slice(0, 3)
203
+ .map((reviewer) => reviewer.name || reviewer.email)
204
+ .filter(Boolean)
205
+ .join(", ");
206
+ return `Generate a concise review name for a Sitecore content review.
207
+
208
+ Context:
209
+ - Language scope: ${languageScope}
210
+ - Items: ${itemNames || "N/A"}
211
+ - Number of reviewers: ${reviewers.length}
212
+ - First reviewers: ${reviewerNames || "N/A"}
213
+ - Includes subitems: ${items.some((x) => x.includeSubitems) ? "yes" : "no"}
214
+
215
+ Rules:
216
+ - Return a short, clear title (4-10 words).
217
+ - Do not use quotes.
218
+ - Do not include prefixes like "Review:".
219
+ - Output ONLY the title inside a \`\`\`suggestion fenced code block.
220
+
221
+ Example:
222
+ \`\`\`suggestion
223
+ Homepage and Navigation Content Review
224
+ \`\`\``;
225
+ };
226
+ const generateReviewTitleSuggestion = async () => {
227
+ if (title.trim())
228
+ return null;
229
+ if (!editContext?.addSocketMessageListener || !editContext.sessionId) {
230
+ return null;
231
+ }
232
+ const newAgentId = crypto.randomUUID();
233
+ setIsGeneratingTitle(true);
234
+ try {
235
+ const profiles = await loadAiProfiles(editContext.currentItemDescriptor);
236
+ if (!profiles || profiles.length === 0) {
237
+ return null;
238
+ }
239
+ const configuredProfileId = editContext.parheliaSettings?.commentResolveProfileId;
240
+ const selectedProfile = profiles.find((profile) => configuredProfileId &&
241
+ profile.id.toLowerCase() === configuredProfileId.toLowerCase()) || profiles[0];
242
+ if (!selectedProfile)
243
+ return null;
244
+ await startAgent({
245
+ agentId: newAgentId,
246
+ message: buildReviewTitleSuggestionPrompt(),
247
+ sessionId: editContext.sessionId,
248
+ profileId: selectedProfile.id,
249
+ mode: "autonomous",
250
+ context: {
251
+ items: items.map((item) => ({
252
+ id: item.descriptor.id,
253
+ language: item.descriptor.language,
254
+ version: item.descriptor.version,
255
+ name: item.descriptor.name,
256
+ path: item.descriptor.path,
257
+ })),
258
+ additionalData: {
259
+ intent: "review-title-suggestion",
260
+ reviewerCount: reviewers.length,
261
+ },
262
+ },
263
+ });
264
+ const result = await awaitAgentResponse({
265
+ agentId: newAgentId,
266
+ addSocketMessageListener: editContext.addSocketMessageListener,
267
+ timeout: 45000,
268
+ });
269
+ if (!result.success)
270
+ return null;
271
+ const extractedTitle = extractReviewTitleFromAgentResponse(result.content);
272
+ return extractedTitle || null;
273
+ }
274
+ catch (err) {
275
+ console.error("Failed to generate review title suggestion:", err);
276
+ return null;
277
+ }
278
+ finally {
279
+ setIsGeneratingTitle(false);
280
+ try {
281
+ await closeAgent(newAgentId);
282
+ }
283
+ catch (err) {
284
+ console.error("Failed to close review title suggestion agent:", err);
285
+ }
286
+ }
287
+ };
170
288
  const emailAlreadyExists = (email) => {
171
289
  return reviewers.some((r) => r.email.toLowerCase() === email.toLowerCase());
172
290
  };
@@ -198,40 +316,80 @@ export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems
198
316
  const removeItem = (index) => {
199
317
  setItems(items.filter((_, i) => i !== index));
200
318
  };
201
- const handleCreate = async () => {
202
- setShowErrors(true);
319
+ const resetDialogState = () => {
320
+ setStep("details");
321
+ setTitle("");
322
+ setReviewers([]);
323
+ setItems([]);
324
+ setExpirationDays("");
325
+ setSecret("");
326
+ setSecretConfirm("");
327
+ setShowSecretFields(false);
328
+ setRequiredApprovals("1");
329
+ setUpdateWorkflowOnCompletion(false);
330
+ setShowErrors(false);
203
331
  setError(null);
332
+ setLanguageMode("single");
333
+ setSelectedLanguages([
334
+ editContext?.currentItemDescriptor?.language || "en",
335
+ ]);
336
+ setLanguage(editContext?.currentItemDescriptor?.language || "en");
337
+ setAdvancedSettingsOpen(false);
338
+ setSelectedInTree([]);
339
+ setSelectedFromList([]);
340
+ setNewReviewer({ name: "", email: "" });
341
+ setCreatedReviewId(null);
342
+ setSentInvitationCount(0);
343
+ setSentInvitationEmails([]);
344
+ setIsGeneratingTitle(false);
345
+ };
346
+ const validateForm = () => {
204
347
  if (items.length === 0) {
205
- setError("Please select at least one page/item");
206
- return;
348
+ return "Please select at least one page/item";
207
349
  }
208
350
  if (reviewers.length === 0) {
209
- setError("Please add at least one reviewer");
210
- return;
351
+ return "Please add at least one reviewer";
211
352
  }
212
- // Validate language selection based on mode
213
353
  if (languageMode === "single" && !language) {
214
- setError("Language is required");
215
- return;
354
+ return "Language is required";
216
355
  }
217
356
  if (languageMode === "multiple" && selectedLanguages.length === 0) {
218
- setError("Please select at least one language");
219
- return;
357
+ return "Please select at least one language";
220
358
  }
221
359
  if (showSecretFields && secret !== secretConfirm) {
222
- setError("Secrets do not match");
223
- return;
360
+ return "Secrets do not match";
224
361
  }
225
- // Validate RequiredApprovals
226
362
  const requiredApprovalsNum = requiredApprovals
227
363
  ? parseInt(requiredApprovals, 10)
228
364
  : 1;
229
365
  if (isNaN(requiredApprovalsNum) || requiredApprovalsNum < 1) {
230
- setError("Required Approvals must be at least 1");
231
- return;
366
+ return "Required Approvals must be at least 1";
232
367
  }
233
368
  if (requiredApprovalsNum > reviewers.length) {
234
- setError(`Required Approvals (${requiredApprovalsNum}) cannot exceed the number of reviewers (${reviewers.length})`);
369
+ return `Required Approvals (${requiredApprovalsNum}) cannot exceed the number of reviewers (${reviewers.length})`;
370
+ }
371
+ return null;
372
+ };
373
+ const handleProceedToConfirm = async () => {
374
+ setShowErrors(true);
375
+ setError(null);
376
+ const validationError = validateForm();
377
+ if (validationError) {
378
+ setError(validationError);
379
+ return;
380
+ }
381
+ const suggestedTitle = await generateReviewTitleSuggestion();
382
+ if (suggestedTitle && !title.trim()) {
383
+ setTitle(suggestedTitle);
384
+ }
385
+ setStep("confirm");
386
+ };
387
+ const handleCreate = async () => {
388
+ setShowErrors(true);
389
+ setError(null);
390
+ const validationError = validateForm();
391
+ if (validationError) {
392
+ setError(validationError);
235
393
  return;
236
394
  }
237
395
  setLoading(true);
@@ -257,6 +415,9 @@ export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems
257
415
  // Single language mode - use legacy language field for backward compatibility
258
416
  languages = [language];
259
417
  }
418
+ const requiredApprovalsNum = requiredApprovals
419
+ ? parseInt(requiredApprovals, 10)
420
+ : 1;
260
421
  const request = {
261
422
  title: title || undefined,
262
423
  language: languageMode === "single" ? language : undefined, // Keep for backward compatibility
@@ -271,34 +432,39 @@ export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems
271
432
  const { createReview, getReviewStatus, sendInvitations } = await import("../services/reviewsService");
272
433
  const result = await createReview(request);
273
434
  if (result.data?.reviewId) {
435
+ setCreatedReviewId(result.data.reviewId);
274
436
  // Get assignments to send invitations
275
437
  const statusResult = await getReviewStatus(result.data.reviewId);
276
438
  if (statusResult.data?.assignments) {
277
- const assignmentIds = statusResult.data.assignments
278
- .filter((a) => !a.invitationSentDate)
279
- .map((a) => a.assignmentId);
439
+ const assignmentsToInvite = statusResult.data.assignments.filter((a) => !a.invitationSentDate);
440
+ const assignmentIds = assignmentsToInvite.map((a) => a.assignmentId);
441
+ const reviewerEmails = Array.from(new Set(assignmentsToInvite.map((a) => a.reviewerEmail)));
280
442
  if (assignmentIds.length > 0) {
281
- await sendInvitations(assignmentIds);
443
+ const invitationResult = await sendInvitations(assignmentIds);
444
+ if (invitationResult.type !== "success") {
445
+ throw new Error(invitationResult.details ||
446
+ invitationResult.summary ||
447
+ "Failed to send invitation emails");
448
+ }
449
+ if (invitationResult.data &&
450
+ typeof invitationResult.data === "object" &&
451
+ "errors" in invitationResult.data &&
452
+ Array.isArray(invitationResult.data.errors) &&
453
+ invitationResult.data.errors.length > 0) {
454
+ const errorMessages = invitationResult.data.errors
455
+ .map((err) => err?.message || err?.Message || String(err))
456
+ .join("; ");
457
+ throw new Error(errorMessages || "Failed to send invitation emails");
458
+ }
282
459
  }
460
+ setSentInvitationCount(assignmentIds.length);
461
+ setSentInvitationEmails(reviewerEmails);
462
+ }
463
+ else {
464
+ setSentInvitationCount(0);
465
+ setSentInvitationEmails([]);
283
466
  }
284
- onCreated(result.data.reviewId);
285
- // Reset form
286
- setTitle("");
287
- setReviewers([]);
288
- setItems([]);
289
- setExpirationDays("");
290
- setSecret("");
291
- setSecretConfirm("");
292
- setShowSecretFields(false);
293
- setRequiredApprovals("1");
294
- setUpdateWorkflowOnCompletion(false);
295
- setShowErrors(false);
296
- setLanguageMode("single");
297
- setSelectedLanguages([
298
- editContext?.currentItemDescriptor?.language || "en",
299
- ]);
300
- setLanguage(editContext?.currentItemDescriptor?.language || "en");
301
- onOpenChange(false);
467
+ setStep("success");
302
468
  }
303
469
  else {
304
470
  setError("Failed to create review");
@@ -314,8 +480,8 @@ export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems
314
480
  const addToList = async () => {
315
481
  const newItems = [...items];
316
482
  const nodesToAdd = selectedInTree.filter((node) => !newItems.some((x) => x.descriptor.id === node.id));
317
- // Fetch missing paths/names if needed
318
- const nodesNeedingFetch = nodesToAdd.filter((node) => !node.path || !node.name);
483
+ // Fetch missing paths/names/display names if needed
484
+ const nodesNeedingFetch = nodesToAdd.filter((node) => !node.path || !node.name || !node.displayName);
319
485
  if (nodesNeedingFetch.length > 0 && editContext?.itemsRepository) {
320
486
  try {
321
487
  const descriptorsToFetch = nodesNeedingFetch.map((node) => ({
@@ -333,6 +499,7 @@ export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems
333
499
  language: node.language || language,
334
500
  version: node.version || 1,
335
501
  name: stub?.name || node.name || "",
502
+ displayName: stub?.displayName || node.displayName || "",
336
503
  path: stub?.path || node.path || node.idPath || "",
337
504
  },
338
505
  includeSubitems: false,
@@ -349,6 +516,7 @@ export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems
349
516
  language: node.language || language,
350
517
  version: node.version || 1,
351
518
  name: node.name || "",
519
+ displayName: node.displayName || "",
352
520
  path: node.path || node.idPath || "",
353
521
  },
354
522
  includeSubitems: false,
@@ -365,6 +533,7 @@ export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems
365
533
  language: node.language || language,
366
534
  version: node.version || 1,
367
535
  name: node.name || "",
536
+ displayName: node.displayName || "",
368
537
  path: node.path || node.idPath || "",
369
538
  },
370
539
  includeSubitems: false,
@@ -387,10 +556,12 @@ export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems
387
556
  language: item.language || language,
388
557
  version: item.version || 1,
389
558
  name: item.name || "",
559
+ displayName: item.displayName || "",
390
560
  path: item.path || item.idPath || "",
391
561
  };
392
- // Fetch missing path/name if needed
393
- if ((!item.path || !item.name) && editContext?.itemsRepository) {
562
+ // Fetch missing path/name/display name if needed
563
+ if ((!item.path || !item.name || !item.displayName) &&
564
+ editContext?.itemsRepository) {
394
565
  try {
395
566
  const stubs = await editContext.itemsRepository.getItemsStubs([
396
567
  {
@@ -403,6 +574,7 @@ export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems
403
574
  itemDescriptor = {
404
575
  ...itemDescriptor,
405
576
  name: stubs[0].name || itemDescriptor.name || "",
577
+ displayName: stubs[0].displayName || itemDescriptor.displayName || "",
406
578
  path: stubs[0].path || itemDescriptor.path || "",
407
579
  };
408
580
  }
@@ -420,121 +592,36 @@ export function CreateReviewDialog({ open, onOpenChange, onCreated, initialItems
420
592
  ]);
421
593
  }
422
594
  };
423
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "flex max-w-5xl overflow-hidden border-none p-0 shadow-2xl", style: { height: "90vh", maxHeight: "90vh" }, children: [_jsx(StyledDialogTitle, { icon: _jsx(ClipboardCheck, { strokeWidth: 1 }), title: "Create Review" }), _jsxs("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden", children: [_jsx("div", { className: "flex min-h-0 flex-1 flex-col overflow-y-auto px-6 pb-4", children: _jsxs("div", { className: "flex flex-col gap-6 pt-6", children: [_jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs("label", { htmlFor: "title", className: "text-sm font-semibold text-gray-900", children: ["Review Name", _jsx("span", { className: "text-muted-foreground ml-1 text-xs font-normal", children: "(optional)" })] }), _jsx(Input, { id: "title", "data-testid": "review-title-input", value: title, onChange: (e) => setTitle(e.target.value), placeholder: "e.g., Homepage Q4 Review", className: "max-w-md bg-white" })] }), _jsx("div", { className: "border-t border-gray-100" }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { className: "text-sm font-semibold text-gray-900", children: "Pages & Items" }), _jsxs("span", { className: "bg-primary/10 text-primary rounded-full px-2.5 py-0.5 text-xs font-semibold", children: [items.length, " selected"] })] }), _jsx("p", { className: "text-muted-foreground text-xs", children: "Select the content items to include in this review. Double-click to add items quickly." }), _jsx("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", style: { height: "340px" }, children: _jsx(Splitter, { className: "h-full", direction: "horizontal", localStorageKey: "create-review-dialog-splitter", panels: [
424
- {
425
- name: "treeSelector",
426
- defaultSize: 50,
427
- content: (_jsx(TreeListSelector, { language: language, rootItemIds: rootItemIds, selectedItemIds: selectedInTree.map((item) => item.id), onSelectionChange: setSelectedInTree, onDoubleClick: handleAddItem, onItemSelected: handleAddItem })),
428
- },
429
- {
430
- name: "selectedItems",
431
- defaultSize: 50,
432
- content: (_jsxs("div", { className: "flex h-full min-h-0 flex-col overflow-hidden bg-gray-50/30", children: [_jsx("div", { className: "flex h-[37px] flex-shrink-0 items-center border-b border-gray-100 bg-gray-50/50 px-3", children: _jsx("span", { className: "text-xs font-semibold tracking-wider text-gray-600 uppercase", children: "Selected Items" }) }), items.length > 0 ? (_jsx("div", { className: "min-h-0 flex-1 space-y-2 overflow-y-auto p-3", children: items.map((item, index) => (_jsxs("div", { className: `group flex cursor-pointer flex-col gap-2 rounded-lg border p-3 transition-all ${selectedFromList.includes(item)
433
- ? "border-primary/30 bg-primary/5 ring-primary/20 ring-1"
434
- : "hover:border-primary/20 border-gray-200 bg-white hover:shadow-sm"}`, onClick: (e) => {
435
- if (e.ctrlKey || e.metaKey) {
436
- if (selectedFromList.includes(item)) {
437
- setSelectedFromList(selectedFromList.filter((i) => i !== item));
438
- }
439
- else {
440
- setSelectedFromList([
441
- ...selectedFromList,
442
- item,
443
- ]);
444
- }
445
- }
446
- else {
447
- if (selectedFromList.length === 1 &&
448
- selectedFromList.includes(item)) {
449
- setSelectedFromList([]);
450
- }
451
- else {
452
- setSelectedFromList([item]);
453
- }
454
- }
455
- }, onDoubleClick: () => {
456
- setItems(items.filter((_, i) => i !== index));
457
- setSelectedFromList([]);
458
- }, children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("span", { className: "truncate text-sm font-semibold text-gray-900", children: item.descriptor.name ||
459
- item.descriptor.path ||
460
- item.descriptor.id }), item.descriptor.path && (_jsx("span", { className: "text-muted-foreground truncate text-[11px]", title: item.descriptor.path, children: item.descriptor.path })), _jsxs("div", { className: "mt-1 flex items-center gap-1.5", children: [_jsx("span", { className: "rounded bg-gray-100 px-1.5 py-0.5 text-[10px] font-medium text-gray-600 uppercase", children: item.descriptor.language }), _jsxs("span", { className: "text-muted-foreground text-[10px] font-medium", children: ["v", item.descriptor.version] })] })] }), _jsx(Button, { variant: "ghost", size: "sm", className: "text-muted-foreground hover:bg-destructive/10 hover:text-destructive h-7 w-7 shrink-0 p-0 opacity-0 transition-all group-hover:opacity-100", onClick: (e) => {
461
- e.stopPropagation();
462
- removeItem(index);
463
- }, children: _jsx(X, { className: "h-4 w-4" }) })] }), _jsxs("div", { className: "flex items-center gap-2 border-t border-gray-100/50 pt-2", children: [_jsx(Switch, { id: `subpages-${item.descriptor.id}-${index}`, checked: item.includeSubitems, onCheckedChange: () => toggleSubitems(index), onClick: (e) => e.stopPropagation(), className: "scale-75" }), _jsx(Label, { htmlFor: `subpages-${item.descriptor.id}-${index}`, className: "cursor-pointer text-[11px] font-medium text-gray-500", onClick: (e) => e.stopPropagation(), children: "Include subitems" })] })] }, `${item.descriptor.id}-${index}`))) })) : (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col items-center justify-center gap-3 p-8 text-center", children: [_jsx("div", { className: "rounded-full bg-gray-100/50 p-4 ring-8 ring-gray-50", children: _jsx(ClipboardCheck, { className: "h-6 w-6 text-gray-300", strokeWidth: 1 }) }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-sm font-medium text-gray-500", children: "No items selected" }), _jsx("p", { className: "text-muted-foreground text-xs", children: "Browse or search to add items" })] })] }))] })),
464
- },
465
- ], handleContent: (index) => index === 0 ? (_jsxs("div", { className: "flex flex-col items-center gap-1 rounded bg-gray-100 py-1", children: [_jsx(SimpleIconButton, { label: "Add", icon: _jsx(ChevronRight, { size: 14, strokeWidth: 1 }), onClick: addToList, disabled: selectedInTree.length === 0, showTooltip: false, className: "px-0" }), _jsx(SimpleIconButton, { label: "Remove", icon: _jsx(ChevronLeft, { size: 14, strokeWidth: 1 }), onClick: removeFromList, disabled: selectedFromList.length === 0, showTooltip: false, className: "px-0" })] })) : null }) }), showErrors && items.length === 0 && (_jsx("p", { className: "text-destructive text-xs font-medium", children: "Please select at least one page or item" }))] }), _jsx("div", { className: "border-t border-gray-100" }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { className: "text-sm font-semibold text-gray-900", children: "Reviewers" }), reviewers.length > 0 && (_jsxs("span", { className: "bg-primary/10 text-primary rounded-full px-2.5 py-0.5 text-xs font-semibold", children: [reviewers.length, " added"] }))] }), _jsx("p", { className: "text-muted-foreground text-xs", children: "Add people who will review and approve the content." }), reviewers.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2.5", children: reviewers.map((reviewer, index) => (_jsxs("div", { className: "group hover:border-primary/30 flex items-center gap-2.5 rounded-full border border-gray-200 bg-white py-1.5 pr-2 pl-3.5 transition-all hover:shadow-sm", children: [_jsx("div", { className: "bg-primary/10 text-primary ring-primary/5 flex h-6 w-6 items-center justify-center rounded-full text-[10px] font-bold ring-2", children: reviewer.name.charAt(0).toUpperCase() }), _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { className: "text-xs leading-none font-semibold text-gray-900", children: reviewer.name }), _jsx("span", { className: "text-muted-foreground mt-1 text-[10px] leading-none", children: reviewer.email })] }), _jsx(Button, { variant: "ghost", size: "sm", className: "text-muted-foreground hover:bg-destructive/10 hover:text-destructive h-6 w-6 rounded-full p-0 opacity-50 transition-all hover:opacity-100", onClick: () => removeReviewer(index), children: _jsx(X, { className: "h-3.5 w-3.5" }) })] }, index))) })), _jsxs("div", { className: "flex flex-col gap-4 rounded-xl border border-gray-200 bg-gray-50/50 p-5 shadow-inner", children: [items.length > 0 && (_jsx(PreconfiguredReviewerSelector, { itemIds: itemIds, language: language, onReviewerSelected: (reviewer) => {
466
- const exists = reviewers.some((r) => r.email.toLowerCase() ===
467
- reviewer.email.toLowerCase());
468
- if (!exists) {
469
- setReviewers([...reviewers, reviewer]);
470
- }
471
- }, existingReviewers: reviewers })), _jsxs("div", { className: "flex flex-col gap-2.5", children: [_jsx("label", { className: "text-[11px] font-bold tracking-wider text-gray-500 uppercase", children: "Add new reviewer" }), _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "flex flex-1 flex-col gap-1", children: _jsx(Input, { "data-testid": "reviewer-name-input", placeholder: "Full Name", value: newReviewer.name, onChange: (e) => setNewReviewer({
472
- ...newReviewer,
473
- name: e.target.value,
474
- }), onKeyDown: (e) => {
475
- if (e.key === "Enter") {
476
- e.preventDefault();
477
- addReviewer();
478
- }
479
- }, className: "bg-white" }) }), _jsxs("div", { className: "flex flex-1 flex-col gap-1", children: [_jsx(Input, { "data-testid": "reviewer-email-input", placeholder: "Email address", value: newReviewer.email, onChange: (e) => setNewReviewer({
480
- ...newReviewer,
481
- email: e.target.value,
482
- }), onKeyDown: (e) => {
483
- if (e.key === "Enter") {
484
- e.preventDefault();
485
- addReviewer();
486
- }
487
- }, className: `bg-white ${newReviewer.email &&
488
- !isValidEmail(newReviewer.email)
489
- ? "border-destructive/30 focus:border-destructive"
490
- : ""}` }), newReviewer.email &&
491
- !isValidEmail(newReviewer.email) && (_jsx("span", { className: "text-destructive text-[10px] font-medium", children: "Invalid email address" })), newReviewer.email &&
492
- emailAlreadyExists(newReviewer.email) && (_jsx("span", { className: "text-destructive text-[10px] font-medium", children: "Reviewer already added" }))] }), _jsxs(Button, { onClick: addReviewer, size: "sm", "aria-label": "Add Reviewer", "data-testid": "add-reviewer-button", className: "h-9 shrink-0 px-3", children: [_jsx(Plus, { className: "mr-1.5 h-3.5 w-3.5" }), "Add"] })] })] })] }), showErrors && reviewers.length === 0 && (_jsx("p", { className: "text-destructive text-xs font-medium", children: "Please add at least one reviewer to create this review" }))] }), _jsx("div", { className: "border-t border-gray-100" }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("label", { className: "text-sm font-semibold text-gray-900", children: "Language" }), _jsx("p", { className: "text-muted-foreground text-xs", children: "Choose which language versions to include in the review." }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-wrap gap-6", children: [_jsxs("label", { className: "flex cursor-pointer items-center gap-2", children: [_jsx("input", { type: "radio", name: "languageMode", "data-testid": "language-mode-single", checked: languageMode === "single", onChange: () => {
493
- setLanguageMode("single");
494
- setLanguage(selectedLanguages[0] || language);
495
- }, className: "accent-primary h-4 w-4 cursor-pointer" }), _jsx("span", { className: "text-xs text-gray-700", children: "Single Language" })] }), _jsxs("label", { className: "flex cursor-pointer items-center gap-2", children: [_jsx("input", { type: "radio", name: "languageMode", "data-testid": "language-mode-multiple", checked: languageMode === "multiple", onChange: () => setLanguageMode("multiple"), className: "accent-primary h-4 w-4 cursor-pointer" }), _jsx("span", { className: "text-xs text-gray-700", children: "Multiple Languages" })] }), _jsxs("label", { className: "flex cursor-pointer items-center gap-2", children: [_jsx("input", { type: "radio", name: "languageMode", "data-testid": "language-mode-all", checked: languageMode === "all", onChange: () => setLanguageMode("all"), className: "accent-primary h-4 w-4 cursor-pointer" }), _jsx("span", { className: "text-xs text-gray-700", children: "All Languages" })] })] }), languageMode === "single" && (_jsx("div", { className: "max-w-xs", children: _jsx(LanguageSelector, { selectedLanguage: language, onLanguageSelected: (lang) => {
496
- setLanguage(lang.languageCode);
497
- setSelectedLanguages([lang.languageCode]);
498
- }, showAllLanguages: true }) })), languageMode === "multiple" && (_jsxs("div", { className: "flex max-h-56 flex-col gap-3 overflow-hidden rounded-xl border border-gray-200 bg-white p-4 shadow-sm", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 pb-3", children: [_jsx("span", { className: "text-[11px] font-bold tracking-wider text-gray-500 uppercase", children: "Select Languages" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => {
499
- const allLangs = editContext?.itemLanguages || [];
500
- setSelectedLanguages(allLangs.map((l) => l.languageCode));
501
- }, className: "text-primary hover:bg-primary/5 h-7 px-2 text-xs", children: "Select All" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setSelectedLanguages([]), className: "h-7 px-2 text-xs text-gray-500 hover:bg-gray-100", children: "Clear" })] })] }), _jsx("div", { className: "grid grid-cols-2 gap-2 overflow-y-auto pr-2", children: (editContext?.itemLanguages || []).map((lang) => (_jsxs("label", { className: "flex cursor-pointer items-center gap-3 rounded-md p-2 transition-all hover:bg-gray-50", children: [_jsx(Checkbox, { checked: selectedLanguages.includes(lang.languageCode), onCheckedChange: (checked) => {
502
- if (checked) {
503
- setSelectedLanguages([
504
- ...selectedLanguages,
505
- lang.languageCode,
506
- ]);
507
- }
508
- else {
509
- setSelectedLanguages(selectedLanguages.filter((l) => l !== lang.languageCode));
510
- }
511
- } }), _jsx("img", { src: lang.icon, className: "h-4 w-4 rounded-sm", alt: lang.name }), _jsx("span", { className: "text-xs font-medium text-gray-700", children: lang.name })] }, lang.languageCode))) }), showErrors && selectedLanguages.length === 0 && (_jsx("p", { className: "text-destructive mt-1 text-center text-xs font-medium", children: "Please select at least one language" }))] })), languageMode === "all" && (_jsx("div", { className: "rounded-lg border border-blue-100 bg-blue-50 p-4 text-sm leading-relaxed font-medium text-blue-700", children: "All available languages will be included in this review. Reviewers will see versions for every language the item exists in." }))] })] }), _jsx("div", { className: "border-t border-gray-100" }), _jsxs("div", { className: "mb-4 flex flex-col", children: [_jsxs("button", { type: "button", "data-testid": "advanced-settings-button", onClick: () => setAdvancedSettingsOpen(!advancedSettingsOpen), className: "flex w-full items-center justify-between rounded-xl border border-gray-200 bg-gray-50/50 px-5 py-4 text-left transition-all hover:bg-gray-100/80 hover:shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "rounded-lg border border-gray-100 bg-white p-2 shadow-sm", children: _jsx(Settings2, { className: "text-primary h-4 w-4" }) }), _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { className: "text-sm leading-tight font-semibold text-gray-900", children: "Advanced Settings" }), _jsx("span", { className: "text-muted-foreground mt-0.5 text-[11px]", children: "Expiration, approvals, security" })] })] }), _jsx(ChevronDown, { className: `h-5 w-5 text-gray-400 transition-transform duration-300 ${advancedSettingsOpen ? "rotate-180" : ""}` })] }), advancedSettingsOpen && (_jsxs("div", { className: "mt-4 flex flex-col gap-6 rounded-xl border border-gray-200 bg-white p-6 shadow-sm", children: [_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("label", { htmlFor: "requiredApprovals", className: "text-sm font-semibold text-gray-900", children: "Required Approvals" }), _jsx("p", { className: "text-muted-foreground text-xs", children: "Number of reviewers that must approve each page or item before it's considered complete." }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Input, { id: "requiredApprovals", "data-testid": "required-approvals-input", type: "number", min: "1", value: requiredApprovals, onChange: (e) => setRequiredApprovals(e.target.value), placeholder: "1", className: "w-24 bg-gray-50/50" }), _jsx("span", { className: "text-xs font-medium text-gray-400", children: "approvals needed" })] }), showErrors &&
512
- reviewers.length > 0 &&
513
- requiredApprovals &&
514
- (() => {
515
- const num = parseInt(requiredApprovals, 10);
516
- if (isNaN(num) || num < 1) {
517
- return (_jsx("p", { className: "text-destructive text-xs font-medium", children: "Must be at least 1" }));
518
- }
519
- if (num > reviewers.length) {
520
- return (_jsxs("p", { className: "text-destructive text-xs font-medium", children: ["Cannot exceed number of reviewers (", reviewers.length, ")"] }));
521
- }
522
- return null;
523
- })()] })] }), _jsx("div", { className: "border-t border-gray-100" }), _jsxs("div", { className: "flex items-start gap-4", children: [_jsx(Checkbox, { id: "updateWorkflowOnCompletion", "data-testid": "update-workflow-checkbox", checked: updateWorkflowOnCompletion, onCheckedChange: (checked) => setUpdateWorkflowOnCompletion(!!checked), className: "mt-1" }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Label, { htmlFor: "updateWorkflowOnCompletion", className: "cursor-pointer text-sm leading-tight font-semibold text-gray-900", children: "Update workflow state on completion" }), _jsx("span", { className: "text-muted-foreground text-xs leading-relaxed", children: "When the review is approved by all required reviewers, automatically execute the \"Approve\" workflow action." })] })] }), _jsx("div", { className: "border-t border-gray-100" }), _jsxs("div", { className: "flex items-start gap-4", children: [_jsx(Checkbox, { id: "hasExpiration", "data-testid": "set-expiration-checkbox", checked: expirationDays !== "", onCheckedChange: (checked) => {
524
- if (!checked) {
525
- setExpirationDays("");
526
- }
527
- else {
528
- setExpirationDays("30");
529
- }
530
- }, className: "mt-1" }), _jsxs("div", { className: "flex flex-1 flex-col gap-3", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Label, { htmlFor: "hasExpiration", className: "cursor-pointer text-sm leading-tight font-semibold text-gray-900", children: "Set expiration" }), _jsx("span", { className: "text-muted-foreground text-xs leading-relaxed", children: "Automatically close the review after a specified number of days if not completed." })] }), expirationDays !== "" && (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Input, { type: "number", "data-testid": "expiration-days-input", min: "1", value: expirationDays, onChange: (e) => setExpirationDays(e.target.value), placeholder: "30", className: "w-24 bg-gray-50/50" }), _jsx("span", { className: "text-xs font-medium text-gray-400", children: "days remaining" })] }))] })] }), _jsx("div", { className: "border-t border-gray-100" }), _jsxs("div", { className: "flex items-start gap-4", children: [_jsx(Checkbox, { id: "hasSecret", "data-testid": "require-secret-checkbox", checked: showSecretFields, onCheckedChange: (checked) => {
531
- setShowSecretFields(!!checked);
532
- if (!checked) {
533
- setSecret("");
534
- setSecretConfirm("");
535
- }
536
- }, className: "mt-1" }), _jsxs("div", { className: "flex flex-1 flex-col gap-3", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Label, { htmlFor: "hasSecret", className: "cursor-pointer text-sm leading-tight font-semibold text-gray-900", children: "Require secret to unlock" }), _jsx("span", { className: "text-muted-foreground text-xs leading-relaxed", children: "Reviewers must enter a shared secret phrase before they can access and review the content." })] }), showSecretFields && (_jsxs("div", { className: "grid max-w-md grid-cols-2 gap-3", children: [_jsx(Input, { "data-testid": "secret-input", type: "password", placeholder: "Create secret phrase", value: secret, onChange: (e) => setSecret(e.target.value), autoComplete: "off", className: "bg-gray-50/50" }), _jsx(Input, { "data-testid": "secret-confirm-input", type: "password", placeholder: "Confirm secret phrase", value: secretConfirm, onChange: (e) => setSecretConfirm(e.target.value), autoComplete: "off", className: "bg-gray-50/50" }), showErrors &&
537
- secret &&
538
- secret !== secretConfirm && (_jsx("p", { className: "text-destructive col-span-2 text-xs font-medium", children: "Secret phrases do not match" }))] }))] })] })] }))] }), error && (_jsx("div", { className: "border-destructive/20 bg-destructive/10 text-destructive rounded-xl border p-4 text-sm font-medium shadow-sm", children: _jsxs("div", { className: "flex gap-3", children: [_jsx(AlertTriangle, { className: "h-5 w-5 shrink-0" }), _jsx("p", { children: error })] }) }))] }) }), _jsxs(DialogButtons, { hideBorder: true, className: "bg-gray-50/50", children: [_jsx(Button, { variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }), _jsx(Button, { "data-testid": "create-review-submit-button", onClick: handleCreate, disabled: loading || !isFormValid, className: "min-w-[140px] shadow-sm", children: loading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Creating..."] })) : ("Create Review") })] })] })] }) }));
595
+ const handleDialogCancel = () => {
596
+ if (loading)
597
+ return;
598
+ resetDialogState();
599
+ onOpenChange(false);
600
+ };
601
+ const handleDialogOpenChange = (nextOpen) => {
602
+ if (!nextOpen) {
603
+ handleDialogCancel();
604
+ return;
605
+ }
606
+ onOpenChange(nextOpen);
607
+ };
608
+ const handleDone = () => {
609
+ const reviewId = createdReviewId;
610
+ resetDialogState();
611
+ onCreated(reviewId);
612
+ };
613
+ const languageSummary = languageMode === "all"
614
+ ? "All languages"
615
+ : languageMode === "multiple"
616
+ ? `${selectedLanguages.length} selected (${selectedLanguages.join(", ")})`
617
+ : language;
618
+ return (_jsx(Dialog, { open: open, onOpenChange: handleDialogOpenChange, children: _jsxs(DialogContent, { className: "flex max-w-5xl overflow-hidden border-none p-0 shadow-2xl", style: { height: "90vh", maxHeight: "90vh" }, children: [_jsx(StyledDialogTitle, { icon: step === "success" ? (_jsx(CheckCircle2, { strokeWidth: 1 })) : step === "confirm" ? (_jsx(MailCheck, { strokeWidth: 1 })) : (_jsx(ClipboardCheck, { strokeWidth: 1 })), title: step === "success"
619
+ ? "Review Created"
620
+ : step === "confirm"
621
+ ? "Confirm Review"
622
+ : "Create Review" }), _jsxs("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden", children: [_jsxs("div", { className: "flex min-h-0 flex-1 flex-col overflow-y-auto px-6 pb-4", children: [step === "details" && (_jsx(CreateReviewDetailsStep, { items: items, setItems: setItems, showErrors: showErrors, language: language, setLanguage: setLanguage, rootItemIds: rootItemIds, selectedInTree: selectedInTree, setSelectedInTree: setSelectedInTree, selectedFromList: selectedFromList, setSelectedFromList: setSelectedFromList, addToList: addToList, removeFromList: removeFromList, handleAddItem: handleAddItem, removeItem: removeItem, toggleSubitems: toggleSubitems, reviewers: reviewers, setReviewers: setReviewers, newReviewer: newReviewer, setNewReviewer: setNewReviewer, addReviewer: addReviewer, removeReviewer: removeReviewer, isValidEmail: isValidEmail, emailAlreadyExists: emailAlreadyExists, itemIds: itemIds, languageMode: languageMode, setLanguageMode: setLanguageMode, selectedLanguages: selectedLanguages, setSelectedLanguages: setSelectedLanguages, itemLanguages: editContext?.itemLanguages || [], advancedSettingsOpen: advancedSettingsOpen, setAdvancedSettingsOpen: setAdvancedSettingsOpen, requiredApprovals: requiredApprovals, setRequiredApprovals: setRequiredApprovals, updateWorkflowOnCompletion: updateWorkflowOnCompletion, setUpdateWorkflowOnCompletion: setUpdateWorkflowOnCompletion, expirationDays: expirationDays, setExpirationDays: setExpirationDays, showSecretFields: showSecretFields, setShowSecretFields: setShowSecretFields, secret: secret, setSecret: setSecret, secretConfirm: secretConfirm, setSecretConfirm: setSecretConfirm, error: error })), step === "confirm" && (_jsx(CreateReviewConfirmStep, { title: title, setTitle: setTitle, languageSummary: languageSummary, reviewers: reviewers, items: items, requiredApprovals: requiredApprovals, updateWorkflowOnCompletion: updateWorkflowOnCompletion, expirationDays: expirationDays, showSecretFields: showSecretFields, error: error })), step === "success" && (_jsx(CreateReviewSuccessStep, { sentInvitationCount: sentInvitationCount, sentInvitationEmails: sentInvitationEmails }))] }), _jsxs(DialogButtons, { hideBorder: true, className: "bg-gray-50/50", children: [step === "details" && (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", onClick: handleDialogCancel, children: "Cancel" }), _jsx(Button, { "data-testid": "create-review-submit-button", onClick: handleProceedToConfirm, disabled: loading || isGeneratingTitle || !isFormValid, className: "min-w-[140px] shadow-sm", children: isGeneratingTitle ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Generating Name..."] })) : ("Continue") })] })), step === "confirm" && (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", onClick: () => {
623
+ setError(null);
624
+ setStep("details");
625
+ }, disabled: loading, children: "Back" }), _jsx(Button, { "data-testid": "create-review-submit-button", onClick: handleCreate, disabled: loading, className: "min-w-[160px] shadow-sm", children: loading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Creating..."] })) : ("Create & Send Emails") })] })), step === "success" && (_jsx(Button, { onClick: handleDone, className: "min-w-[120px] shadow-sm", children: "Done" }))] })] })] }) }));
539
626
  }
540
627
  //# sourceMappingURL=CreateReviewDialog.js.map