mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-28 00:17:12 +01:00
feat($env): additional environments - API integration (#8424)
Make API calls from "order environments" dialog, improve validation
This commit is contained in:
parent
01b2a15b8a
commit
1fa918e4f7
@ -0,0 +1,42 @@
|
||||
import { screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { render } from 'utils/testRenderer';
|
||||
import { OrderEnvironments } from './OrderEnvironments';
|
||||
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
||||
|
||||
const server = testServerSetup();
|
||||
|
||||
const setupServerRoutes = (changeRequestsEnabled = true) => {
|
||||
testServerRoute(server, '/api/admin/ui-config', {
|
||||
environment: 'Pro',
|
||||
flags: {
|
||||
purchaseAdditionalEnvironments: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
describe('OrderEnvironmentsDialog Component', () => {
|
||||
test('should show error if environment name is empty', async () => {
|
||||
setupServerRoutes();
|
||||
render(<OrderEnvironments />);
|
||||
|
||||
await waitFor(async () => {
|
||||
const openDialogButton = await screen.queryByRole('button', {
|
||||
name: /view pricing/i,
|
||||
});
|
||||
expect(openDialogButton).toBeInTheDocument();
|
||||
fireEvent.click(openDialogButton!);
|
||||
});
|
||||
|
||||
const checkbox = screen.getByRole('checkbox', {
|
||||
name: /i understand adding environments/i,
|
||||
});
|
||||
fireEvent.click(checkbox);
|
||||
|
||||
const submitButton = screen.getByRole('button', { name: /order/i });
|
||||
fireEvent.click(submitButton);
|
||||
|
||||
expect(
|
||||
screen.getByText(/environment name is required/i),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
@ -4,6 +4,10 @@ import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import { PurchasableFeature } from './PurchasableFeature/PurchasableFeature';
|
||||
import { OrderEnvironmentsDialog } from './OrderEnvironmentsDialog/OrderEnvironmentsDialog';
|
||||
import { OrderEnvironmentsConfirmation } from './OrderEnvironmentsConfirmation/OrderEnvironmentsConfirmation';
|
||||
import { useFormErrors } from 'hooks/useFormErrors';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useOrderEnvironmentApi } from 'hooks/api/actions/useOrderEnvironmentsApi/useOrderEnvironmentsApi';
|
||||
|
||||
type OrderEnvironmentsProps = {};
|
||||
|
||||
@ -17,18 +21,40 @@ export const OrderEnvironments: FC<OrderEnvironmentsProps> = () => {
|
||||
const isPurchaseAdditionalEnvironmentsEnabled = useUiFlag(
|
||||
'purchaseAdditionalEnvironments',
|
||||
);
|
||||
const errors = useFormErrors();
|
||||
const { orderEnvironments } = useOrderEnvironmentApi();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
|
||||
if (!isPro() || !isPurchaseAdditionalEnvironmentsEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const onSubmit = (environments: string[]) => {
|
||||
setPurchaseDialogOpen(false);
|
||||
// TODO: API call
|
||||
setConfirmationState({
|
||||
isOpen: true,
|
||||
environmentsCount: environments.length,
|
||||
const onSubmit = async (environments: string[]) => {
|
||||
let hasErrors = false;
|
||||
environments.forEach((environment, index) => {
|
||||
const field = `environment-${index}`;
|
||||
if (environment.trim() === '') {
|
||||
errors.setFormError(field, 'Environment name is required');
|
||||
hasErrors = true;
|
||||
} else {
|
||||
errors.removeFormError(field);
|
||||
}
|
||||
});
|
||||
|
||||
if (hasErrors) {
|
||||
return;
|
||||
} else {
|
||||
try {
|
||||
await orderEnvironments({ environments });
|
||||
setPurchaseDialogOpen(false);
|
||||
setConfirmationState({
|
||||
isOpen: true,
|
||||
environmentsCount: environments.length,
|
||||
});
|
||||
} catch (error) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@ -42,6 +68,7 @@ export const OrderEnvironments: FC<OrderEnvironmentsProps> = () => {
|
||||
open={purchaseDialogOpen}
|
||||
onClose={() => setPurchaseDialogOpen(false)}
|
||||
onSubmit={onSubmit}
|
||||
errors={errors}
|
||||
/>
|
||||
<OrderEnvironmentsConfirmation
|
||||
open={confirmationState.isOpen}
|
||||
|
@ -11,12 +11,14 @@ import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||
import { OrderEnvironmentsDialogPricing } from './OrderEnvironmentsDialogPricing/OrderEnvironmentsDialogPricing';
|
||||
import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect';
|
||||
import Input from 'component/common/Input/Input';
|
||||
import type { IFormErrors } from 'hooks/useFormErrors';
|
||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||
|
||||
type OrderEnvironmentsDialogProps = {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onSubmit: (environments: string[]) => void;
|
||||
errors?: IFormErrors;
|
||||
};
|
||||
|
||||
const StyledDialog = styled(Dialog)(({ theme }) => ({
|
||||
@ -75,11 +77,14 @@ export const OrderEnvironmentsDialog: FC<OrderEnvironmentsDialogProps> = ({
|
||||
open,
|
||||
onClose,
|
||||
onSubmit,
|
||||
errors,
|
||||
}) => {
|
||||
const { trackEvent } = usePlausibleTracker();
|
||||
const [selectedOption, setSelectedOption] = useState(OPTIONS[0]);
|
||||
const [costCheckboxChecked, setCostCheckboxChecked] = useState(false);
|
||||
const [environmentNames, setEnvironmentNames] = useState<string[]>([]);
|
||||
const [environmentNames, setEnvironmentNames] = useState<string[]>(['']);
|
||||
|
||||
console.log({ environmentNames });
|
||||
|
||||
const trackEnvironmentSelect = () => {
|
||||
trackEvent('order-environments', {
|
||||
@ -143,7 +148,10 @@ export const OrderEnvironmentsDialog: FC<OrderEnvironmentsDialogProps> = ({
|
||||
const value = Number.parseInt(option, 10);
|
||||
setSelectedOption(value);
|
||||
setEnvironmentNames((names) =>
|
||||
names.slice(0, value),
|
||||
[...names, ...Array(value).fill('')].slice(
|
||||
0,
|
||||
value,
|
||||
),
|
||||
);
|
||||
trackEnvironmentSelect();
|
||||
}}
|
||||
@ -154,20 +162,28 @@ export const OrderEnvironmentsDialog: FC<OrderEnvironmentsDialogProps> = ({
|
||||
How would you like the environment
|
||||
{selectedOption > 1 ? 's' : ''} to be named?
|
||||
</Typography>
|
||||
{[...Array(selectedOption)].map((_, i) => (
|
||||
<Input
|
||||
key={i}
|
||||
label={`Environment ${i + 1} name`}
|
||||
value={environmentNames[i]}
|
||||
onChange={(event) => {
|
||||
setEnvironmentNames((names) => {
|
||||
const newValues = [...names];
|
||||
newValues[i] = event.target.value;
|
||||
return newValues;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{[...Array(selectedOption)].map((_, i) => {
|
||||
const error = errors?.getFormError(
|
||||
`environment-${i}`,
|
||||
);
|
||||
|
||||
return (
|
||||
<Input
|
||||
key={i}
|
||||
label={`Environment ${i + 1} name`}
|
||||
value={environmentNames[i]}
|
||||
onChange={(event) => {
|
||||
setEnvironmentNames((names) => {
|
||||
const newValues = [...names];
|
||||
newValues[i] = event.target.value;
|
||||
return newValues;
|
||||
});
|
||||
}}
|
||||
error={Boolean(error)}
|
||||
errorText={error}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</StyledEnvironmentNameInputs>
|
||||
<Box>
|
||||
<StyledCheckbox
|
||||
|
@ -0,0 +1,24 @@
|
||||
import useAPI from '../useApi/useApi';
|
||||
import type { OrderEnvironmentsSchema } from 'openapi';
|
||||
|
||||
export const useOrderEnvironmentApi = () => {
|
||||
const { makeRequest, createRequest, errors, loading } = useAPI({
|
||||
propagateErrors: true,
|
||||
});
|
||||
|
||||
const orderEnvironments = async (payload: OrderEnvironmentsSchema) => {
|
||||
const req = createRequest('api/admin/order-environments', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
const res = await makeRequest(req.caller, req.id);
|
||||
return res.json();
|
||||
};
|
||||
|
||||
return {
|
||||
orderEnvironments,
|
||||
errors,
|
||||
loading,
|
||||
};
|
||||
};
|
@ -862,6 +862,7 @@ export * from './oidcSettingsSchemaOneOfFour';
|
||||
export * from './oidcSettingsSchemaOneOfFourDefaultRootRole';
|
||||
export * from './oidcSettingsSchemaOneOfFourIdTokenSigningAlgorithm';
|
||||
export * from './oidcSettingsSchemaOneOfIdTokenSigningAlgorithm';
|
||||
export * from './orderEnvironmentsSchema';
|
||||
export * from './outdatedSdksSchema';
|
||||
export * from './outdatedSdksSchemaSdksItem';
|
||||
export * from './overrideSchema';
|
||||
|
13
frontend/src/openapi/models/orderEnvironmentsSchema.ts
Normal file
13
frontend/src/openapi/models/orderEnvironmentsSchema.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Generated by Orval
|
||||
* Do not edit manually.
|
||||
* See `gen:api` script in package.json
|
||||
*/
|
||||
|
||||
/**
|
||||
* A request for hosted customers to order new environments in Unleash.
|
||||
*/
|
||||
export interface OrderEnvironmentsSchema {
|
||||
/** An array of environment names to be ordered. */
|
||||
environments: string[];
|
||||
}
|
Loading…
Reference in New Issue
Block a user