From 8c189cabd20586deeb5cb2543271b807ec0d7609 Mon Sep 17 00:00:00 2001 From: "gitar-bot[bot]" <159877585+gitar-bot[bot]@users.noreply.github.com> Date: Wed, 11 Dec 2024 10:11:23 +0200 Subject: [PATCH] [Gitar] Cleaning up stale flag: purchaseAdditionalEnvironments with value false (#8955) [![Gitar](https://raw.githubusercontent.com/gitarcode/.github/main/assets/gitar-banner.svg)](https://gitar.ai) This automated PR permanently removes the `purchaseAdditionalEnvironments` feature flag. --- This automated PR was generated by [Gitar](https://gitar.ai). View [docs](https://gitar.ai/docs). --------- Co-authored-by: Gitar Co-authored-by: sjaanus --- .../EnvironmentTable/EnvironmentTable.tsx | 7 +- .../OrderEnvironments.test.tsx | 42 --- .../OrderEnvironments/OrderEnvironments.tsx | 84 ------ .../OrderEnvironmentsConfirmation.tsx | 29 -- .../OrderEnvironmentsDialog.test.tsx | 212 --------------- .../OrderEnvironmentsDialog.tsx | 253 ------------------ .../OrderEnvironmentsDialogPricing.tsx | 71 ----- .../PurchasableFeature/PurchasableFeature.tsx | 83 ------ .../useOrderEnvironmentsApi.ts | 23 -- frontend/src/interfaces/uiConfig.ts | 1 - src/lib/services/email-service.test.ts | 41 --- src/lib/services/email-service.ts | 70 ----- src/lib/types/experimental.ts | 5 - src/server-dev.ts | 1 - 14 files changed, 1 insertion(+), 921 deletions(-) delete mode 100644 frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironments.test.tsx delete mode 100644 frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironments.tsx delete mode 100644 frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironmentsConfirmation/OrderEnvironmentsConfirmation.tsx delete mode 100644 frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironmentsDialog/OrderEnvironmentsDialog.test.tsx delete mode 100644 frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironmentsDialog/OrderEnvironmentsDialog.tsx delete mode 100644 frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironmentsDialog/OrderEnvironmentsDialogPricing/OrderEnvironmentsDialogPricing.tsx delete mode 100644 frontend/src/component/environments/EnvironmentTable/OrderEnvironments/PurchasableFeature/PurchasableFeature.tsx delete mode 100644 frontend/src/hooks/api/actions/useOrderEnvironmentsApi/useOrderEnvironmentsApi.ts diff --git a/frontend/src/component/environments/EnvironmentTable/EnvironmentTable.tsx b/frontend/src/component/environments/EnvironmentTable/EnvironmentTable.tsx index 770f9f2f9a12..c9d0a8fe0147 100644 --- a/frontend/src/component/environments/EnvironmentTable/EnvironmentTable.tsx +++ b/frontend/src/component/environments/EnvironmentTable/EnvironmentTable.tsx @@ -28,7 +28,6 @@ import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; import type { IEnvironment } from 'interfaces/environments'; import { useUiFlag } from 'hooks/useUiFlag'; import { PremiumFeature } from 'component/common/PremiumFeature/PremiumFeature'; -import { OrderEnvironments } from './OrderEnvironments/OrderEnvironments'; const StyledAlert = styled(Alert)(({ theme }) => ({ marginBottom: theme.spacing(4), })); @@ -38,9 +37,6 @@ export const EnvironmentTable = () => { const { setToastApiError } = useToast(); const { environments, mutateEnvironments } = useEnvironments(); const isFeatureEnabled = useUiFlag('EEA'); - const isPurchaseAdditionalEnvironmentsEnabled = useUiFlag( - 'purchaseAdditionalEnvironments', - ); const moveListItem: MoveListItem = useCallback( async (dragIndex: number, dropIndex: number, save = false) => { @@ -116,7 +112,7 @@ export const EnvironmentTable = () => { ); - if (!isFeatureEnabled && !isPurchaseAdditionalEnvironmentsEnabled) { + if (!isFeatureEnabled) { return ( @@ -126,7 +122,6 @@ export const EnvironmentTable = () => { return ( - This is the order of environments that you have today in each feature flag. Rearranging them here will change also the order diff --git a/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironments.test.tsx b/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironments.test.tsx deleted file mode 100644 index 49e7d9513192..000000000000 --- a/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironments.test.tsx +++ /dev/null @@ -1,42 +0,0 @@ -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(); - - 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(); - }); -}); diff --git a/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironments.tsx b/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironments.tsx deleted file mode 100644 index 12c6c17784e7..000000000000 --- a/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironments.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { useState, type FC } from 'react'; -import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; -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'; -import type { OrderEnvironmentsSchema } from 'openapi'; - -type OrderEnvironmentsProps = {}; - -export const OrderEnvironments: FC = () => { - const [purchaseDialogOpen, setPurchaseDialogOpen] = useState(false); - const [confirmationState, setConfirmationState] = useState<{ - isOpen: boolean; - environmentsCount?: number; - }>({ isOpen: false }); - const { isPro } = useUiConfig(); - const isPurchaseAdditionalEnvironmentsEnabled = useUiFlag( - 'purchaseAdditionalEnvironments', - ); - const errors = useFormErrors(); - const { orderEnvironments } = useOrderEnvironmentApi(); - const { setToastApiError } = useToast(); - - if (!isPro() || !isPurchaseAdditionalEnvironmentsEnabled) { - return null; - } - - const onSubmit = async ( - environments: OrderEnvironmentsSchema['environments'], - ) => { - let hasErrors = false; - environments.forEach((environment, index) => { - const field = `environment-${index}`; - const environmentName = environment.name.trim(); - if (environmentName === '') { - 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 ( - <> - setPurchaseDialogOpen(true)} - /> - setPurchaseDialogOpen(false)} - onSubmit={onSubmit} - errors={errors} - /> - setConfirmationState({ isOpen: false })} - /> - - ); -}; diff --git a/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironmentsConfirmation/OrderEnvironmentsConfirmation.tsx b/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironmentsConfirmation/OrderEnvironmentsConfirmation.tsx deleted file mode 100644 index ba666a5f34a1..000000000000 --- a/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironmentsConfirmation/OrderEnvironmentsConfirmation.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import type { FC } from 'react'; -import { Typography } from '@mui/material'; -import { Dialogue } from 'component/common/Dialogue/Dialogue'; - -type OrderEnvironmentsConfirmationProps = { - open: boolean; - orderedEnvironments: number; - onClose: () => void; -}; - -export const OrderEnvironmentsConfirmation: FC< - OrderEnvironmentsConfirmationProps -> = ({ open, orderedEnvironments, onClose }) => { - return ( - - - You have ordered {orderedEnvironments}{' '} - additional{' '} - {orderedEnvironments === 1 ? 'environment' : 'environments'}. It - may take up to 24 hours before you will get access. - - - ); -}; diff --git a/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironmentsDialog/OrderEnvironmentsDialog.test.tsx b/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironmentsDialog/OrderEnvironmentsDialog.test.tsx deleted file mode 100644 index 29329dc5dcab..000000000000 --- a/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironmentsDialog/OrderEnvironmentsDialog.test.tsx +++ /dev/null @@ -1,212 +0,0 @@ -import { vi } from 'vitest'; -import { render, screen, fireEvent } from '@testing-library/react'; -import { OrderEnvironmentsDialog } from './OrderEnvironmentsDialog'; - -describe('OrderEnvironmentsDialog Component', () => { - const renderComponent = (props = {}) => - render( - {}} - onSubmit={() => {}} - {...props} - />, - ); - - test('should disable "Order" button until the checkbox is checked', () => { - renderComponent(); - - const orderButton = screen.getByRole('button', { name: /order/i }); - const checkbox = screen.getByRole('checkbox', { - name: /i understand adding environments leads to extra costs/i, - }); - - expect(orderButton).toBeDisabled(); - - fireEvent.click(checkbox); - - expect(orderButton).toBeEnabled(); - }); - - test('should render correct number of environment name inputs based on selected environments', () => { - renderComponent(); - - let environmentInputs = - screen.getAllByLabelText(/environment \d+ name/i); - expect(environmentInputs).toHaveLength(1); - - const selectButton = screen.getByRole('combobox', { - name: /select the number of additional environments/i, - }); - fireEvent.mouseDown(selectButton); - - const option2 = screen.getByRole('option', { name: '2 environments' }); - fireEvent.click(option2); - - environmentInputs = screen.getAllByLabelText(/environment \d+ name/i); - expect(environmentInputs).toHaveLength(2); - - fireEvent.mouseDown(selectButton); - const option3 = screen.getByRole('option', { name: '3 environments' }); - fireEvent.click(option3); - - environmentInputs = screen.getAllByLabelText(/environment \d+ name/i); - expect(environmentInputs).toHaveLength(3); - }); - - test('should enable "Order" button only when checkbox is checked', () => { - renderComponent(); - - const orderButton = screen.getByRole('button', { name: /order/i }); - const checkbox = screen.getByRole('checkbox', { - name: /i understand adding environments leads to extra costs/i, - }); - - expect(orderButton).toBeDisabled(); - - fireEvent.click(checkbox); - - expect(orderButton).toBeEnabled(); - - fireEvent.click(checkbox); - - expect(orderButton).toBeDisabled(); - }); - - test('should output environment names', () => { - const onSubmitMock = vi.fn(); - renderComponent({ onSubmit: onSubmitMock }); - - const selectButton = screen.getByRole('combobox', { - name: /select the number of additional environments/i, - }); - fireEvent.mouseDown(selectButton); - - const option2 = screen.getByRole('option', { name: '2 environments' }); - fireEvent.click(option2); - - const environmentInputs = - screen.getAllByLabelText(/environment \d+ name/i); - - fireEvent.change(environmentInputs[0], { target: { value: 'Dev' } }); - fireEvent.change(environmentInputs[1], { - target: { value: 'Staging' }, - }); - const checkbox = screen.getByRole('checkbox', { - name: /i understand adding environments leads to extra costs/i, - }); - fireEvent.click(checkbox); - - const submitButton = screen.getByRole('button', { name: /order/i }); - fireEvent.click(submitButton); - - expect(onSubmitMock).toHaveBeenCalledTimes(1); - expect(onSubmitMock).toHaveBeenCalledWith([ - { name: 'Dev', type: 'development' }, - { name: 'Staging', type: 'development' }, - ]); - }); - - test('should call onClose when "Cancel" button is clicked', () => { - const onCloseMock = vi.fn(); - renderComponent({ onClose: onCloseMock }); - - const cancelButton = screen.getByRole('button', { name: /cancel/i }); - fireEvent.click(cancelButton); - - expect(onCloseMock).toHaveBeenCalledTimes(1); - }); - - test('should adjust environment name inputs when decreasing environments', () => { - const onSubmitMock = vi.fn(); - renderComponent({ onSubmit: onSubmitMock }); - - const selectButton = screen.getByRole('combobox', { - name: /select the number of additional environments/i, - }); - fireEvent.mouseDown(selectButton); - - const option3 = screen.getByRole('option', { name: '3 environments' }); - fireEvent.click(option3); - - let environmentInputs = - screen.getAllByLabelText(/environment \d+ name/i); - expect(environmentInputs).toHaveLength(3); - - fireEvent.change(environmentInputs[0], { target: { value: 'Dev' } }); - fireEvent.change(environmentInputs[1], { - target: { value: 'Staging' }, - }); - fireEvent.change(environmentInputs[2], { target: { value: 'Prod' } }); - - fireEvent.mouseDown(selectButton); - const option2 = screen.getByRole('option', { name: '2 environments' }); - fireEvent.click(option2); - - environmentInputs = screen.getAllByLabelText(/environment \d+ name/i); - expect(environmentInputs).toHaveLength(2); - - const checkbox = screen.getByRole('checkbox', { - name: /i understand adding environments leads to extra costs/i, - }); - fireEvent.click(checkbox); - - const submitButton = screen.getByRole('button', { name: /order/i }); - fireEvent.click(submitButton); - - expect(onSubmitMock).toHaveBeenCalledTimes(1); - expect(onSubmitMock).toHaveBeenCalledWith([ - { name: 'Dev', type: 'development' }, - { name: 'Staging', type: 'development' }, - ]); - }); - - test('should allow for changing environment types', () => { - const onSubmitMock = vi.fn(); - renderComponent({ onSubmit: onSubmitMock }); - - const selectButton = screen.getByRole('combobox', { - name: /select the number of additional environments/i, - }); - fireEvent.mouseDown(selectButton); - const option3 = screen.getByRole('option', { name: '2 environments' }); - fireEvent.click(option3); - - const checkbox = screen.getByRole('checkbox', { - name: /i understand adding environments leads to extra costs/i, - }); - fireEvent.click(checkbox); - - const environmentInputs = - screen.getAllByLabelText(/environment \d+ name/i); - fireEvent.change(environmentInputs[0], { target: { value: 'Test' } }); - fireEvent.change(environmentInputs[1], { - target: { value: 'Staging' }, - }); - - const typeSelects = screen.getAllByRole('combobox', { - name: /type of environment/i, - }); - - fireEvent.mouseDown(typeSelects[0]); - const optionTesting = screen.getByRole('option', { - name: /testing/i, - }); - fireEvent.click(optionTesting); - - fireEvent.mouseDown(typeSelects[1]); - const optionProduction = screen.getByRole('option', { - name: /pre\-production/i, - }); - fireEvent.click(optionProduction); - - const submitButton = screen.getByRole('button', { name: /order/i }); - fireEvent.click(submitButton); - - expect(onSubmitMock).toHaveBeenCalledTimes(1); - expect(onSubmitMock).toHaveBeenCalledWith([ - { name: 'Test', type: 'testing' }, - { name: 'Staging', type: 'pre-production' }, - ]); - }); -}); diff --git a/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironmentsDialog/OrderEnvironmentsDialog.tsx b/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironmentsDialog/OrderEnvironmentsDialog.tsx deleted file mode 100644 index c9e9625fe447..000000000000 --- a/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironmentsDialog/OrderEnvironmentsDialog.tsx +++ /dev/null @@ -1,253 +0,0 @@ -import { useState, type FC } from 'react'; -import { - Box, - Button, - Checkbox, - Dialog, - styled, - Typography, - TextField, -} from '@mui/material'; -import FormTemplate from 'component/common/FormTemplate/FormTemplate'; -import { OrderEnvironmentsDialogPricing } from './OrderEnvironmentsDialogPricing/OrderEnvironmentsDialogPricing'; -import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect'; -import type { IFormErrors } from 'hooks/useFormErrors'; -import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; -import type { OrderEnvironmentsSchemaEnvironmentsItem } from 'openapi'; - -type OrderEnvironmentsDialogProps = { - open: boolean; - onClose: () => void; - onSubmit: (environments: OrderEnvironmentsSchemaEnvironmentsItem[]) => void; - errors?: IFormErrors; -}; - -const StyledDialog = styled(Dialog)(({ theme }) => ({ - maxWidth: '940px', - margin: 'auto', - '& .MuiDialog-paper': { - borderRadius: theme.shape.borderRadiusExtraLarge, - maxWidth: theme.spacing(170), - width: '100%', - backgroundColor: 'transparent', - }, - padding: 0, - '& .MuiPaper-root > section': { - overflowX: 'hidden', - }, -})); - -const StyledTitle = styled('div')(({ theme }) => ({ - marginBottom: theme.spacing(3), -})); - -const StyledFooter = styled('div')(({ theme }) => ({ - display: 'flex', - justifyContent: 'flex-end', - gap: theme.spacing(2), -})); - -const StyledGeneralSelect = styled(GeneralSelect)(({ theme }) => ({ - margin: theme.spacing(1, 0), -})); - -const StyledTypeSelect = styled(GeneralSelect)(({ theme }) => ({ - minWidth: '166px', -})); - -const StyledEnvironmentInputs = styled(Box)(({ theme }) => ({ - display: 'flex', - gap: theme.spacing(2), - marginBottom: theme.spacing(2), -})); - -const StyledFields = styled(Box)(({ theme }) => ({ - display: 'flex', - flexDirection: 'column', - gap: theme.spacing(2), - paddingTop: theme.spacing(3), -})); - -const StyledEnvironmentNameInputs = styled('fieldset')(({ theme }) => ({ - display: 'flex', - flexDirection: 'column', - border: 'none', - padding: 0, - margin: 0, - gap: theme.spacing(1.5), -})); - -const StyledCheckbox = styled(Checkbox)(({ theme }) => ({ - marginBottom: theme.spacing(0.4), -})); - -const PRICE = 10; -const OPTIONS = [1, 2, 3]; -const ENVIRONMENT_TYPES = [ - 'development', - 'testing', - 'pre-production', - 'production', -]; - -export const OrderEnvironmentsDialog: FC = ({ - open, - onClose, - onSubmit, - errors, -}) => { - const { trackEvent } = usePlausibleTracker(); - const [selectedOption, setSelectedOption] = useState(OPTIONS[0]); - const [costCheckboxChecked, setCostCheckboxChecked] = useState(false); - const [environments, setEnvironments] = useState< - { name: string; type: string }[] - >([{ name: '', type: ENVIRONMENT_TYPES[0] }]); - - const trackEnvironmentSelect = () => { - trackEvent('order-environments', { - props: { - eventType: 'selected environment count', - }, - }); - }; - - const onTypeChange = (index: number, type: string) => { - setEnvironments( - environments.map((env, i) => - i === index ? { ...env, type } : { ...env }, - ), - ); - }; - - const onNameChange = (index: number, name: string) => { - setEnvironments( - environments.map((env, i) => - i === index ? { ...env, name } : { ...env }, - ), - ); - }; - - return ( - - ({ - environments: option, - price: option * PRICE, - }))} - /> - } - footer={ - - - - - } - > - - - Order additional environments - - - - With our PRO plan, you have the flexibility to expand your - workspace by adding environments at ${PRICE} per user per - month. - - - - - Select the number of additional environments - - ({ - key: `${option}`, - label: `${option} environment${option > 1 ? 's' : ''}`, - }))} - onChange={(option) => { - const value = Number.parseInt(option, 10); - setSelectedOption(value); - setEnvironments((envs) => - [ - ...envs, - ...Array(value).fill({ - name: '', - type: ENVIRONMENT_TYPES[0], - }), - ].slice(0, value), - ); - trackEnvironmentSelect(); - }} - /> - - - - How would you like the environment - {selectedOption > 1 ? 's' : ''} to be named? - - {[...Array(selectedOption)].map((_, i) => { - const error = errors?.getFormError( - `environment-${i}`, - ); - return ( - - ({ - key: type, - label: type, - }), - )} - onChange={(type) => { - onTypeChange(i, type); - }} - /> - { - onNameChange(i, e.target.value); - }} - error={!!error} - helperText={error} - /> - - ); - })} - - - - setCostCheckboxChecked((state) => !state) - } - /> - - I understand adding environments leads to extra - costs - - - - - - ); -}; diff --git a/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironmentsDialog/OrderEnvironmentsDialogPricing/OrderEnvironmentsDialogPricing.tsx b/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironmentsDialog/OrderEnvironmentsDialogPricing/OrderEnvironmentsDialogPricing.tsx deleted file mode 100644 index 9d6089591e1b..000000000000 --- a/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/OrderEnvironmentsDialog/OrderEnvironmentsDialogPricing/OrderEnvironmentsDialogPricing.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import type { FC } from 'react'; -import { Box, Card, styled, Typography } from '@mui/material'; -import EnvironmentIcon from 'component/common/EnvironmentIcon/EnvironmentIcon'; -import { BILLING_PRO_DEFAULT_INCLUDED_SEATS } from 'component/admin/billing/BillingDashboard/BillingPlan/BillingPlan'; - -type OrderEnvironmentsDialogPricingProps = { - pricingOptions: Array<{ environments: number; price: number }>; -}; - -const StyledContainer = styled(Box)(({ theme }) => ({ - display: 'flex', - flexDirection: 'column', - gap: theme.spacing(1), - justifyContent: 'center', - height: '100%', - [theme.breakpoints.up('lg')]: { - marginTop: theme.spacing(7.5), - }, -})); - -const StyledCard = styled(Card)(({ theme }) => ({ - borderRadius: `${theme.shape.borderRadiusMedium}px`, - boxShadow: 'none', -})); - -const StyledCardContent = styled(Box)(({ theme }) => ({ - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - flexDirection: 'row', - padding: theme.spacing(2), -})); - -const StyledExtraText = styled('div')(({ theme }) => ({ - paddingTop: theme.spacing(2), -})); - -export const OrderEnvironmentsDialogPricing: FC< - OrderEnvironmentsDialogPricingProps -> = ({ pricingOptions }) => ( - - - Pricing - - {pricingOptions.map((option) => ( - - - - - - - {option.environments} additional environment - {option.environments > 1 ? 's' : ''} - - - - ${option.price} per user per month - - - - - ))} - - - With Pro, there is a minimum of{' '} - {BILLING_PRO_DEFAULT_INCLUDED_SEATS} users, meaning an - additional environment will cost at least $50 per month. - - - -); diff --git a/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/PurchasableFeature/PurchasableFeature.tsx b/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/PurchasableFeature/PurchasableFeature.tsx deleted file mode 100644 index e63d3890e046..000000000000 --- a/frontend/src/component/environments/EnvironmentTable/OrderEnvironments/PurchasableFeature/PurchasableFeature.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import type { FC, ReactNode } from 'react'; -import { Box, Button, styled, Typography } from '@mui/material'; -import { ThemeMode } from 'component/common/ThemeMode/ThemeMode'; -import { ReactComponent as ProPlanIcon } from 'assets/icons/pro-enterprise-feature-badge.svg'; -import { ReactComponent as ProPlanIconLight } from 'assets/icons/pro-enterprise-feature-badge-light.svg'; -import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; - -type PurchasableFeatureProps = { - title: ReactNode; - description: ReactNode; - onClick: () => void; -}; - -const Icon = () => ( - } lightmode={} /> -); - -const StyledContainer = styled(Box)(({ theme }) => ({ - padding: theme.spacing(2, 3), - marginBottom: theme.spacing(3), - background: theme.palette.background.elevation2, - borderRadius: `${theme.shape.borderRadiusMedium}px`, - display: 'flex', - flexDirection: 'row', - gap: theme.spacing(3), - [theme.breakpoints.down('md')]: { - flexDirection: 'column', - gap: theme.spacing(2), - }, -})); - -const StyledIconContainer = styled(Box)(() => ({ - width: '36px', - flexShrink: 0, -})); - -const StyledMessage = styled(Box)(() => ({ - flexGrow: 1, - display: 'flex', -})); - -const StyledButtonContainer = styled(Box)(() => ({ - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - whiteSpace: 'nowrap', -})); - -export const PurchasableFeature: FC = ({ - title, - description, - onClick, -}) => { - const { trackEvent } = usePlausibleTracker(); - - const onViewPricingClick = () => { - onClick(); - trackEvent('order-environments', { - props: { - eventType: 'view pricing clicked', - }, - }); - }; - - return ( - - - - - - - {title} - {description} - - - - - - - ); -}; diff --git a/frontend/src/hooks/api/actions/useOrderEnvironmentsApi/useOrderEnvironmentsApi.ts b/frontend/src/hooks/api/actions/useOrderEnvironmentsApi/useOrderEnvironmentsApi.ts deleted file mode 100644 index b941b8025500..000000000000 --- a/frontend/src/hooks/api/actions/useOrderEnvironmentsApi/useOrderEnvironmentsApi.ts +++ /dev/null @@ -1,23 +0,0 @@ -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), - }); - - await makeRequest(req.caller, req.id); - }; - - return { - orderEnvironments, - errors, - loading, - }; -}; diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index b135df6b0219..e7667b4546b2 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -85,7 +85,6 @@ export type UiFlags = { manyStrategiesPagination?: boolean; enableLegacyVariants?: boolean; flagCreator?: boolean; - purchaseAdditionalEnvironments?: boolean; unleashAI?: boolean; releasePlans?: boolean; 'enterprise-payg'?: boolean; diff --git a/src/lib/services/email-service.test.ts b/src/lib/services/email-service.test.ts index 1ee62d8d9d24..f8967f669387 100644 --- a/src/lib/services/email-service.test.ts +++ b/src/lib/services/email-service.test.ts @@ -103,47 +103,6 @@ test('should strip special characters from email subject', async () => { expect(emailService.stripSpecialCharacters('tom-jones')).toBe('tom-jones'); }); -test('Can send order environments email', async () => { - process.env.ORDER_ENVIRONMENTS_BCC = 'bcc@bcc.com'; - const emailService = new EmailService({ - email: { - host: 'test', - port: 587, - secure: false, - smtpuser: '', - smtppass: '', - sender: 'noreply@getunleash.ai', - }, - getLogger: noLoggerProvider, - } as unknown as IUnleashConfig); - - const customerId = 'customer133'; - const environments = [ - { name: 'test', type: 'development' }, - { name: 'live', type: 'production' }, - ]; - - const content = await emailService.sendOrderEnvironmentEmail( - 'user@user.com', - customerId, - environments, - ); - expect(content.from).toBe('noreply@getunleash.ai'); - expect(content.subject).toBe('Unleash - ordered environments successfully'); - expect( - content.html.includes( - `
  • Name: ${environments[0].name}, Type: ${environments[0].type}
  • `, - ), - ).toBe(true); - expect( - content.html.includes( - `
  • Name: ${environments[1].name}, Type: ${environments[1].type}
  • `, - ), - ).toBe(true); - expect(content.html.includes(customerId)).toBe(true); - expect(content.bcc).toBe('bcc@bcc.com'); -}); - test('Can send productivity report email', async () => { const emailService = new EmailService({ server: { diff --git a/src/lib/services/email-service.ts b/src/lib/services/email-service.ts index f9fd56cb5981..432d7865f0c5 100644 --- a/src/lib/services/email-service.ts +++ b/src/lib/services/email-service.ts @@ -70,11 +70,6 @@ export type ChangeRequestScheduleConflictData = environment: string; }; -export type OrderEnvironmentData = { - name: string; - type: string; -}; - export class EmailService { private logger: Logger; private config: IUnleashConfig; @@ -462,71 +457,6 @@ export class EmailService { }); } - async sendOrderEnvironmentEmail( - userEmail: string, - customerId: string, - environments: OrderEnvironmentData[], - ): Promise { - if (this.configured()) { - const context = { - userEmail, - customerId, - environments: environments.map((data) => ({ - name: this.stripSpecialCharacters(data.name), - type: this.stripSpecialCharacters(data.type), - })), - }; - - const bodyHtml = await this.compileTemplate( - 'order-environments', - TemplateFormat.HTML, - context, - ); - const bodyText = await this.compileTemplate( - 'order-environments', - TemplateFormat.PLAIN, - context, - ); - const email = { - from: this.sender, - to: userEmail, - bcc: - process.env.ORDER_ENVIRONMENTS_BCC || - 'pro-sales@getunleash.io', - subject: ORDER_ENVIRONMENTS_SUBJECT, - html: bodyHtml, - text: bodyText, - }; - process.nextTick(() => { - this.mailer!.sendMail(email).then( - () => - this.logger.info( - 'Successfully sent order environments email', - ), - (e) => - this.logger.warn( - 'Failed to send order environments email', - e, - ), - ); - }); - return Promise.resolve(email); - } - return new Promise((res) => { - this.logger.warn( - 'No mailer is configured. Please read the docs on how to configure an email service', - ); - res({ - from: this.sender, - to: userEmail, - bcc: '', - subject: ORDER_ENVIRONMENTS_SUBJECT, - html: '', - text: '', - }); - }); - } - async sendProductivityReportEmail( userEmail: string, userName: string, diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index 3896f6e21074..1da78c0f68a2 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -47,7 +47,6 @@ export type IFlagKey = | 'extendedMetrics' | 'removeUnsafeInlineStyleSrc' | 'projectRoleAssignment' - | 'purchaseAdditionalEnvironments' | 'originMiddlewareRequestLogging' | 'unleashAI' | 'webhookDomainLogging' @@ -239,10 +238,6 @@ const flags: IFlags = { process.env.UNLEASH_EXPERIMENTAL_PROJECT_ROLE_ASSIGNMENT, false, ), - purchaseAdditionalEnvironments: parseEnvVarBoolean( - process.env.UNLEASH_EXPERIMENTAL_PURCHASE_ADDITIONAL_ENVIRONMENTS, - false, - ), originMiddlewareRequestLogging: parseEnvVarBoolean( process.env.UNLEASH_ORIGIN_MIDDLEWARE_REQUEST_LOGGING, false, diff --git a/src/server-dev.ts b/src/server-dev.ts index d4b7e36deb20..69c9303a96e0 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -48,7 +48,6 @@ process.nextTick(async () => { manyStrategiesPagination: true, enableLegacyVariants: false, extendedMetrics: true, - purchaseAdditionalEnvironments: true, originMiddlewareRequestLogging: true, unleashAI: true, webhookDomainLogging: true,