From aedfa43d4596e558a21676c3cc1acfc875c79407 Mon Sep 17 00:00:00 2001 From: Jackie Jou Date: Tue, 30 Jun 2026 13:18:37 -0700 Subject: [PATCH 1/2] feat(activity-feed-v2): wire TaskFormV2 into TaskModalV2 with edit prefill and error handling --- i18n/en-US.properties | 32 +- .../task-modal-v2/TaskFormV2.tsx | 26 ++ .../task-modal-v2/TaskModalV2.scss | 9 +- .../task-modal-v2/TaskModalV2.tsx | 298 +++++++++++++- .../__tests__/TaskFormV2.test.tsx | 27 ++ .../__tests__/TaskModalV2.test.tsx | 382 +++++++++++++++++- .../task-modal-v2/messages.ts | 75 +++- 7 files changed, 803 insertions(+), 46 deletions(-) diff --git a/i18n/en-US.properties b/i18n/en-US.properties index 36ae547e2f..dd1c21194f 100644 --- a/i18n/en-US.properties +++ b/i18n/en-US.properties @@ -892,14 +892,22 @@ be.taskModalV2.assigneeFieldRequiredError = Required Field be.taskModalV2.assigneePlaceholder = Add an assignee or group # Label for the assignee combobox in the task modal be.taskModalV2.assigneeSelectorLabel = Select Assignees +# Label for the Cancel button in the task modal footer +be.taskModalV2.cancelButtonLabel = Cancel # aria-label for the task modal close button be.taskModalV2.close = Close # Label for the checkbox that switches a task to any-assignee completion be.taskModalV2.completionRuleCheckboxLabel = Only one assignee is required to complete this task # Title of the modal for creating an approval task be.taskModalV2.createApprovalTask = Create Approval Task +# Label for the Create button in the task modal footer (create mode) +be.taskModalV2.createButtonLabel = Create # Title of the modal for creating a general task be.taskModalV2.createGeneralTask = Create General Task +# Body text of the error notice when creating a task fails +be.taskModalV2.createTaskErrorMessage = An error occurred while creating this task. Please try again. +# Title of the error notice when creating or updating a task fails +be.taskModalV2.createTaskErrorTitle = Error # aria-label for the calendar inside the due-date date picker be.taskModalV2.datePickerCalendarAriaLabel = Due date calendar # aria-label for the clear button on the due-date date picker @@ -910,18 +918,38 @@ be.taskModalV2.datePickerNextMonthAriaLabel = Next month be.taskModalV2.datePickerOpenAriaLabel = Open due date calendar # aria-label for the previous-month button in the due-date date picker be.taskModalV2.datePickerPreviousMonthAriaLabel = Previous month -# Label for the due-date field in the task modal -be.taskModalV2.dueDateLabel = Due Date +# Label for the due-date field in the task modal; due date is optional +be.taskModalV2.dueDateLabel = Due Date (optional) # Title of the modal for editing an existing approval task be.taskModalV2.editApprovalTask = Modify Approval Task +# Warning shown when removing assignees from an approved task fails with 403 +be.taskModalV2.editApprovalTaskForbiddenMessage = Unable to remove assignee(s) because the task is now approved. +# Title of the warning notice when an edit partially fails (403 on update) +be.taskModalV2.editForbiddenTitle = Task Updated with Errors # Title of the modal for editing an existing general task be.taskModalV2.editGeneralTask = Modify General Task +# Warning shown when removing assignees from a completed task fails with 403 +be.taskModalV2.editGeneralTaskForbiddenMessage = Unable to remove assignee(s) because the task is now completed. +# Body of the warning notice when a group assignee exceeds the per-group maximum +be.taskModalV2.groupExceedsLimitWarningMessage = One or more groups can not receive this task as a group size cannot exceed the limit of {max} assignees per group. +# Title of the warning notice when a group assignee exceeds the per-group maximum +be.taskModalV2.groupExceedsLimitWarningTitle = Exceeded max assignees per group +# aria-label for the error variant icon in the task modal inline notice +be.taskModalV2.inlineNoticeErrorAriaLabel = Error +# aria-label for the warning variant icon in the task modal inline notice +be.taskModalV2.inlineNoticeWarningAriaLabel = Warning +# aria-label for the loading indicator on the submit button while a task is saving +be.taskModalV2.loadingAriaLabel = Loading # Error message when the task message field is empty on submit be.taskModalV2.messageFieldRequiredError = Required Field # Label for the message field in the task modal be.taskModalV2.messageLabel = Message # Placeholder text for the message field in the task modal be.taskModalV2.messagePlaceholder = Write a message +# Label for the Update button in the task modal footer (edit mode) +be.taskModalV2.updateButtonLabel = Update +# Body text of the error notice when updating an existing task fails +be.taskModalV2.updateTaskErrorMessage = An error occurred while modifying this task. Please try again. # Shown instead of todays date. be.today = today # Label for keywords/topics skill section in the preview sidebar diff --git a/src/elements/content-sidebar/activity-feed-v2/task-modal-v2/TaskFormV2.tsx b/src/elements/content-sidebar/activity-feed-v2/task-modal-v2/TaskFormV2.tsx index b336a0dd71..40e201a162 100644 --- a/src/elements/content-sidebar/activity-feed-v2/task-modal-v2/TaskFormV2.tsx +++ b/src/elements/content-sidebar/activity-feed-v2/task-modal-v2/TaskFormV2.tsx @@ -36,6 +36,7 @@ export type TaskFormV2Props = { initialMessage?: string; isDisabled?: boolean; onSubmit: (payload: TaskFormV2SubmitPayload) => void | Promise; + taskId?: string; taskType: TaskType; }; @@ -74,6 +75,7 @@ const TaskFormV2 = ({ initialMessage = '', isDisabled = false, onSubmit, + taskId = '', taskType, }: TaskFormV2Props) => { const { formatMessage } = useIntl(); @@ -108,6 +110,25 @@ const TaskFormV2 = ({ ? formatMessage(messages.assigneeFieldRequiredError) : undefined; + const resinTags = React.useMemo(() => { + const initialIds = new Set(initialAssignees.map(a => a.target.id)); + const currentIds = new Set(selectedUsers.map(u => u.value)); + const added = selectedUsers.filter(u => !initialIds.has(u.value)); + const removed = initialAssignees.filter(a => !currentIds.has(a.target.id)); + const userCount = added.filter(u => u.type === 'user').length; + const groupCount = added.filter(u => u.type === 'group').length; + const submitDate = dueDate ? toSubmitDate(dueDate, originalDueDateTime).getTime() : undefined; + return { + 'data-resin-assigneesadded': added.map(u => u.value).join(','), + 'data-resin-assigneesremoved': removed.map(a => a.target.id).join(','), + 'data-resin-duedate': submitDate, + 'data-resin-numassigneesadded': userCount, + 'data-resin-numassigneesremoved': removed.length, + 'data-resin-numgroupsadded': groupCount, + 'data-resin-taskid': taskId, + }; + }, [dueDate, initialAssignees, originalDueDateTime, selectedUsers, taskId]); + const minCalendarDate = React.useMemo(() => { const todayDate = today(getLocalTimeZone()); if (initialDueDate && initialDueDate < todayDate.toDate(getLocalTimeZone())) { @@ -142,8 +163,10 @@ const TaskFormV2 = ({ data-resin-tasktype={taskType} id={TASK_FORM_V2_ID} onSubmit={handleFormSubmit} + {...resinTags} > )}