Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-10684-added-1721165645259.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Added
---

Account Limit support ticket to remaining create flows ([#10684](https://github.com/linode/manager/pull/10684))
Original file line number Diff line number Diff line change
Expand Up @@ -422,14 +422,11 @@ describe('help & support', () => {
.click();
cy.wait('@createLinode');

cy.get('[data-qa-error="true"]')
.first()
.scrollIntoView()
.within(() => {
cy.contains(ACCOUNT_THING_LIMIT_ERROR);
// Navigate to the account limit ticket form.
cy.findByText('contact Support').should('be.visible').click();
});
cy.get('[data-qa-error="true"]').first().scrollIntoView();
cy.contains(ACCOUNT_THING_LIMIT_ERROR);

// Navigate to the account limit ticket form.
cy.findByText('contact Support').should('be.visible').click();

// Fill out ticket form.
ui.dialog
Expand Down
11 changes: 7 additions & 4 deletions packages/manager/src/components/ErrorMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,23 @@ import type { EntityType } from 'src/features/Support/SupportTickets/SupportTick

interface Props {
entityType: EntityType;
message: JSX.Element | string;
message: string;
}

export const supportTextRegex = /(open a support ticket|contact Support)/i;

export const ErrorMessage = (props: Props) => {
const { entityType, message } = props;
const isSupportTicketError = supportTextRegex.test(message);

if (typeof message === 'string') {
return <Typography py={2}>{message}</Typography>;
} else {
if (isSupportTicketError) {
return (
<SupportTicketGeneralError
entityType={entityType}
generalError={message}
/>
);
}

return <Typography py={2}>{message}</Typography>;
};
49 changes: 0 additions & 49 deletions packages/manager/src/components/SupportError.tsx

This file was deleted.

12 changes: 6 additions & 6 deletions packages/manager/src/components/SupportTicketGeneralError.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,28 @@ import React from 'react';
import { SupportLink } from 'src/components/SupportLink';
import { capitalize } from 'src/utilities/capitalize';

import { supportTextRegex } from './ErrorMessage';
import { Typography } from './Typography';

import type { EntityType } from 'src/features/Support/SupportTickets/SupportTicketDialog';

interface SupportTicketGeneralErrorProps {
entityType: EntityType;
generalError: JSX.Element;
generalError: string;
}

const accountLimitRegex = /(limit|limit for the number of active services) on your account/i;

export const SupportTicketGeneralError = (
props: SupportTicketGeneralErrorProps
) => {
const { entityType, generalError } = props;
const theme = useTheme();

const supportTextRegex = /(open a support ticket|contact Support)/i;
const reason: string = generalError.props.errors[0].reason;
const limitError = reason.split(supportTextRegex);
const limitError = generalError.split(supportTextRegex);

// Determine whether we'll need to link to a specific support ticket form based on ticketType.
const accountLimitRegex = /(limit|limit for the number of active services) on your account/i;
const isAccountLimitSupportTicket = accountLimitRegex.test(reason);
const isAccountLimitSupportTicket = accountLimitRegex.test(generalError);

return (
<Typography
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
import {
ClusterSize,
ComprehensiveReplicationType,
CreateDatabasePayload,
DatabaseClusterSizeObject,
DatabaseEngine,
DatabasePriceObject,
Engine,
} from '@linode/api-v4/lib/databases/types';
import { APIError } from '@linode/api-v4/lib/types';
import { createDatabaseSchema } from '@linode/validation/lib/databases.schema';
import { Theme } from '@mui/material/styles';
import Grid from '@mui/material/Unstable_Grid2';
import { useFormik } from 'formik';
import { groupBy } from 'ramda';
Expand All @@ -25,7 +14,8 @@ import { Button } from 'src/components/Button/Button';
import { CircleProgress } from 'src/components/CircleProgress';
import { Divider } from 'src/components/Divider';
import { _SingleValue } from 'src/components/EnhancedSelect/components/SingleValue';
import Select, { Item } from 'src/components/EnhancedSelect/Select';
import Select from 'src/components/EnhancedSelect/Select';
import { ErrorMessage } from 'src/components/ErrorMessage';
import { ErrorState } from 'src/components/ErrorState/ErrorState';
import { FormControl } from 'src/components/FormControl';
import { FormControlLabel } from 'src/components/FormControlLabel';
Expand Down Expand Up @@ -55,14 +45,23 @@ import { useRegionsQuery } from 'src/queries/regions/regions';
import { formatStorageUnits } from 'src/utilities/formatStorageUnits';
import { handleAPIErrors } from 'src/utilities/formikErrorUtils';
import { getSelectedOptionFromGroupedOptions } from 'src/utilities/getSelectedOptionFromGroupedOptions';
import {
ExtendedIP,
ipFieldPlaceholder,
validateIPs,
} from 'src/utilities/ipUtils';
import { ipFieldPlaceholder, validateIPs } from 'src/utilities/ipUtils';
import { scrollErrorIntoViewV2 } from 'src/utilities/scrollErrorIntoViewV2';

import type {
ClusterSize,
ComprehensiveReplicationType,
CreateDatabasePayload,
DatabaseClusterSizeObject,
DatabaseEngine,
DatabasePriceObject,
Engine,
} from '@linode/api-v4/lib/databases/types';
import type { APIError } from '@linode/api-v4/lib/types';
import type { Theme } from '@mui/material/styles';
import type { Item } from 'src/components/EnhancedSelect/Select';
import type { PlanSelectionType } from 'src/features/components/PlansPanel/types';
import type { ExtendedIP } from 'src/utilities/ipUtils';

const useStyles = makeStyles()((theme: Theme) => ({
btnCtn: {
Expand Down Expand Up @@ -461,7 +460,11 @@ const DatabaseCreate = () => {
title="Create"
/>
<Paper>
{createError ? <Notice text={createError} variant="error" /> : null}
{createError && (
<Notice variant="error">
<ErrorMessage entityType="database_id" message={createError} />
</Notice>
)}
<Grid>
<Typography variant="h2">Name Your Cluster</Typography>
<TextField
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useLocation } from 'react-router-dom';
import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel';
import { Box } from 'src/components/Box';
import { Drawer } from 'src/components/Drawer';
import { ErrorMessage } from 'src/components/ErrorMessage';
import { FormControlLabel } from 'src/components/FormControlLabel';
import { Link } from 'src/components/Link';
import { Notice } from 'src/components/Notice/Notice';
Expand Down Expand Up @@ -254,12 +255,12 @@ export const CreateFirewallDrawer = React.memo(
/>
) : null}
{generalError && (
<Notice
data-qa-error
key={status}
text={status?.generalError ?? 'An unexpected error occurred'}
variant="error"
/>
<Notice data-qa-error key={status} variant="error">
<ErrorMessage
entityType="firewall_id"
message={generalError ?? 'An unexpected error occurred'}
/>
</Notice>
)}
<TextField
inputProps={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Autocomplete } from 'src/components/Autocomplete/Autocomplete';
import { Box } from 'src/components/Box';
import { DocsLink } from 'src/components/DocsLink/DocsLink';
import { DocumentTitleSegment } from 'src/components/DocumentTitle';
import { ErrorMessage } from 'src/components/ErrorMessage';
import { ErrorState } from 'src/components/ErrorState/ErrorState';
import { LandingHeader } from 'src/components/LandingHeader';
import { Notice } from 'src/components/Notice/Notice';
Expand Down Expand Up @@ -184,6 +185,8 @@ export const CreateCluster = () => {
errors
);

const generalError = errorMap.none;

const {
hasSelectedRegion,
isPlanPanelDisabled,
Expand All @@ -207,7 +210,11 @@ export const CreateCluster = () => {
title="Create Cluster"
/>
<Grid className={`mlMain py0`}>
{errorMap.none && <Notice text={errorMap.none} variant="error" />}
{generalError && (
<Notice variant="error">
<ErrorMessage entityType="lkecluster_id" message={generalError} />
</Notice>
)}
<Paper data-qa-label-header>
<TextField
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Theme } from '@mui/material/styles';
import * as React from 'react';
import { makeStyles } from 'tss-react/mui';

import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel';
import { Box } from 'src/components/Box';
import { Drawer } from 'src/components/Drawer';
import { ErrorMessage } from 'src/components/ErrorMessage';
import { Notice } from 'src/components/Notice/Notice';
import { Typography } from 'src/components/Typography';
import { useCreateNodePoolMutation } from 'src/queries/kubernetes';
Expand All @@ -24,6 +24,7 @@ import { nodeWarning } from '../../kubeUtils';
import { hasInvalidNodePoolPrice } from './utils';

import type { Region } from '@linode/api-v4';
import type { Theme } from '@mui/material/styles';

const useStyles = makeStyles()((theme: Theme) => ({
boxOuter: {
Expand Down Expand Up @@ -162,11 +163,12 @@ export const AddNodePoolDrawer = (props: Props) => {
wide
>
{error && (
<Notice
className={classes.error}
text={error?.[0].reason}
variant="error"
/>
<Notice spacingBottom={0} spacingTop={12} variant="error">
<ErrorMessage
entityType="lkecluster_id"
message={error?.[0].reason}
/>
</Notice>
)}
<form className={classes.plans}>
<KubernetesPlansPanel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { CheckoutSummary } from 'src/components/CheckoutSummary/CheckoutSummary'
import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog';
import { DocsLink } from 'src/components/DocsLink/DocsLink';
import { DocumentTitleSegment } from 'src/components/DocumentTitle';
import { ErrorMessage } from 'src/components/ErrorMessage';
import { LandingHeader } from 'src/components/LandingHeader';
import { Link } from 'src/components/Link';
import { Notice } from 'src/components/Notice/Notice';
Expand Down Expand Up @@ -481,7 +482,7 @@ const NodeBalancerCreate = () => {
/>
{generalError && !isRestricted && (
<Notice spacingTop={8} variant="error">
{generalError}
<ErrorMessage entityType="nodebalancer_id" message={generalError} />
</Notice>
)}
{isRestricted && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,23 @@ export interface AccountLimitCustomFields extends CustomFields {
}

export const SupportTicketAccountLimitFields = () => {
const { control, formState, reset } = useFormContext<
const { control, formState, reset, watch } = useFormContext<
AccountLimitCustomFields & SupportTicketFormFields
>();

const { data: account } = useAccount();

const { entityType } = watch();

const defaultValues = {
companyName: account?.company,
customerName: `${account?.first_name} ${account?.last_name}`,
...formState.defaultValues,
};

const shouldShowLinodePlanField =
entityType === 'linode_id' || entityType === 'lkecluster_id';

React.useEffect(() => {
reset(defaultValues);
}, []);
Expand Down Expand Up @@ -65,26 +70,28 @@ export const SupportTicketAccountLimitFields = () => {
name="companyName"
/>
<SupportTicketProductSelectionFields ticketType="accountLimit" />
<Controller
render={({ field, fieldState }) => (
<TextField
helperText={
<Link to="https://www.linode.com/pricing/">
View types of plans
</Link>
}
data-qa-ticket-linode-plan
errorText={fieldState.error?.message}
label={ACCOUNT_LIMIT_FIELD_NAME_TO_LABEL_MAP.linodePlan}
name="linodePlan"
onChange={field.onChange}
placeholder="Dedicated 4GB, Shared 8GB, High Memory 24GB, etc."
value={field.value}
/>
)}
control={control}
name="linodePlan"
/>
{shouldShowLinodePlanField && (
<Controller
render={({ field, fieldState }) => (
<TextField
helperText={
<Link to="https://www.linode.com/pricing/">
View types of plans
</Link>
}
data-qa-ticket-linode-plan
errorText={fieldState.error?.message}
label={ACCOUNT_LIMIT_FIELD_NAME_TO_LABEL_MAP.linodePlan}
name="linodePlan"
onChange={field.onChange}
placeholder="Dedicated 4GB, Shared 8GB, High Memory 24GB, etc."
value={field.value}
/>
)}
control={control}
name="linodePlan"
/>
)}
<Controller
render={({ field, fieldState }) => (
<TextField
Expand Down
Loading