feat(web-ongoing-operations): add designated agent foa validation - #23154
feat(web-ongoing-operations): add designated agent foa validation#23154louisbensiovh wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a “Designated Agent” FOA (Form of Authorization) validation flow for DomainTrade ongoing operations in web-ongoing-operations, including APIv2 integration, UI entry points from the datagrid actions, and a dedicated validation page.
Changes:
- Added APIv2 FOA/task fetching + validation client, with React Query hooks and utility helpers.
- Added a new FOA validation page and a new datagrid actions component to expose the FOA CTA when applicable.
- Updated tests, mocks, routes, and dashboard translations to support the new flow.
Reviewed changes
Copilot reviewed 29 out of 29 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/manager/apps/web-ongoing-operations/src/utils/foa.utils.ts | Adds FOA “pending” predicate and “most recent task” selection helper. |
| packages/manager/apps/web-ongoing-operations/src/utils/foa.utils.spec.ts | Unit tests for FOA utils. |
| packages/manager/apps/web-ongoing-operations/src/types/index.ts | Introduces APIv2 task + FOA types and an actions props type. |
| packages/manager/apps/web-ongoing-operations/src/setupTests.tsx | Extends test mocks (core-api v2 + stable navigate spy + params). |
| packages/manager/apps/web-ongoing-operations/src/routes/routes.tsx | Registers the new FOA validation route/page. |
| packages/manager/apps/web-ongoing-operations/src/routes/routes.constant.ts | Adds FOA route pattern. |
| packages/manager/apps/web-ongoing-operations/src/pages/foa/FoaValidation.tsx | New FOA validation page composition and gating logic. |
| packages/manager/apps/web-ongoing-operations/src/pages/foa/FoaValidation.spec.tsx | Page-level tests for validation behavior, navigation, and errors. |
| packages/manager/apps/web-ongoing-operations/src/pages/dashboard/domain/Domain.spec.tsx | Adjusts mocks to account for new FOA-related hook usage. |
| packages/manager/apps/web-ongoing-operations/src/pages/dashboard/dns/Dns.spec.tsx | Adjusts mocks to account for new FOA-related hook usage. |
| packages/manager/apps/web-ongoing-operations/src/pages/dashboard/allDom/AllDom.spec.tsx | Adjusts mocks to account for new FOA-related hook usage. |
| packages/manager/apps/web-ongoing-operations/src/hooks/useOngoingOperationDatagridColumns.tsx | Swaps inline menu for a dedicated actions component. |
| packages/manager/apps/web-ongoing-operations/src/hooks/data/query.tsx | Adds FOA/task queries and the usePendingFoas hook. |
| packages/manager/apps/web-ongoing-operations/src/enum/foa.enum.ts | Adds FOA choice enum for validate calls. |
| packages/manager/apps/web-ongoing-operations/src/data/api/foa.ts | Implements APIv2 FOA/task endpoints and validate action. |
| packages/manager/apps/web-ongoing-operations/src/data/api/foa.spec.ts | Unit tests for FOA API client functions. |
| packages/manager/apps/web-ongoing-operations/src/constants.ts | Adds the scheduled-task status constant used for FOA filtering. |
| packages/manager/apps/web-ongoing-operations/src/components/OngoingOperationDatagrid/OngoingOperationDatagridActions.tsx | New actions menu component including FOA CTA and navigation. |
| packages/manager/apps/web-ongoing-operations/src/components/OngoingOperationDatagrid/OngoingOperationDatagridActions.spec.tsx | Tests ensuring FOA CTA visibility and navigation behavior. |
| packages/manager/apps/web-ongoing-operations/src/components/Foa/FoaActions.component.tsx | Implements accept/reject flow (multi-FOA, 409-idempotency, notifications). |
| packages/manager/apps/web-ongoing-operations/src/components/Dashboard/DashboardPage.spec.tsx | Adjusts mocks to account for new FOA-related hook usage. |
| packages/manager/apps/web-ongoing-operations/public/translations/dashboard/Messages_pt_PT.json | Adds FOA CTA/page/action strings (pt-PT). |
| packages/manager/apps/web-ongoing-operations/public/translations/dashboard/Messages_pl_PL.json | Adds FOA CTA/page/action strings (pl-PL). |
| packages/manager/apps/web-ongoing-operations/public/translations/dashboard/Messages_it_IT.json | Adds FOA CTA/page/action strings (it-IT). |
| packages/manager/apps/web-ongoing-operations/public/translations/dashboard/Messages_fr_FR.json | Adds FOA CTA/page/action strings (fr-FR). |
| packages/manager/apps/web-ongoing-operations/public/translations/dashboard/Messages_fr_CA.json | Adds FOA CTA/page/action strings (fr-CA). |
| packages/manager/apps/web-ongoing-operations/public/translations/dashboard/Messages_es_ES.json | Adds FOA CTA/page/action strings (es-ES). |
| packages/manager/apps/web-ongoing-operations/public/translations/dashboard/Messages_en_GB.json | Adds FOA CTA/page/action strings (en-GB). |
| packages/manager/apps/web-ongoing-operations/public/translations/dashboard/Messages_de_DE.json | Adds FOA CTA/page/action strings (de-DE). |
Suppressed comments (2)
packages/manager/apps/web-ongoing-operations/src/components/OngoingOperationDatagrid/OngoingOperationDatagridActions.tsx:50
- This
classNameexpression can evaluate to"false menu-item-button"when the condition is not met, because template literals stringifyfalse. Prefer a ternary (or clsx) so the "not hidden" case produces an empty string rather than the literal "false" class.
label: t('domain_operations_tab_popover_progress'),
className: `${props.function !==
DomainOperationsEnum.DomainIncomingTransfer &&
'hidden'} menu-item-button`,
packages/manager/apps/web-ongoing-operations/src/components/OngoingOperationDatagrid/OngoingOperationDatagridActions.tsx:61
- Same issue here:
${!canValidateFoa && 'hidden'}becomes the literal string "false" in the rendered class list whencanValidateFoais true. Use a ternary (or clsx) to avoid emitting a bogusfalseclass.
id: 3,
label: t('domain_operations_foa_cta'),
className: `${!canValidateFoa && 'hidden'} menu-item-button`,
onClick: () => {
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| id: 1, | ||
| label: t('domain_operations_tab_popover_update'), | ||
| className: `${!canUpdate && 'hidden'} menu-item-button`, | ||
| onClick: () => { |
| cell: (props: TOngoingOperations) => ( | ||
| <ActionMenu | ||
| id={`${props.id}`} | ||
| isCompact | ||
| isDisabled={ | ||
| !props.canAccelerate && !props.canRelaunch && !props.canCancel | ||
| } | ||
| variant={ODS_BUTTON_VARIANT.ghost} | ||
| items={[ | ||
| { | ||
| id: 1, | ||
| label: t('domain_operations_tab_popover_update'), | ||
| className: `${!props.canAccelerate && | ||
| !props.canRelaunch && | ||
| !props.canCancel && | ||
| 'hidden'} menu-item-button`, | ||
| onClick: () => { | ||
| const url = `${location.pathname}/update/${props.id}`; | ||
| trackPageNavivationTile(url); | ||
| navigate(url); | ||
| clearNotifications(); | ||
| }, | ||
| }, | ||
| { | ||
| id: 2, | ||
| label: t('domain_operations_tab_popover_progress'), | ||
| className: `${props.function !== | ||
| DomainOperationsEnum.DomainIncomingTransfer && | ||
| 'hidden'} menu-item-button`, | ||
| onClick: () => { | ||
| const url = `/tracking/${props.id}`; | ||
| trackPageNavivationTile(url); | ||
| navigate(url); | ||
| }, | ||
| }, | ||
| ]} | ||
| /> | ||
| <OngoingOperationDatagridActions props={props} /> | ||
| ), |
d3f2cf5 to
ed6376b
Compare
ref: #D2I-6259 Signed-off-by: Louis BENSI <louis.bensi@corp.ovh.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ed6376b to
6f0a409
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 28 changed files in this pull request and generated 2 comments.
Suppressed comments (3)
packages/manager/apps/web-ongoing-operations/src/components/OngoingOperationDatagrid/OngoingOperationDatagridActions.tsx:37
- Using a template literal with
&&here will inject the string "false" into the class list when the condition is falsy (e.g.,"false menu-item-button"). Use a ternary (or a classnames helper) to avoid unintended classes.
className: `${!canUpdate && 'hidden'} menu-item-button`,
packages/manager/apps/web-ongoing-operations/src/components/OngoingOperationDatagrid/OngoingOperationDatagridActions.tsx:50
- This multi-line
&& 'hidden'expression in a template literal will also produce a literal "false" class when the condition is false. Prefer a ternary so the class list is either "hidden menu-item-button" or just "menu-item-button".
className: `${props.function !==
DomainOperationsEnum.DomainIncomingTransfer &&
'hidden'} menu-item-button`,
packages/manager/apps/web-ongoing-operations/src/components/OngoingOperationDatagrid/OngoingOperationDatagridActions.tsx:60
- Same issue as above: interpolating
!canValidateFoa && 'hidden'into a template literal yields an extra "false" class when the item is visible. Use a ternary (or classnames helper) instead.
className: `${!canValidateFoa && 'hidden'} menu-item-button`,
| const isTrade = props.function === DomainOperationsEnum.DomainTrade; | ||
| // Only a change of registrant carries FOAs, so no other row fetches them | ||
| const { taskId, pendingFoas } = usePendingFoas(props.domain ?? '', isTrade); |
| export const getMostRecentTask = ( | ||
| tasks: TDomainTaskV2[], | ||
| ): TDomainTaskV2 | null => | ||
| [...(tasks ?? [])].sort( | ||
| (a, b) => getTaskTimestamp(b) - getTaskTimestamp(a), | ||
| )[0] ?? null; |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 28 changed files in this pull request and generated no new comments.
Suppressed comments (4)
packages/manager/apps/web-ongoing-operations/src/components/OngoingOperationDatagrid/OngoingOperationDatagridActions.tsx:37
- Using a boolean expression inside a template string can produce an unwanted "false" CSS class when
canUpdateis true, because${!canUpdate && 'hidden'}evaluates tofalseand gets stringified. Use a ternary (or a classnames helper) so the className is either "hidden" or "".
className: `${!canUpdate && 'hidden'} menu-item-button`,
packages/manager/apps/web-ongoing-operations/src/components/OngoingOperationDatagrid/OngoingOperationDatagridActions.tsx:50
- This template-string pattern can also stringify to "false" when the condition is not met, adding an unintended class name. Prefer a ternary so the className is stable and only adds "hidden" when needed.
className: `${props.function !==
DomainOperationsEnum.DomainIncomingTransfer &&
'hidden'} menu-item-button`,
packages/manager/apps/web-ongoing-operations/src/components/OngoingOperationDatagrid/OngoingOperationDatagridActions.tsx:60
- Same issue here:
${!canValidateFoa && 'hidden'}becomes the literal "false" class whencanValidateFoais true. Use a ternary to avoid emitting "false" into className.
className: `${!canValidateFoa && 'hidden'} menu-item-button`,
packages/manager/apps/web-ongoing-operations/src/components/OngoingOperationDatagrid/OngoingOperationDatagridActions.tsx:22
usePendingFoastriggers at least one (and potentially two) APIv2 queries per trade row to decide whether to show the FOA entry point. In a datagrid with many trade operations, this becomes an N+1 client-side fetch pattern and can noticeably slow down the listing / increase API load. Consider deferring the FOA lookup until the user interacts (e.g., when opening the menu / clicking the CTA), or aggregating the FOA availability into the main listing payload so the grid can decide without per-row queries.
const isTrade = props.function === DomainOperationsEnum.DomainTrade;
// Only a change of registrant carries FOAs, so no other row fetches them
const { taskId, pendingFoas } = usePendingFoas(props.domain ?? '', isTrade);
Ticket Reference: #D2I-6259