Skip to content

Commit

Permalink
feat(plan) add new graduated percentage charge model
Browse files Browse the repository at this point in the history
  • Loading branch information
ansmonjol committed Aug 21, 2023
1 parent ece4191 commit 044650c
Show file tree
Hide file tree
Showing 24 changed files with 1,581 additions and 79 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cypress.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
docker-compose -f ./api/docker-compose.ci.yml -f docker-compose.ci.yml --env-file ./.env up -d redis api front
- name: Cypress run
uses: cypress-io/github-action@v4
uses: cypress-io/github-action@v5
env:
CYPRESS_GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
Expand Down
129 changes: 74 additions & 55 deletions cypress/e2e/10-resources/t40-create-plan.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,103 +14,122 @@ describe('Create plan', () => {
const randomId = Math.round(Math.random() * 1000)
const planName = `plan ${randomId}`

cy.get('[data-test="create-plan"]').click()
cy.get('[data-test="create-plan"]').click({ force: true })
cy.url().should('be.equal', Cypress.config().baseUrl + '/create/plans')
cy.get('input[name="name"]').type(planName)
cy.get('input[name="code"]').type(planName)
cy.get('[data-test="show-description"]').click()
cy.get('[data-test="show-description"]').click({ force: true })
cy.get('textarea[name="description"]').type('I am a description')
cy.get('input[name="amountCents"]').type('30000')
cy.get('[data-test="submit"]').click()
cy.get('[data-test="submit"]').click({ force: true })
cy.url().should('be.equal', Cypress.config().baseUrl + '/plans')
cy.contains(planName).should('exist')
})

it('should be able to create a plan with all 0 dimension charges and submit', () => {
cy.get('[data-test="create-plan"]').click()
it.only('should be able to create a plan with all 0 dimension charges and submit', () => {
cy.get('[data-test="create-plan"]').click({ force: true })
cy.url().should('be.equal', Cypress.config().baseUrl + '/create/plans')
cy.get('input[name="name"]').type(planWithChargesName)
cy.get('[data-test="submit"]').should('be.disabled')
cy.get('input[name="code"]').type(planWithChargesName)
cy.get('[data-test="submit"]').should('be.disabled')
cy.get('[data-test="show-description"]').click()
cy.get('[data-test="show-description"]').click({ force: true })
cy.get('textarea[name="description"]').type('I am a description')
cy.get('[data-test="submit"]').should('be.disabled')
cy.get('input[name="amountCents"]').type('30000')
cy.get('[data-test="submit"]').should('not.be.disabled')
cy.get('input[name="amountCurrency"]').click()
cy.get('[data-test="USD"]').click()
cy.get('input[name="amountCurrency"]').click({ force: true })
cy.get('[data-test="USD"]').click({ force: true })

// Standard
cy.get('[data-test="add-charge"]').first().click()
cy.get('[data-test="add-metered-charge"]').first().click()
cy.get('[data-option-index="0"]').click()
cy.get('[data-test="add-charge"]').first().click({ force: true })
cy.get('[data-test="add-metered-charge"]').first().click({ force: true })
cy.get('[data-option-index="0"]').click({ force: true })
cy.get('[data-test="remove-charge"]').should('exist').and('not.be.disabled')
cy.get('input[name="chargeModel"]').last().should('have.value', 'Standard pricing')
cy.get('input[name="properties.amount"]').type('5000')
cy.get('[data-test="submit"]').should('not.be.disabled')

// Graduated
cy.get('[data-test="add-charge"]').last().click()
cy.get('[data-test="add-metered-charge"]').last().click()
cy.get('[data-option-index="1"]').click()
cy.get('[data-test="add-charge"]').last().click({ force: true })
cy.get('[data-test="add-metered-charge"]').last().click({ force: true })
cy.get('[data-option-index="1"]').click({ force: true })
cy.get('[data-test="remove-charge"]').should('exist').and('not.be.disabled')
cy.get('input[name="chargeModel"]').last().click()
cy.get('[data-test="graduated"]').click()
cy.get('input[name="chargeModel"]').last().click({ force: true })
cy.get('[data-test="graduated"]').click({ force: true })
cy.get('input[name="chargeModel"]').last().should('have.value', 'Graduated pricing')
cy.get('[data-test="row-2"]').should('not.exist')
cy.get('[data-test="add-tier"]').click()
cy.get('[data-test="add-tier"]').click({ force: true })
cy.get('[data-test="row-2"]').should('exist')
cy.get('[data-test="cell-amount-0"]').type('1')
cy.get('[data-test="cell-amount-1"]').type('1')
cy.get('[data-test="cell-amount-2"]').type('1')
cy.get('[data-test="submit"]').should('not.be.disabled')

// Graduated percentage
// cy.get('[data-test="add-charge"]').last().click({ force: true })
// cy.get('[data-test="add-metered-charge"]').last().click({ force: true })
// cy.get('[data-option-index="1"]').click({ force: true })
// cy.get('[data-test="remove-charge"]').should('exist').and('not.be.disabled')
// cy.get('input[name="chargeModel"]').last().click({ force: true })
// cy.get('[data-test="graduated_percentage"]').click({ force: true })
// cy.get('[data-test="charge-accordion-2"]').within(() => {
// cy.get('input[name="chargeModel"]')
// .last()
// .should('have.value', 'Graduated percentage pricing')
// cy.get('[data-test="add-tier"]').last().click({ force: true })
// cy.get('[data-test="cell-rate-0"]').type('1')
// cy.get('[data-test="cell-rate-1"]').type('1')
// cy.get('[data-test="cell-rate-2"]').type('1')
// })
// cy.get('[data-test="row-2"]').should('have.length', 2)
// cy.get('[data-test="submit"]').should('not.be.disabled')

// Package
cy.get('[data-test="add-charge"]').last().click()
cy.get('[data-test="add-metered-charge"]').last().click()
cy.get('[data-option-index="1"]').click()
cy.get('[data-test="add-charge"]').last().click({ force: true })
cy.get('[data-test="add-metered-charge"]').last().click({ force: true })
cy.get('[data-option-index="1"]').click({ force: true })
cy.get('[data-test="remove-charge"]').should('exist').and('not.be.disabled')
cy.get('input[name="chargeModel"]').last().click()
cy.get('[data-test="package"]').click()
cy.get('input[name="chargeModel"]').last().click({ force: true })
cy.get('[data-test="package"]').click({ force: true })
cy.get('input[name="chargeModel"]').last().should('have.value', 'Package pricing')
cy.get('input[name="properties.amount"]').last().type('1')
cy.get('[data-test="submit"]').should('not.be.disabled')

// Percentage
cy.get('[data-test="add-charge"]').last().click()
cy.get('[data-test="add-metered-charge"]').last().click()
cy.get('[data-option-index="1"]').click()
cy.get('[data-test="add-charge"]').last().click({ force: true })
cy.get('[data-test="add-metered-charge"]').last().click({ force: true })
cy.get('[data-option-index="1"]').click({ force: true })
cy.get('[data-test="remove-charge"]').should('exist').and('not.be.disabled')
cy.get('input[name="chargeModel"]').last().click()
cy.get('[data-test="percentage"]').click()
cy.get('input[name="chargeModel"]').last().click({ force: true })
cy.get('[data-test="percentage"]').click({ force: true })
cy.get('input[name="chargeModel"]').last().should('have.value', 'Percentage pricing')
cy.get('input[name="properties.rate"]').last().type('1')
cy.get('[data-test="add-fixed-fee"]').click()
cy.get('[data-test="add-fixed-fee"]').click({ force: true })
cy.get('input[name="properties.fixedAmount"]').should('exist')
cy.get('[data-test="add-free-units"]').click()
cy.get('[data-test="add-free-units-events"]').click()
cy.get('[data-test="add-free-units"]').click({ force: true })
cy.get('[data-test="add-free-units-events"]').click({ force: true })
cy.get('[data-test="free-unit-per-event"] input').should('exist')
cy.get('[data-test="add-free-units"]').click()
cy.get('[data-test="add-free-units-total-amount"]').click()
cy.get('[data-test="add-free-units"]').click({ force: true })
cy.get('[data-test="add-free-units-total-amount"]').click({ force: true })
cy.get('[data-test="free-unit-per-total-aggregation"] input').should('exist')
cy.get('[data-test="submit"]').should('not.be.disabled')

// Volume
cy.get('[data-test="add-charge"]').last().click()
cy.get('[data-test="add-recurring-charge"]').last().click()
cy.get('[data-option-index="0"]').click()
cy.get('[data-test="add-charge"]').last().click({ force: true })
cy.get('[data-test="add-recurring-charge"]').last().click({ force: true })
cy.get('[data-option-index="0"]').click({ force: true })
cy.get('[data-test="remove-charge"]').should('exist').and('not.be.disabled')
cy.get('input[name="chargeModel"]').last().click()
cy.get('[data-test="volume"]').click()
cy.get('input[name="chargeModel"]').last().click({ force: true })
cy.get('[data-test="volume"]').click({ force: true })
cy.get('input[name="chargeModel"]').last().should('have.value', 'Volume pricing')
cy.get('[data-test="add-tier"]').last().click()
cy.get('[data-test="add-tier"]').last().click({ force: true })
cy.get('[data-test="cell-amount-0"]').last().type('1')
cy.get('[data-test="cell-amount-1"]').last().type('1')
cy.get('[data-test="cell-amount-2"]').last().type('1')
cy.get('[data-test="submit"]').should('not.be.disabled')

cy.get('[data-test="submit"]').click()
cy.get('[data-test="submit"]').click({ force: true })
cy.url().should('be.equal', Cypress.config().baseUrl + '/plans')
cy.contains(planWithChargesName).should('exist')
})
Expand All @@ -122,40 +141,40 @@ describe('Create plan', () => {
const planName = `plan ${randomId}`

// Default plan data
cy.get('[data-test="create-plan"]').click()
cy.get('[data-test="create-plan"]').click({ force: true })
cy.url().should('be.equal', Cypress.config().baseUrl + '/create/plans')
cy.get('input[name="name"]').type(planName)
cy.get('input[name="code"]').type(planName)
cy.get('[data-test="show-description"]').click()
cy.get('[data-test="show-description"]').click({ force: true })
cy.get('textarea[name="description"]').type('I am a description')
cy.get('input[name="amountCents"]').type('30000')

// Config charge
cy.get('[data-test="add-charge"]').last().click()
cy.get('[data-test="add-metered-charge"]').last().click()
cy.get('[data-option-index="1"]').click()
cy.get('[data-test="add-charge"]').last().click({ force: true })
cy.get('[data-test="add-metered-charge"]').last().click({ force: true })
cy.get('[data-option-index="1"]').click({ force: true })
cy.get('[data-test="remove-charge"]').should('exist').and('not.be.disabled')
cy.get('input[name="chargeModel"]').last().click()
cy.get('[data-test="percentage"]').click()
cy.get('input[name="chargeModel"]').last().click({ force: true })
cy.get('[data-test="percentage"]').click({ force: true })
cy.get('input[name="chargeModel"]').last().should('have.value', 'Percentage pricing')
cy.get('input[name="properties.rate"]').last().type('1')
cy.get('[data-test="add-fixed-fee"]').click()
cy.get('[data-test="add-fixed-fee"]').click({ force: true })
cy.get('input[name="properties.fixedAmount"]').last().type('1')
cy.get('[data-test="add-free-units"]').click()
cy.get('[data-test="add-free-units-events"]').click()
cy.get('[data-test="add-free-units"]').click({ force: true })
cy.get('[data-test="add-free-units-events"]').click({ force: true })
cy.get('[data-test="free-unit-per-event"] input').last().type('1')
cy.get('[data-test="add-free-units"]').click()
cy.get('[data-test="add-free-units-total-amount"]').click()
cy.get('[data-test="add-free-units"]').click({ force: true })
cy.get('[data-test="add-free-units-total-amount"]').click({ force: true })
cy.get('[data-test="free-unit-per-total-aggregation"] input').last().type('1')

// Test regression scenario
cy.get('[data-test="remove-fixed-fee"]').click()
cy.get('[data-test="remove-free-units-per-event"]').click()
cy.get('[data-test="remove-free-unit-per-total-aggregation"]').click()
cy.get('[data-test="remove-fixed-fee"]').click({ force: true })
cy.get('[data-test="remove-free-units-per-event"]').click({ force: true })
cy.get('[data-test="remove-free-unit-per-total-aggregation"]').click({ force: true })
cy.get('[data-test="submit"]').should('not.be.disabled')
cy.get('input[name="properties.rate"]').should('have.value', '1')

cy.get('[data-test="submit"]').click()
cy.get('[data-test="submit"]').click({ force: true })
cy.url().should('be.equal', Cypress.config().baseUrl + '/plans')
cy.contains(planName).should('exist')
})
Expand Down
4 changes: 3 additions & 1 deletion cypress/e2e/10-resources/t60-coupons-create-edit-apply.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ describe('Coupons', () => {
cy.get('[data-test="limited-plan-0"]').should('not.exist')
cy.get('[data-test="add-plan-limit"]').click()
cy.get('input[name="selectedPlan"]').click()
cy.get('[data-option-index="1"]').click()
cy.get('[data-option-index="0"]').click()
cy.get('[data-test="submitAddPlanToCouponDialog"]').click()
cy.get('[data-test="submit"]').should('be.disabled')
cy.get('input[name="amountCents"]').type('1')

cy.get('[data-test="submit"]').click()
cy.get(`[data-test="${couponName}"]`).should('exist')
Expand Down
8 changes: 8 additions & 0 deletions ditto/base.json
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,14 @@
"text_6435888d7cc86500646d8977": "Usage-based charges",
"text_6435895831d323008a47911f": "Metric already used in the plan, customers will be charged several times.",
"text_64358e074a3b7500714f256c": "If true, all charges will be aggregated and invoiced monthly, even if the plan is yearly.",
"text_64de472463e2da6b31737db0": "Graduated percentage pricing",
"text_64de472463e2da6b31737db8": "For instance, charge 5% + $1.00 per transaction for the first 1000 units and then $3% + 0.50 per transaction for the next ones.",
"text_64de472463e2da6b31737de0": "Rate",
"text_64de472463e2da6b31737df2": "Flat fee",
"text_64de472563e2da6b31737e6f": "First {{units}} units, {{rate}} of units + {{flatAmount}} flat fee",
"text_64de472563e2da6b31737e75": "After {{units}} units, {{rate}} of units + {{flatAmount}} flat fee",
"text_64de5dd470cdf80100c15fdb": "For all units, {{rate}} of units + {{flatAmount}} flat fee",
"text_64de472563e2da6b31737e77": "Fee applied to the whole tier",
"text_643e592657fc1ba5ce110b9e": "Add a spending minimum",
"text_64463aaa34904c00a23be4f7": "True-up",
"text_643e592657fc1ba5ce110c30": "Spending minimum",
Expand Down
4 changes: 4 additions & 0 deletions ditto/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,11 @@ sources:
fileName: 👍 [Ready for dev] - Settings - Net payment term
- name: 👍 [Ready for dev] - Add-on - Add tax to add-ons & one off invoice
id: 64d40b7c611d58bb88ec7d63
fileName: 👍 [Ready for dev] - Add-on - Add tax to add-ons & one off invoice
- name: 👍 [Ready for dev] - Plan - Quarterly plan interval
id: 64d63579f0cd15ff208e69e3
fileName: 👍 [Ready for dev] - Plan - Quarterly plan interval
- name: 👍 [Ready for dev] - Plans - Graduated percentage charge model
id: 64de471ef3038f0ad36833f8
format: flat
variants: true
3 changes: 3 additions & 0 deletions ditto/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ module.exports = {
"project_642d5eac2dc55f7f81e01dd4": {
"base": require('./-ready-for-dev---plans---create-a-plan-ui-refacto__base.json')
},
"project_64de471ef3038f0ad36833f8": {
"base": require('./-ready-for-dev---plans---graduated-percentage-charge-model__base.json')
},
"project_643e59213ea23c04674eba8c": {
"base": require('./-ready-for-dev---plans---set-a-minimum-spending-on-charges__base.json')
},
Expand Down
3 changes: 2 additions & 1 deletion src/components/designSystem/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,13 @@ export const Accordion = ({
size = AccordionSizeEnum.medium,
noContentMargin = false,
transitionProps = {},
...props
}: AccordionProps) => {
const [isOpen, setIsOpen] = useState(initiallyOpen)
const { translate } = useInternationalization()

return (
<Container id={id}>
<Container id={id} {...props}>
<StyledAccordion
expanded={isOpen}
onChange={(_, expanded) => setIsOpen(expanded)}
Expand Down
9 changes: 9 additions & 0 deletions src/components/designSystem/BetaChip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Chip, ChipSize } from './Chip'

type BetaChipProps = {
size?: ChipSize
}

export const BetaChip = ({ size = 'small' }: BetaChipProps) => {
return <Chip type="beta" label="Beta" size={size} />
}
27 changes: 23 additions & 4 deletions src/components/designSystem/Chip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ enum ChipVariantEnum {
enum ChipTypeEnum {
default = 'default',
error = 'error',
beta = 'beta',
}

type ChipSize = 'small' | 'medium'
export type ChipSize = 'xsmall' | 'small' | 'medium'
type ChipVariant = keyof typeof ChipVariantEnum

interface ChipGenericProps {
Expand Down Expand Up @@ -88,8 +89,20 @@ export const Chip = ({
)}
{avatarProps && <Avatar size="small" variant="user" {...avatarProps} />}
<Typography
variant={variant === ChipVariantEnum.secondary ? 'body' : 'captionHl'}
color={type === ChipTypeEnum.error ? 'danger600' : 'textSecondary'}
variant={
type === ChipTypeEnum.beta
? 'captionCode'
: variant === ChipVariantEnum.secondary
? 'body'
: 'captionHl'
}
color={
type === ChipTypeEnum.error
? 'danger600'
: type === ChipTypeEnum.beta
? 'info600'
: 'textSecondary'
}
>
{label}
</Typography>
Expand Down Expand Up @@ -120,7 +133,8 @@ export const Chip = ({
const Container = styled.div`
min-height: 32px;
height: fit-content;
border: 1px solid ${theme.palette.grey[300]};
outline: 1px solid ${theme.palette.grey[300]};
outline-offset: -1px;
background-color: ${theme.palette.grey[100]};
padding: ${theme.spacing(1)} ${theme.spacing(2)};
box-sizing: border-box;
Expand All @@ -138,6 +152,11 @@ const Container = styled.div`
}
/* Size */
&.chip-container--xsmall {
min-height: 0;
padding: 0 ${theme.spacing(1)};
border-radius: 4px;
}
&.chip-container--medium {
padding: 10px ${theme.spacing(3)};
}
Expand Down
1 change: 1 addition & 0 deletions src/components/designSystem/Typography.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ enum ColorTypeEnum {
grey600 = 'grey.600',
grey500 = 'grey.500',
grey400 = 'grey.400',
info600 = 'info.600',
primary600 = 'primary.600',
danger600 = 'error.600',
success600 = 'success.600',
Expand Down
2 changes: 1 addition & 1 deletion src/components/form/ComboBox/ComboBoxItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const ComboBoxItem = ({
const { className, ...allProps } = comboboxProps

return (
<ItemWrapper>
<ItemWrapper data-test={`combobox-item-${label}`}>
<ConditionalWrapper
condition={!!addValueRedirectionUrl}
invalidWrapper={(children) => <>{children}</>}
Expand Down
Loading

0 comments on commit 044650c

Please sign in to comment.