diff --git a/packages/api-v4/src/account/types.ts b/packages/api-v4/src/account/types.ts index 02e3cbf3ec8..7921bdc4543 100644 --- a/packages/api-v4/src/account/types.ts +++ b/packages/api-v4/src/account/types.ts @@ -258,7 +258,8 @@ export type NotificationType = | 'promotion' | 'user_email_bounce' | 'volume_migration_scheduled' - | 'volume_migration_imminent'; + | 'volume_migration_imminent' + | 'tax_id_invalid'; export type NotificationSeverity = 'minor' | 'major' | 'critical'; @@ -434,6 +435,7 @@ export const EventActionKeys = [ 'tag_create', 'tag_delete', 'tax_id_invalid', + 'tax_id_valid', 'tfa_disabled', 'tfa_enabled', 'ticket_attachment_upload', diff --git a/packages/manager/.changeset/pr-10558-upcoming-features-1719516602980.md b/packages/manager/.changeset/pr-10558-upcoming-features-1719516602980.md new file mode 100644 index 00000000000..76324e159a3 --- /dev/null +++ b/packages/manager/.changeset/pr-10558-upcoming-features-1719516602980.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Tax ID Notifications & Warning Icon ([#10558](https://github.com/linode/manager/pull/10558)) diff --git a/packages/manager/cypress/e2e/core/notificationsAndEvents/events.spec.ts b/packages/manager/cypress/e2e/core/notificationsAndEvents/events.spec.ts index 421c819dd14..0adc8a7f8c1 100644 --- a/packages/manager/cypress/e2e/core/notificationsAndEvents/events.spec.ts +++ b/packages/manager/cypress/e2e/core/notificationsAndEvents/events.spec.ts @@ -75,6 +75,8 @@ const eventActions: RecPartial[] = [ 'subnet_create', 'subnet_delete', 'subnet_update', + 'tax_id_invalid', + 'tax_id_valid', 'tfa_disabled', 'tfa_enabled', 'user_ssh_key_add', diff --git a/packages/manager/src/components/TooltipIcon.tsx b/packages/manager/src/components/TooltipIcon.tsx index fc982c1a1b6..ab4c83c1d2a 100644 --- a/packages/manager/src/components/TooltipIcon.tsx +++ b/packages/manager/src/components/TooltipIcon.tsx @@ -3,15 +3,17 @@ import SuccessOutline from '@mui/icons-material/CheckCircleOutlined'; import ErrorOutline from '@mui/icons-material/ErrorOutline'; import HelpOutline from '@mui/icons-material/HelpOutline'; import InfoOutline from '@mui/icons-material/InfoOutlined'; -import WarningOutline from '@mui/icons-material/WarningAmberOutlined'; +import WarningSolid from '@mui/icons-material/Warning'; import { useTheme } from '@mui/material/styles'; -import { SxProps } from '@mui/system'; import * as React from 'react'; import { IconButton } from 'src/components/IconButton'; -import { Tooltip, TooltipProps, tooltipClasses } from 'src/components/Tooltip'; +import { Tooltip, tooltipClasses } from 'src/components/Tooltip'; import { omittedProps } from 'src/utilities/omittedProps'; +import type { SxProps } from '@mui/system'; +import type { TooltipProps } from 'src/components/Tooltip'; + type TooltipIconStatus = | 'error' | 'help' @@ -132,7 +134,7 @@ export const TooltipIcon = (props: TooltipIconProps) => { renderIcon = ; break; case 'warning': - renderIcon = ; + renderIcon = ; break; case 'info': renderIcon = ; diff --git a/packages/manager/src/features/Billing/BillingPanels/ContactInfoPanel/ContactInformation.tsx b/packages/manager/src/features/Billing/BillingPanels/ContactInfoPanel/ContactInformation.tsx index 9a4256a0df6..3364e13861a 100644 --- a/packages/manager/src/features/Billing/BillingPanels/ContactInfoPanel/ContactInformation.tsx +++ b/packages/manager/src/features/Billing/BillingPanels/ContactInfoPanel/ContactInformation.tsx @@ -4,10 +4,13 @@ import { allCountries } from 'country-region-data'; import * as React from 'react'; import { useHistory, useRouteMatch } from 'react-router-dom'; +import { Box } from 'src/components/Box'; +import { TooltipIcon } from 'src/components/TooltipIcon'; import { Typography } from 'src/components/Typography'; import { getRestrictedResourceText } from 'src/features/Account/utils'; import { EDIT_BILLING_CONTACT } from 'src/features/Billing/constants'; import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck'; +import { useNotificationsQuery } from 'src/queries/account/notifications'; import { BillingActionButton, @@ -74,10 +77,16 @@ const ContactInformation = (props: Props) => { setEditContactDrawerOpen, ] = React.useState(false); + const { data: notifications } = useNotificationsQuery(); + const [focusEmail, setFocusEmail] = React.useState(false); const isChildUser = Boolean(profile?.user_type === 'child'); + const invalidTaxIdNotification = notifications?.find((notification) => { + return notification.type === 'tax_id_invalid'; + }); + const isReadOnly = useRestrictedGlobalGrantCheck({ globalGrantType: 'account_access', @@ -220,9 +229,28 @@ const ContactInformation = (props: Props) => { {phone} )} {taxId && ( - - Tax ID {taxId} - + + + Tax ID {taxId} + + {invalidTaxIdNotification && ( + svg': { + fontSize: '18px', + }, + paddingBottom: 0, + paddingTop: 0, + }} + status="warning" + text={invalidTaxIdNotification.label} + /> + )} + )} diff --git a/packages/manager/src/features/Billing/BillingPanels/ContactInfoPanel/UpdateContactInformationForm/UpdateContactInformationForm.tsx b/packages/manager/src/features/Billing/BillingPanels/ContactInfoPanel/UpdateContactInformationForm/UpdateContactInformationForm.tsx index a9402f6a85e..76b13950a9d 100644 --- a/packages/manager/src/features/Billing/BillingPanels/ContactInfoPanel/UpdateContactInformationForm/UpdateContactInformationForm.tsx +++ b/packages/manager/src/features/Billing/BillingPanels/ContactInfoPanel/UpdateContactInformationForm/UpdateContactInformationForm.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; -import EnhancedSelect, { Item } from 'src/components/EnhancedSelect/Select'; +import EnhancedSelect from 'src/components/EnhancedSelect/Select'; import { Notice } from 'src/components/Notice/Notice'; import { TextField } from 'src/components/TextField'; import { @@ -19,6 +19,8 @@ import { useNotificationsQuery } from 'src/queries/account/notifications'; import { useProfile } from 'src/queries/profile/profile'; import { getErrorMap } from 'src/utilities/errorUtils'; +import type { Item } from 'src/components/EnhancedSelect/Select'; + interface Props { focusEmail: boolean; onClose: () => void; diff --git a/packages/manager/src/features/Events/constants.ts b/packages/manager/src/features/Events/constants.ts index ad07694713c..8d07b3e0273 100644 --- a/packages/manager/src/features/Events/constants.ts +++ b/packages/manager/src/features/Events/constants.ts @@ -98,6 +98,7 @@ export const EVENT_ACTIONS: Event['action'][] = [ 'subnet_delete', 'subnet_update', 'tax_id_invalid', + 'tax_id_valid', 'tfa_disabled', 'tfa_enabled', 'ticket_attachment_upload', diff --git a/packages/manager/src/features/Events/eventMessageGenerator.ts b/packages/manager/src/features/Events/eventMessageGenerator.ts index 95d06003d74..f96aa48932a 100644 --- a/packages/manager/src/features/Events/eventMessageGenerator.ts +++ b/packages/manager/src/features/Events/eventMessageGenerator.ts @@ -865,6 +865,9 @@ export const eventMessageCreators: { [index: string]: CreatorsForStatus } = { tax_id_invalid: { notification: (e) => `Tax Identification Number format is invalid.`, }, + tax_id_valid: { + notification: (e) => `Tax Identification Number has been verified.`, + }, tfa_disabled: { notification: (e) => `Two-factor authentication has been disabled.`, }, diff --git a/packages/manager/src/features/Events/factories/tax.tsx b/packages/manager/src/features/Events/factories/tax.tsx index 5ac7cb45211..0cb576dbca4 100644 --- a/packages/manager/src/features/Events/factories/tax.tsx +++ b/packages/manager/src/features/Events/factories/tax.tsx @@ -10,4 +10,11 @@ export const tax: PartialEventMap<'tax'> = { ), }, + tax_id_valid: { + notification: () => ( + <> + Tax Identification Number has been verified. + + ), + }, }; diff --git a/packages/manager/src/hooks/useEventHandlers.ts b/packages/manager/src/hooks/useEventHandlers.ts index d4692ae1f42..da32d9a3059 100644 --- a/packages/manager/src/hooks/useEventHandlers.ts +++ b/packages/manager/src/hooks/useEventHandlers.ts @@ -1,17 +1,18 @@ import { useQueryClient } from '@tanstack/react-query'; +import { taxIdEventHandler } from 'src/queries/account/billing'; import { oauthClientsEventHandler } from 'src/queries/account/oauth'; import { databaseEventsHandler } from 'src/queries/databases/events'; import { domainEventsHandler } from 'src/queries/domains'; import { firewallEventsHandler } from 'src/queries/firewalls'; import { imageEventsHandler } from 'src/queries/images'; -import { diskEventHandler } from 'src/queries/linodes/events'; import { linodeEventsHandler } from 'src/queries/linodes/events'; +import { diskEventHandler } from 'src/queries/linodes/events'; import { nodebalancerEventHandler } from 'src/queries/nodebalancers'; import { sshKeyEventHandler } from 'src/queries/profile/profile'; +import { tokenEventHandler } from 'src/queries/profile/tokens'; import { stackScriptEventHandler } from 'src/queries/stackscripts'; import { supportTicketEventHandler } from 'src/queries/support'; -import { tokenEventHandler } from 'src/queries/profile/tokens'; import { volumeEventsHandler } from 'src/queries/volumes/events'; import type { Event } from '@linode/api-v4'; @@ -81,6 +82,10 @@ export const eventHandlers: { filter: (event) => event.action.startsWith('stackscript'), handler: stackScriptEventHandler, }, + { + filter: (event) => event.action.startsWith('tax_id'), + handler: taxIdEventHandler, + }, ]; export const useEventHandlers = () => { diff --git a/packages/manager/src/hooks/useToastNotifications.tsx b/packages/manager/src/hooks/useToastNotifications.tsx index d62593921b7..8be6974168a 100644 --- a/packages/manager/src/hooks/useToastNotifications.tsx +++ b/packages/manager/src/hooks/useToastNotifications.tsx @@ -164,7 +164,8 @@ const toasts: Toasts = { failure: { message: 'Error validating Tax Identification Number.' }, invertVariant: true, success: { - message: 'Tax Identification Number could not be verified.', + message: + 'Tax Identification Number could not be verified. Please check your Tax ID for accuracy or contact support for assistance.', persist: true, }, }, diff --git a/packages/manager/src/queries/account/account.ts b/packages/manager/src/queries/account/account.ts index 55f242914d6..14c250d610f 100644 --- a/packages/manager/src/queries/account/account.ts +++ b/packages/manager/src/queries/account/account.ts @@ -60,11 +60,6 @@ export const useMutateAccount = () => { "You edited the Tax Identification Number. It's being verified. You'll get an email with the verification result.", { hideIconVariant: false, - style: { - display: 'flex', - flexWrap: 'nowrap', - width: '372px', - }, variant: 'info', } ); diff --git a/packages/manager/src/queries/account/billing.ts b/packages/manager/src/queries/account/billing.ts index 61b6ffd79cb..0b85e8c0cc2 100644 --- a/packages/manager/src/queries/account/billing.ts +++ b/packages/manager/src/queries/account/billing.ts @@ -1,10 +1,12 @@ -import { Invoice, Payment } from '@linode/api-v4/lib/account'; -import { APIError, Filter, Params } from '@linode/api-v4/lib/types'; import { useQuery } from '@tanstack/react-query'; import { queryPresets } from '../base'; import { accountQueries } from './queries'; +import type { Invoice, Payment } from '@linode/api-v4/lib/account'; +import type { APIError, Filter, Params } from '@linode/api-v4/lib/types'; +import type { EventHandlerData } from 'src/hooks/useEventHandlers'; + export const useAllAccountInvoices = ( params: Params = {}, filter: Filter = {} @@ -26,3 +28,11 @@ export const useAllAccountPayments = ( keepPreviousData: true, }); }; + +export const taxIdEventHandler = ({ event, queryClient }: EventHandlerData) => { + if (event.action === 'tax_id_invalid' || event.action === 'tax_id_valid') { + queryClient.invalidateQueries({ + queryKey: accountQueries.notifications.queryKey, + }); + } +};