├── .auto-changelog ├── .cursor └── personalized_prompt.md ├── .env.sh ├── .env.template ├── .github ├── ISSUE_TEMPLATE │ ├── bug.md │ └── feature.md ├── pull_request_template.md └── workflows │ ├── codegen.yml │ ├── cypress.yml │ ├── lago-internal.yml │ ├── linter.yml │ ├── release.yml │ └── tests.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.mjs ├── .sentrycliignore ├── .vscode ├── extensions.json ├── lago.code-snippets └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── Dockerfile.dev ├── LICENSE ├── README.md ├── __mocks__ ├── styleMock.cjs └── svgMock.cjs ├── babel.config.cjs ├── ci └── docker-compose.ci.yml ├── codegen.yml ├── cypress ├── cypress.config.js ├── e2e │ ├── 00-auth │ │ ├── t0-signup.cy.ts │ │ └── t10-login.cy.ts │ ├── 10-resources │ │ ├── t10-create-taxes.cy.ts │ │ ├── t20-create-customer.cy.ts │ │ ├── t30-create-bm.cy.ts │ │ ├── t40-create-plan.cy.ts │ │ ├── t50-edit-plan.cy.ts │ │ ├── t60-coupons-create-edit-apply.cy.ts │ │ └── t70-addon-create-edit.cy.ts │ ├── t10-add-subscription.cy.ts │ ├── t20-invitations.cy.ts │ └── t30-create-one-off-invoice.cy.ts ├── support │ ├── e2e.ts │ ├── index.d.ts │ └── reusableConstants.ts └── tsconfig.json ├── eslint.config.mjs ├── globals.d.ts ├── index.html ├── jest-setup.ts ├── jest.config.ts ├── materialUiTheme.d.ts ├── nginx ├── gzip.conf └── nginx.conf ├── package.json ├── packages ├── configs │ ├── .prettierrc.mjs │ ├── eslint.config.mjs │ ├── package.json │ ├── tailwind.config.ts │ └── tsconfig.json └── design-system │ ├── eslint.config.mjs │ ├── package.json │ ├── playground │ ├── App.tsx │ ├── components │ │ ├── AccordionSection.tsx │ │ ├── AlertSection.tsx │ │ ├── AvatarSection.tsx │ │ ├── ButtonsSection.tsx │ │ ├── ChipsSection.tsx │ │ ├── GenericPlaceholderSection.tsx │ │ ├── IconsSection.tsx │ │ ├── LinksSection.tsx │ │ ├── PoppersSection.tsx │ │ ├── SelectorSection.tsx │ │ ├── ShowMoreSection.tsx │ │ ├── SkeletonSection.tsx │ │ └── TypographySection.tsx │ ├── index.html │ ├── main.tsx │ └── vite.config.playground.ts │ ├── postcss.config.mjs │ ├── public │ └── images │ │ ├── adyen.svg │ │ ├── airbyte.svg │ │ ├── anrok.svg │ │ ├── cashfree.svg │ │ ├── gocardless-large.svg │ │ ├── gocardless.svg │ │ ├── hightouch.svg │ │ ├── hubspot.svg │ │ ├── lago-tax-management.svg │ │ ├── logo │ │ ├── lago-logo-grey.svg │ │ └── lago-logo.svg │ │ ├── maneki │ │ ├── empty.svg │ │ ├── error.svg │ │ ├── success.svg │ │ └── thinking.svg │ │ ├── moneyhash.svg │ │ ├── netsuite.svg │ │ ├── okta.svg │ │ ├── oso.svg │ │ ├── psp-icons.svg │ │ ├── salesforce.svg │ │ ├── segment.svg │ │ ├── stripe.svg │ │ └── xero.svg │ ├── src │ ├── components │ │ ├── Accordion.tsx │ │ ├── Alert.tsx │ │ ├── Avatar.tsx │ │ ├── Button.tsx │ │ ├── ButtonLink.tsx │ │ ├── Chip.tsx │ │ ├── Dialog.tsx │ │ ├── Drawer │ │ │ ├── PreventClosingDrawerDialog.tsx │ │ │ └── index.tsx │ │ ├── GenericPlaceholder.tsx │ │ ├── Icon │ │ │ ├── Icon.tsx │ │ │ ├── index.tsx │ │ │ └── mapping.tsx │ │ ├── Popper.tsx │ │ ├── Selector.tsx │ │ ├── ShowMoreText.tsx │ │ ├── Skeleton.tsx │ │ ├── Tooltip.tsx │ │ ├── Typography.tsx │ │ ├── WarningDialog.tsx │ │ └── index.ts │ ├── icons │ │ ├── alphabet.svg │ │ ├── apps.svg │ │ ├── arrow-bottom.svg │ │ ├── arrow-down-circle-filled.svg │ │ ├── arrow-indent.svg │ │ ├── arrow-left-right.svg │ │ ├── arrow-left.svg │ │ ├── arrow-right.svg │ │ ├── arrow-top.svg │ │ ├── arrow-up-circle-filled.svg │ │ ├── ascending.svg │ │ ├── at.svg │ │ ├── attachment-na.svg │ │ ├── bank.svg │ │ ├── bell.svg │ │ ├── board.svg │ │ ├── book.svg │ │ ├── box.svg │ │ ├── bulb.svg │ │ ├── burger.svg │ │ ├── calendar.svg │ │ ├── chart-bar.svg │ │ ├── checkmark.svg │ │ ├── chevron-down-filled.svg │ │ ├── chevron-down.svg │ │ ├── chevron-left.svg │ │ ├── chevron-right-filled.svg │ │ ├── chevron-right.svg │ │ ├── chevron-top.svg │ │ ├── chevron-up-down.svg │ │ ├── clock.svg │ │ ├── close-circle-filled.svg │ │ ├── close-circle-unfilled.svg │ │ ├── close.svg │ │ ├── code.svg │ │ ├── coin-dollar.svg │ │ ├── collect.svg │ │ ├── command.svg │ │ ├── company.svg │ │ ├── condition.svg │ │ ├── content-left-align.svg │ │ ├── coupon.svg │ │ ├── descending.svg │ │ ├── document.svg │ │ ├── dots-horizontal.svg │ │ ├── download.svg │ │ ├── duplicate.svg │ │ ├── error-filled.svg │ │ ├── error-unfilled.svg │ │ ├── eye-hidden.svg │ │ ├── eye.svg │ │ ├── filter.svg │ │ ├── flash-filled.svg │ │ ├── flash.svg │ │ ├── formulas.svg │ │ ├── globe.svg │ │ ├── google.svg │ │ ├── handle.svg │ │ ├── hashtag.svg │ │ ├── heart.svg │ │ ├── id.svg │ │ ├── info-circle.svg │ │ ├── key.svg │ │ ├── keyboard-enter.svg │ │ ├── laptop.svg │ │ ├── link.svg │ │ ├── lock.svg │ │ ├── log-out.svg │ │ ├── magnifying-glass.svg │ │ ├── mail.svg │ │ ├── map.svg │ │ ├── micro.svg │ │ ├── minus.svg │ │ ├── okta.svg │ │ ├── outside.svg │ │ ├── paperclip.svg │ │ ├── partially-filled.svg │ │ ├── pause-circle-filled.svg │ │ ├── pause-circle-unfilled.svg │ │ ├── pen.svg │ │ ├── percentage.svg │ │ ├── pivot.svg │ │ ├── play.svg │ │ ├── plug.svg │ │ ├── plus.svg │ │ ├── processing.svg │ │ ├── pulse.svg │ │ ├── push.svg │ │ ├── puzzle.svg │ │ ├── question-circle.svg │ │ ├── receipt.svg │ │ ├── reload.svg │ │ ├── requested.svg │ │ ├── resize-expand.svg │ │ ├── resize-reduce.svg │ │ ├── rocket.svg │ │ ├── rotate.svg │ │ ├── schema.svg │ │ ├── scissor.svg │ │ ├── settings.svg │ │ ├── share.svg │ │ ├── smartphone.svg │ │ ├── sparkles.svg │ │ ├── star-filled.svg │ │ ├── star-outlined-hidden.svg │ │ ├── stop.svg │ │ ├── switch-off.svg │ │ ├── switch.svg │ │ ├── sync.svg │ │ ├── table-horizontale.svg │ │ ├── table.svg │ │ ├── target.svg │ │ ├── terminal.svg │ │ ├── text.svg │ │ ├── trash.svg │ │ ├── unlock.svg │ │ ├── user-add.svg │ │ ├── user-multiple.svg │ │ ├── user.svg │ │ ├── validate-filled.svg │ │ ├── validate-unfilled.svg │ │ ├── wallet.svg │ │ ├── warning-filled.svg │ │ └── warning-unfilled.svg │ ├── index.ts │ ├── lib │ │ ├── .gitignore │ │ ├── conditionalWrapper.tsx │ │ ├── index.ts │ │ ├── muiTheme.ts │ │ ├── provider.tsx │ │ └── tw.ts │ ├── style.css │ └── types │ │ └── svg.d.ts │ ├── tailwind.config.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── tsconfig.playground.json │ └── vite.config.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── postcss.config.cjs ├── public ├── favicon-local.svg ├── favicon-prod.svg └── favicon-staging.svg ├── renovate.json ├── scripts ├── codemod.js ├── transforms │ ├── skeleton-migrate-margin-values.js │ ├── skeleton-migrate-width-values.js │ └── skeleton-remove-unnecessary-height.js └── translations │ ├── add.js │ └── inspect.js ├── src ├── App.tsx ├── components │ ├── CodeSnippet │ │ ├── CodeSnippet.tsx │ │ ├── codeSnippet.css │ │ └── index.ts │ ├── ConditionalWrapper.tsx │ ├── ErrorBoundary.tsx │ ├── GenericPlaceholder.tsx │ ├── LogoPicker.tsx │ ├── OrganizationLogoPicker.tsx │ ├── OverviewCard.tsx │ ├── PaymentProviderChip.tsx │ ├── PremiumWarningDialog.tsx │ ├── RouteWrapper.tsx │ ├── SearchInput.tsx │ ├── TimezoneDate.tsx │ ├── UserIdentifier.tsx │ ├── WarningDialog.tsx │ ├── __tests__ │ │ └── WarningDialog.test.tsx │ ├── activityLogs │ │ ├── ActivityLogsTable.tsx │ │ └── utils.ts │ ├── addOns │ │ ├── AddOnCodeSnippet.tsx │ │ ├── DeleteAddOnDialog.tsx │ │ └── types.ts │ ├── alerts │ │ ├── Thresholds.tsx │ │ └── __tests__ │ │ │ └── Thresholds.test.tsx │ ├── analytics │ │ ├── AnalyticsStateContext.tsx │ │ ├── __tests__ │ │ │ └── utils.test.ts │ │ ├── invoices │ │ │ └── utils.ts │ │ ├── mrr │ │ │ ├── MrrBreakdownSection.tsx │ │ │ ├── MrrOverviewSection.tsx │ │ │ ├── __tests__ │ │ │ │ └── utils.test.ts │ │ │ ├── fixture.ts │ │ │ ├── useMrrAnalyticsOverview.ts │ │ │ └── utils.ts │ │ ├── prepaidCredits │ │ │ ├── PrepaidCreditsOverviewSection.tsx │ │ │ ├── fixture.ts │ │ │ ├── usePrepaidCreditsAnalyticsOverview.ts │ │ │ └── utils.ts │ │ ├── revenueStreams │ │ │ ├── RevenueStreamsBreakdownSection.tsx │ │ │ ├── RevenueStreamsCustomerBreakdownSection.tsx │ │ │ ├── RevenueStreamsOverviewSection.tsx │ │ │ ├── RevenueStreamsPlanBreakdownSection.tsx │ │ │ ├── useRevenueAnalyticsOverview.ts │ │ │ └── utils.ts │ │ └── usage │ │ │ ├── UsageBreakdownBillableMetrics.tsx │ │ │ ├── UsageBreakdownIndividualSection.tsx │ │ │ ├── UsageBreakdownSection.tsx │ │ │ ├── UsageOverviewSection.tsx │ │ │ ├── fixture.ts │ │ │ ├── types.ts │ │ │ ├── useUsageAnalyticsBillableMetric.ts │ │ │ ├── useUsageAnalyticsBreakdown.ts │ │ │ ├── useUsageAnalyticsOverview.ts │ │ │ └── utils.ts │ ├── auth │ │ └── GoogleAuthButton.tsx │ ├── billableMetrics │ │ ├── BillableMetricCodeSnippet.tsx │ │ ├── BillableMetricDetailsOverview.tsx │ │ ├── CustomExpressionDrawer.tsx │ │ ├── DeleteBillableMetricDialog.tsx │ │ └── utils.ts │ ├── coupons │ │ ├── AddBillableMetricToCouponDialog.tsx │ │ ├── AddPlanToCouponDialog.tsx │ │ ├── CouponCaption.tsx │ │ ├── CouponCodeSnippet.tsx │ │ ├── DeleteCouponDialog.tsx │ │ ├── TerminateCouponDialog.tsx │ │ └── __tests__ │ │ │ └── CouponCaption.test.tsx │ ├── creditNote │ │ ├── CreditNoteActionsLine.tsx │ │ ├── CreditNoteBadge.tsx │ │ ├── CreditNoteCodeSnippet.tsx │ │ ├── CreditNoteEstimationLine.tsx │ │ ├── CreditNoteFormCalculation.tsx │ │ ├── CreditNoteFormItem.tsx │ │ ├── CreditNoteItemsForm.tsx │ │ ├── CreditNotesTable.tsx │ │ ├── __tests__ │ │ │ ├── fixtures.ts │ │ │ └── utils.test.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── customerPortal │ │ ├── PortalCustomerInfos.tsx │ │ ├── PortalInvoicesList.tsx │ │ ├── common │ │ │ ├── CustomerPortalLoading.tsx │ │ │ ├── CustomerPortalSections.tsx │ │ │ ├── CustomerPortalSidebar.tsx │ │ │ ├── PageTitle.tsx │ │ │ ├── SectionError.tsx │ │ │ ├── SectionLoading.tsx │ │ │ ├── SectionTitle.tsx │ │ │ ├── TextButton.tsx │ │ │ ├── hooks │ │ │ │ └── useCustomerPortalNavigation.ts │ │ │ └── useCustomerPortalTranslate.ts │ │ ├── customerInformation │ │ │ └── CustomerInformationPage.tsx │ │ ├── usage │ │ │ ├── UsagePage.tsx │ │ │ ├── UsageSection.tsx │ │ │ └── UsageSubscriptionItem.tsx │ │ ├── utils.ts │ │ └── wallet │ │ │ ├── WalletPage.tsx │ │ │ └── WalletSection.tsx │ ├── customers │ │ ├── AddCouponToCustomerDialog.tsx │ │ ├── CustomerCreditNotesList.tsx │ │ ├── CustomerInvoicesList.tsx │ │ ├── CustomerInvoicesTab.tsx │ │ ├── CustomerMainInfos.tsx │ │ ├── CustomerPaymentsList.tsx │ │ ├── CustomerPaymentsTab.tsx │ │ ├── CustomerSettings.tsx │ │ ├── DeleteCustomerDialog.tsx │ │ ├── DeleteCustomerDocumentLocaleDialog.tsx │ │ ├── DeleteCustomerFinalizeZeroAmountInvoiceDialog.tsx │ │ ├── DeleteCustomerGracePeriodeDialog.tsx │ │ ├── DeleteCustomerNetPaymentTermDialog.tsx │ │ ├── DeleteCustomerVatRateDialog.tsx │ │ ├── EditCustomerDocumentLocaleDialog.tsx │ │ ├── EditCustomerDunningCampaignDialog.tsx │ │ ├── EditCustomerInvoiceCustomSectionsDialog.tsx │ │ ├── EditCustomerInvoiceGracePeriodDialog.tsx │ │ ├── EditCustomerVatRateDialog.tsx │ │ ├── __tests__ │ │ │ ├── EditCustomerVatRateDialog.test.tsx │ │ │ └── utils.test.ts │ │ ├── createCustomer │ │ │ ├── BillingAccordion.tsx │ │ │ ├── CustomerInformation.tsx │ │ │ ├── ExternalAppsAccordion │ │ │ │ ├── AccountingProvidersAccordion.tsx │ │ │ │ ├── CRMProvidersAccordion.tsx │ │ │ │ ├── ExternalAppsAccordionLayout.tsx │ │ │ │ ├── PaymentProvidersAccordion.tsx │ │ │ │ ├── TaxProvidersAccordion.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── utils.ts │ │ │ └── MetadataAccordion.tsx │ │ ├── creditNotes │ │ │ └── VoidCreditNoteDialog.tsx │ │ ├── overview │ │ │ ├── CustomerCoupons.tsx │ │ │ ├── CustomerOverview.tsx │ │ │ └── CustomerSubscriptionsList.tsx │ │ ├── subscriptions │ │ │ ├── SubscriptionDatesOffsetHelperComponent.tsx │ │ │ ├── TerminateCustomerSubscriptionDialog.tsx │ │ │ └── __tests__ │ │ │ │ └── SubscriptionDatesOffsetHelperComponent.test.tsx │ │ ├── usage │ │ │ ├── CustomerUsage.tsx │ │ │ └── SubscriptionUsageDetailDrawer.tsx │ │ └── utils.ts │ ├── designSystem │ │ ├── Accordion.tsx │ │ ├── Alert.tsx │ │ ├── Button.tsx │ │ ├── ButtonLink.tsx │ │ ├── Card.tsx │ │ ├── Chip.tsx │ │ ├── Dialog.tsx │ │ ├── Drawer.tsx │ │ ├── Filters │ │ │ ├── ActiveFiltersList.tsx │ │ │ ├── CustomerAccountTypeQuickFilter.tsx │ │ │ ├── Filters.tsx │ │ │ ├── FiltersPanelItemTypeSwitch.tsx │ │ │ ├── FiltersPanelPopper.tsx │ │ │ ├── InvoiceStatusQuickFilter.tsx │ │ │ ├── QuickFilters.tsx │ │ │ ├── TimeGranularitySelector.tsx │ │ │ ├── __tests__ │ │ │ │ ├── useFilters.test.tsx │ │ │ │ └── utils.test.ts │ │ │ ├── context.tsx │ │ │ ├── filtersElements │ │ │ │ ├── FiltersItemActivityIds.tsx │ │ │ │ ├── FiltersItemActivitySources.tsx │ │ │ │ ├── FiltersItemActivityTypes.tsx │ │ │ │ ├── FiltersItemAmount.tsx │ │ │ │ ├── FiltersItemApiKeyIds.tsx │ │ │ │ ├── FiltersItemBillingEntity.tsx │ │ │ │ ├── FiltersItemBillingEntityCode.tsx │ │ │ │ ├── FiltersItemCountry.tsx │ │ │ │ ├── FiltersItemCreditNoteCreditStatus.tsx │ │ │ │ ├── FiltersItemCreditNoteReason.tsx │ │ │ │ ├── FiltersItemCreditNoteRefundStatus.tsx │ │ │ │ ├── FiltersItemCurrency.tsx │ │ │ │ ├── FiltersItemCustomer.tsx │ │ │ │ ├── FiltersItemCustomerAccountType.tsx │ │ │ │ ├── FiltersItemCustomerType.tsx │ │ │ │ ├── FiltersItemDate.tsx │ │ │ │ ├── FiltersItemInvoiceNumber.tsx │ │ │ │ ├── FiltersItemInvoiceType.tsx │ │ │ │ ├── FiltersItemIssuingDate.tsx │ │ │ │ ├── FiltersItemLoggedDate.tsx │ │ │ │ ├── FiltersItemPartiallyPaid.tsx │ │ │ │ ├── FiltersItemPaymentDisputeLost.tsx │ │ │ │ ├── FiltersItemPaymentOverdue.tsx │ │ │ │ ├── FiltersItemPaymentStatus.tsx │ │ │ │ ├── FiltersItemPeriod.tsx │ │ │ │ ├── FiltersItemPlanCode.tsx │ │ │ │ ├── FiltersItemResourceIds.tsx │ │ │ │ ├── FiltersItemResourceTypes.tsx │ │ │ │ ├── FiltersItemSelfBilled.tsx │ │ │ │ ├── FiltersItemStatus.tsx │ │ │ │ ├── FiltersItemSubscription.tsx │ │ │ │ ├── FiltersItemUserEmails.tsx │ │ │ │ └── FiltersItemWebhookStatus.tsx │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ ├── useFilters.ts │ │ │ └── utils.ts │ │ ├── Icon │ │ │ ├── Icon.tsx │ │ │ ├── index.tsx │ │ │ └── mapping.tsx │ │ ├── InfiniteScroll.tsx │ │ ├── NavigationTab.tsx │ │ ├── Popper.tsx │ │ ├── PreventClosingDrawerDialog.tsx │ │ ├── Selector.tsx │ │ ├── ShowMoreText.tsx │ │ ├── Skeleton.tsx │ │ ├── Status.tsx │ │ ├── Table │ │ │ ├── ChargeTable.tsx │ │ │ ├── HorizontalDataTable.tsx │ │ │ ├── Table.tsx │ │ │ └── index.ts │ │ ├── Toasts │ │ │ ├── Toast.tsx │ │ │ ├── ToastContainer.tsx │ │ │ └── index.ts │ │ ├── Tooltip.tsx │ │ ├── Typography.tsx │ │ ├── VerticalMenu.tsx │ │ ├── __tests__ │ │ │ ├── Button.test.tsx │ │ │ ├── ButtonLink.test.tsx │ │ │ └── Table.test.tsx │ │ ├── graphs │ │ │ ├── AreaChart.tsx │ │ │ ├── ChartHeader.tsx │ │ │ ├── InlineBarsChart.tsx │ │ │ ├── MultipleLineChart.tsx │ │ │ ├── StackedBarChart.tsx │ │ │ ├── __tests__ │ │ │ │ └── utils.test.ts │ │ │ ├── const.tsx │ │ │ ├── fixtures.ts │ │ │ ├── types.ts │ │ │ └── utils.ts │ │ └── index.ts │ ├── developers │ │ ├── DevtoolsRouter.tsx │ │ ├── DevtoolsView.tsx │ │ ├── LogsLayout.tsx │ │ ├── activityLogs │ │ │ ├── ActivityLogDetails.tsx │ │ │ ├── ActivityLogTable.tsx │ │ │ └── ActivityLogs.tsx │ │ ├── apiKeys │ │ │ ├── ApiKeys.tsx │ │ │ ├── DeleteApiKeyDialog.tsx │ │ │ └── RotateApiKeyDialog.tsx │ │ ├── events │ │ │ ├── EventDetails.tsx │ │ │ ├── EventTable.tsx │ │ │ └── Events.tsx │ │ ├── views.ts │ │ └── webhooks │ │ │ ├── CreateWebhookDialog.tsx │ │ │ ├── DeleteWebhookDialog.tsx │ │ │ ├── WebhookLogDetails.tsx │ │ │ ├── WebhookLogTable.tsx │ │ │ ├── WebhookLogs.tsx │ │ │ └── Webhooks.tsx │ ├── emails │ │ └── DunningEmail.tsx │ ├── exports │ │ └── ExportDialog.tsx │ ├── form │ │ ├── AmountInput │ │ │ ├── AmountInput.tsx │ │ │ ├── AmountInputField.tsx │ │ │ └── index.ts │ │ ├── ButtonSelector │ │ │ ├── ButtonSelector.tsx │ │ │ ├── ButtonSelectorField.tsx │ │ │ ├── TabButton.tsx │ │ │ └── index.tsx │ │ ├── Checkbox │ │ │ ├── Checkbox.tsx │ │ │ ├── CheckboxField.tsx │ │ │ ├── CheckboxIcon.tsx │ │ │ └── index.ts │ │ ├── ComboBox │ │ │ ├── ComboBox.tsx │ │ │ ├── ComboBoxField.tsx │ │ │ ├── ComboBoxInput.tsx │ │ │ ├── ComboBoxItem.tsx │ │ │ ├── ComboBoxItemWrapper.tsx │ │ │ ├── ComboBoxPopperFactory.tsx │ │ │ ├── ComboBoxVirtualizedList.tsx │ │ │ ├── ComboboxList.tsx │ │ │ ├── index.tsx │ │ │ └── types.ts │ │ ├── DatePicker │ │ │ ├── DatePicker.tsx │ │ │ ├── DatePickerField.tsx │ │ │ └── index.ts │ │ ├── JsonEditor │ │ │ ├── JsonEditor.tsx │ │ │ ├── JsonEditorField.tsx │ │ │ ├── index.ts │ │ │ └── jsonEditor.css │ │ ├── MultipleComboBox │ │ │ ├── MultipleComboBox.tsx │ │ │ ├── MultipleComboBoxField.tsx │ │ │ ├── MultipleComboBoxItemWrapper.tsx │ │ │ ├── MultipleComboBoxList.tsx │ │ │ ├── MultipleComboBoxPopperFactory.tsx │ │ │ ├── MultipleComboBoxVirtualizedList.tsx │ │ │ ├── index.tsx │ │ │ └── types.ts │ │ ├── Radio │ │ │ ├── Radio.tsx │ │ │ ├── RadioField.tsx │ │ │ ├── RadioGroupField.tsx │ │ │ ├── RadioIcon.tsx │ │ │ └── index.ts │ │ ├── Switch │ │ │ ├── Switch.tsx │ │ │ ├── SwitchField.tsx │ │ │ └── index.ts │ │ ├── TextInput │ │ │ ├── TextInput.tsx │ │ │ ├── TextInputField.tsx │ │ │ ├── __tests__ │ │ │ │ └── FormatValue.test.ts │ │ │ └── index.ts │ │ └── index.ts │ ├── graphs │ │ ├── Gross.tsx │ │ ├── Invoices.tsx │ │ ├── MonthSelectorDropdown.tsx │ │ ├── Mrr.tsx │ │ ├── Overview.tsx │ │ ├── Usage.tsx │ │ ├── __tests__ │ │ │ ├── Gross.test.ts │ │ │ ├── Invoices.test.ts │ │ │ ├── Mrr.test.ts │ │ │ ├── Usage.test.ts │ │ │ └── utils.test.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── invoices │ │ ├── AddMetadataDrawer.tsx │ │ ├── DisputeInvoiceDialog.tsx │ │ ├── EditInvoiceDisplayName.tsx │ │ ├── EditInvoiceItemDescriptionDialog.tsx │ │ ├── EditInvoiceItemTaxDialog.tsx │ │ ├── EditInvoicePaymentStatusDialog.tsx │ │ ├── FinalizeInvoiceDialog.tsx │ │ ├── InvoiceCreditNoteList.tsx │ │ ├── InvoiceCreditNotesTable.tsx │ │ ├── InvoiceCustomerInfos.tsx │ │ ├── InvoicePaymentList.tsx │ │ ├── InvoicesList.tsx │ │ ├── Metadatas.tsx │ │ ├── PaymentsList.tsx │ │ ├── VoidInvoiceDialog.tsx │ │ ├── details │ │ │ ├── DeleteAdjustedFeeDialog.tsx │ │ │ ├── EditFeeDrawer.tsx │ │ │ ├── InvoiceDetailsTable.tsx │ │ │ ├── InvoiceDetailsTableBodyLine.tsx │ │ │ ├── InvoiceDetailsTableBodyLineGraduated.tsx │ │ │ ├── InvoiceDetailsTableBodyLineGraduatedPercentage.tsx │ │ │ ├── InvoiceDetailsTableBodyLinePackage.tsx │ │ │ ├── InvoiceDetailsTableBodyLinePercentage.tsx │ │ │ ├── InvoiceDetailsTableBodyLineVolume.tsx │ │ │ ├── InvoiceDetailsTableFooter.tsx │ │ │ ├── InvoiceDetailsTableHeader.tsx │ │ │ ├── InvoiceDetailsTablePeriodLine.tsx │ │ │ ├── InvoiceFeeAdvanceDetailsTable.tsx │ │ │ ├── InvoiceFeeArrearsDetailsTable.tsx │ │ │ ├── __tests__ │ │ │ │ ├── InvoiceDetailsTableBodyLine.test.ts │ │ │ │ ├── fixture.ts │ │ │ │ └── utils.test.ts │ │ │ └── utils.ts │ │ └── types.ts │ ├── layouts │ │ ├── CenteredPage.tsx │ │ ├── Charts.tsx │ │ ├── DetailsPage.tsx │ │ ├── Drawer.tsx │ │ ├── FullscreenPage.tsx │ │ ├── Integrations.tsx │ │ ├── Section.tsx │ │ └── Settings.tsx │ ├── plans │ │ ├── ChargeAccordion.tsx │ │ ├── ChargeBillingRadioGroup.tsx │ │ ├── ChargeFilter.tsx │ │ ├── ChargeOptionsAccordion.tsx │ │ ├── ChargePercentage.tsx │ │ ├── ChargeWrapperSwitch.tsx │ │ ├── ChargesSection.tsx │ │ ├── CommitmentsSection.tsx │ │ ├── CustomCharge.tsx │ │ ├── DeletePlanDialog.tsx │ │ ├── DynamicCharge.tsx │ │ ├── EditCustomChargeDrawer.tsx │ │ ├── FixedFeeSection.tsx │ │ ├── GraduatedChargeTable.tsx │ │ ├── GraduatedPercentageChargeTable.tsx │ │ ├── ImpactOverridenSubscriptionsDialog.tsx │ │ ├── PackageCharge.tsx │ │ ├── PlanCodeSnippet.tsx │ │ ├── PlanSettingsSection.tsx │ │ ├── ProgressiveBillingSection.tsx │ │ ├── RemoveChargeWarningDialog.tsx │ │ ├── StandardCharge.tsx │ │ ├── VolumeChargeTable.tsx │ │ ├── __tests__ │ │ │ ├── ChargeAccordion.test.tsx │ │ │ └── utils.test.ts │ │ ├── details │ │ │ ├── PlanDetailsAdvancedSettingsSection.tsx │ │ │ ├── PlanDetailsChargeWrapperSwitch.tsx │ │ │ ├── PlanDetailsChargesSection.tsx │ │ │ ├── PlanDetailsChargesSectionAccordion.tsx │ │ │ ├── PlanDetailsFixedFeeAccordion.tsx │ │ │ ├── PlanDetailsOverview.tsx │ │ │ └── PlanSubscriptionList.tsx │ │ ├── types.ts │ │ └── utils.ts │ ├── settings │ │ ├── LanguageSettingsButton.tsx │ │ ├── PreviewEmailLayout.tsx │ │ ├── authentication │ │ │ ├── AddOktaDialog.tsx │ │ │ ├── DeleteOktaIntegrationDialog.tsx │ │ │ └── useOktaIntegration.ts │ │ ├── dunnings │ │ │ ├── DefaultCampaignDialog.tsx │ │ │ ├── DeleteCampaignDialog.tsx │ │ │ └── PreviewCampaignEmailDrawer.tsx │ │ ├── emails │ │ │ └── UpdateBillingEntityLogoDialog.tsx │ │ ├── integrations │ │ │ ├── AddAdyenDialog.tsx │ │ │ ├── AddAnrokDialog.tsx │ │ │ ├── AddAvalaraDialog.tsx │ │ │ ├── AddCashfreeDialog.tsx │ │ │ ├── AddEditDeleteSuccessRedirectUrlDialog.tsx │ │ │ ├── AddGocardlessDialog.tsx │ │ │ ├── AddHubspotDialog.tsx │ │ │ ├── AddLagoTaxManagementDialog.tsx │ │ │ ├── AddMoneyhashDialog.tsx │ │ │ ├── AddNetsuiteDialog.tsx │ │ │ ├── AddSalesforceDialog.tsx │ │ │ ├── AddStripeDialog.tsx │ │ │ ├── AddXeroDialog.tsx │ │ │ ├── AnrokIntegrationItemsList.tsx │ │ │ ├── AnrokIntegrationItemsListAddons.tsx │ │ │ ├── AnrokIntegrationItemsListBillableMetrics.tsx │ │ │ ├── AnrokIntegrationItemsListDefault.tsx │ │ │ ├── AnrokIntegrationMapItemDialog.tsx │ │ │ ├── AnrokIntegrationSettings.tsx │ │ │ ├── AvalaraIntegrationItemsList.tsx │ │ │ ├── AvalaraIntegrationItemsListAddons.tsx │ │ │ ├── AvalaraIntegrationItemsListBillableMetrics.tsx │ │ │ ├── AvalaraIntegrationItemsListDefault.tsx │ │ │ ├── AvalaraIntegrationMapItemDialog.tsx │ │ │ ├── AvalaraIntegrationSettings.tsx │ │ │ ├── DeleteAdyenIntegrationDialog.tsx │ │ │ ├── DeleteAnrokIntegrationDialog.tsx │ │ │ ├── DeleteAvalaraIntegrationDialog.tsx │ │ │ ├── DeleteCashfreeIntegrationDialog.tsx │ │ │ ├── DeleteGocardlessIntegrationDialog.tsx │ │ │ ├── DeleteHubspotIntegrationDialog.tsx │ │ │ ├── DeleteMoneyhashIntegrationDialog.tsx │ │ │ ├── DeleteNetsuiteIntegrationDialog.tsx │ │ │ ├── DeleteSalesforceIntegrationDialog.tsx │ │ │ ├── DeleteStripeIntegrationDialog.tsx │ │ │ ├── DeleteXeroIntegrationDialog.tsx │ │ │ ├── IntegrationItemHeader.tsx │ │ │ ├── IntegrationItemLine.tsx │ │ │ ├── NetsuiteIntegrationItemsList.tsx │ │ │ ├── NetsuiteIntegrationItemsListAddons.tsx │ │ │ ├── NetsuiteIntegrationItemsListBillableMetrics.tsx │ │ │ ├── NetsuiteIntegrationItemsListDefault.tsx │ │ │ ├── NetsuiteIntegrationMapItemDialog.tsx │ │ │ ├── NetsuiteIntegrationSettings.tsx │ │ │ ├── XeroIntegrationItemsList.tsx │ │ │ ├── XeroIntegrationItemsListAddons.tsx │ │ │ ├── XeroIntegrationItemsListBillableMetrics.tsx │ │ │ ├── XeroIntegrationItemsListDefault.tsx │ │ │ ├── XeroIntegrationMapItemDialog.tsx │ │ │ └── XeroIntegrationSettings.tsx │ │ ├── invoices │ │ │ ├── AddBillingEntityVatRateDialog.tsx │ │ │ ├── DefaultCustomSectionDialog.tsx │ │ │ ├── DeleteBillingEntityVatRateDialog.tsx │ │ │ ├── DeleteCustomSectionDialog.tsx │ │ │ ├── EditBillingEntityDocumentLocaleDialog.tsx │ │ │ ├── EditBillingEntityGracePeriodDialog.tsx │ │ │ ├── EditBillingEntityInvoiceNumberingDialog.tsx │ │ │ ├── EditBillingEntityInvoiceTemplateDialog.tsx │ │ │ ├── EditDefaultCurrencyDialog.tsx │ │ │ ├── EditFinalizeZeroAmountInvoiceDialog.tsx │ │ │ ├── EditNetPaymentTermDialog.tsx │ │ │ └── PreviewCustomSectionDrawer.tsx │ │ └── members │ │ │ ├── CreateInviteDialog.tsx │ │ │ ├── EditInviteRoleDialog.tsx │ │ │ ├── EditMemberRoleDialog.tsx │ │ │ ├── RevokeInviteDialog.tsx │ │ │ ├── RevokeMembershipDialog.tsx │ │ │ └── RolePickerField.tsx │ ├── subscriptions │ │ ├── SubscriptionAlertsList.tsx │ │ ├── SubscriptionCurrentUsageTable.tsx │ │ ├── SubscriptionDetailsOverview.tsx │ │ ├── SubscriptionInformations.tsx │ │ ├── SubscriptionUsageLifetimeGraph.tsx │ │ ├── SubscriptionUsageTabContent.tsx │ │ ├── __tests__ │ │ │ └── utils.test.ts │ │ ├── alerts │ │ │ └── DeleteAlertDialog.tsx │ │ └── utils.ts │ ├── taxes │ │ ├── DeleteTaxDialog.tsx │ │ ├── TaxCodeSnippet.tsx │ │ └── types.ts │ └── wallets │ │ ├── CustomerWalletList.tsx │ │ ├── TerminateCustomerWalletDialog.tsx │ │ ├── VoidWalletDialog.tsx │ │ ├── WalletAccordion.tsx │ │ ├── WalletCodeSnippet.tsx │ │ ├── WalletDetailsDrawer.tsx │ │ ├── WalletTransactionList.tsx │ │ ├── WalletTransactionListItem │ │ ├── ListItem.tsx │ │ ├── __tests__ │ │ │ └── index.test.tsx │ │ └── index.tsx │ │ ├── __tests__ │ │ └── utils.test.ts │ │ └── utils.ts ├── core │ ├── apolloClient │ │ ├── __tests__ │ │ │ └── errorUtils.test.ts │ │ ├── cache.ts │ │ ├── cacheUtils.ts │ │ ├── errorUtils.ts │ │ ├── graphqlResolvers.tsx │ │ ├── index.ts │ │ ├── init.ts │ │ └── reactiveVars │ │ │ ├── authTokenVar.ts │ │ │ ├── customerPortalTokenVar.ts │ │ │ ├── duplicatePlanVar.ts │ │ │ ├── envGlobalVar.ts │ │ │ ├── index.ts │ │ │ ├── internationalizationVar.ts │ │ │ ├── locationHistoryVar.ts │ │ │ └── toastVar.ts │ ├── constants │ │ ├── countryCodes.ts │ │ ├── externalUrls.ts │ │ ├── filters.ts │ │ ├── form.ts │ │ ├── globalTypes.ts │ │ ├── localStorageKeys.ts │ │ ├── paymentTerm.ts │ │ ├── statusCouponMapping.ts │ │ ├── statusInvoiceMapping.ts │ │ ├── statusSubscriptionMapping.ts │ │ ├── statusWebhookMapping.ts │ │ └── tabsOptions.ts │ ├── formats │ │ ├── __tests__ │ │ │ ├── countryDataForCombobox.test.ts │ │ │ ├── fixture.ts │ │ │ ├── formatCreditNotesItems.test.ts │ │ │ ├── formatInvoiceItemsMap.test.ts │ │ │ ├── intlFormatNumber.test.ts │ │ │ └── obfuscate.test.ts │ │ ├── countryDataForCombobox.ts │ │ ├── formatBillableMetricsItems.ts │ │ ├── formatCreditNotesItems.ts │ │ ├── formatInvoiceItemsMap.ts │ │ ├── intlFormatNumber.ts │ │ └── obfuscate.ts │ ├── router │ │ ├── AuthRoutes.tsx │ │ ├── CustomerPortalRoutes.tsx │ │ ├── CustomerRoutes.tsx │ │ ├── ObjectsRoutes.tsx │ │ ├── SettingRoutes.tsx │ │ ├── index.tsx │ │ ├── types.ts │ │ └── utils.tsx │ ├── serializers │ │ ├── __tests__ │ │ │ ├── serializeAmount.test.ts │ │ │ ├── serializeCreditNoteInput.test.ts │ │ │ └── serializePlanInput.test.ts │ │ ├── getPropertyShape.ts │ │ ├── index.ts │ │ ├── serializeAmount.ts │ │ ├── serializeCreditNoteInput.ts │ │ └── serializePlanInput.ts │ ├── timezone │ │ ├── __tests__ │ │ │ ├── config.test.ts │ │ │ └── utils.test.ts │ │ ├── config.ts │ │ ├── index.ts │ │ └── utils.ts │ ├── translations │ │ ├── __tests__ │ │ │ └── utils.test.ts │ │ ├── documentLocales.ts │ │ ├── index.ts │ │ ├── types.ts │ │ └── utils.ts │ └── utils │ │ ├── __tests__ │ │ ├── copyToClipboard.test.ts │ │ ├── dateUtils.test.ts │ │ ├── responsiveProps.test.ts │ │ ├── snippetBuilder.test.ts │ │ └── urlUtils.test.ts │ │ ├── billingEntityNumberPreview.ts │ │ ├── copyToClipboard.ts │ │ ├── dateUtils.ts │ │ ├── downloadFiles.ts │ │ ├── featureFlags.ts │ │ ├── getCurrentBreakpoint.ts │ │ ├── responsiveProps.ts │ │ ├── snippetBuilder.ts │ │ └── urlUtils.ts ├── formValidation │ ├── __tests__ │ │ ├── chargeSchemaGraduated.test.ts │ │ ├── chargeSchemaGraduatedPercentage.test.ts │ │ ├── chargeSchemaPackage.test.ts │ │ ├── chargeSchemaPercentage.test.ts │ │ ├── chargeSchemaStandard.test.ts │ │ ├── chargeSchemaVolume.test.ts │ │ ├── feesSchema.test.ts │ │ └── metadataSchema.test.ts │ ├── chargeSchema.ts │ ├── feesSchema.ts │ ├── initializeYup.ts │ ├── metadataSchema.ts │ └── schema.d.ts ├── generated │ └── graphql.tsx ├── hooks │ ├── __tests__ │ │ ├── fixtures.ts │ │ ├── useCreateCreditNote.test.ts │ │ ├── useDebouncedSearch.test.ts │ │ └── usePermissions.test.ts │ ├── auth │ │ └── useIsAuthenticated.ts │ ├── core │ │ ├── __tests__ │ │ │ ├── useInternationalization.test.tsx │ │ │ └── useLocationHistory.test.ts │ │ ├── useContextualLocale.ts │ │ ├── useInternationalization.ts │ │ └── useLocationHistory.ts │ ├── customer │ │ └── useAddSubscription.tsx │ ├── paymentReceipts │ │ └── useDownloadPaymentReceipts.ts │ ├── plans │ │ ├── __tests__ │ │ │ ├── fixture.ts │ │ │ ├── useChargeForm.test.tsx │ │ │ ├── useGraduatedChargeForm.test.tsx │ │ │ ├── useGraduatedPercentageChargeForm.test.tsx │ │ │ └── useVolumeChargeForm.test.tsx │ │ ├── useChargeForm.tsx │ │ ├── useGraduatedChargeForm.ts │ │ ├── useGraduatedPercentageChargeForm.ts │ │ ├── usePlanForm.tsx │ │ ├── useProgressiveBillingForm.tsx │ │ └── useVolumeChargeForm.ts │ ├── ui │ │ ├── __tests__ │ │ │ ├── useListKeyNavigation.test.tsx │ │ │ └── useShortcuts.test.tsx │ │ ├── useListKeyNavigation.tsx │ │ └── useShortcuts.tsx │ ├── useCreateCreditNote.ts │ ├── useCreateEditAddOn.ts │ ├── useCreateEditBillableMetric.ts │ ├── useCreateEditBillingEntity.ts │ ├── useCreateEditCoupon.ts │ ├── useCreateEditCustomer.ts │ ├── useCreateEditDunningCampaign.ts │ ├── useCreateEditInvoiceCustomSection.ts │ ├── useCreateEditTax.ts │ ├── useCurrentUser.ts │ ├── useDebouncedSearch.ts │ ├── useDeveloperTool.tsx │ ├── useEmailConfig.ts │ ├── useIntegrations.ts │ ├── useOrganizationInfos.ts │ ├── usePermissions.ts │ ├── usePermissionsInvoiceActions.ts │ └── useSalesForceConfig.ts ├── layouts │ ├── Settings.tsx │ └── SideNavLayout.tsx ├── main.css ├── main.tsx ├── pages │ ├── AddOnDetails.tsx │ ├── AddOnsList.tsx │ ├── AlertForm.tsx │ ├── Analytics.tsx │ ├── BillableMetricDetails.tsx │ ├── BillableMetricsList.tsx │ ├── CouponDetails.tsx │ ├── CouponsList.tsx │ ├── CreateAddOn.tsx │ ├── CreateBillableMetric.tsx │ ├── CreateCoupon.tsx │ ├── CreateCreditNote.tsx │ ├── CreateCustomer.tsx │ ├── CreateInvoice.tsx │ ├── CreatePayment.tsx │ ├── CreatePlan.tsx │ ├── CreateSubscription.tsx │ ├── CreateTax.tsx │ ├── CreditNoteDetails.tsx │ ├── CustomerDetails.tsx │ ├── CustomerDraftInvoicesList.tsx │ ├── CustomerInvoiceDetails.tsx │ ├── CustomerRequestOverduePayment │ │ ├── __tests__ │ │ │ └── validateEmails.test.ts │ │ ├── components │ │ │ ├── EmailPreview.tsx │ │ │ ├── FreemiumAlert.tsx │ │ │ └── RequestPaymentForm.tsx │ │ ├── index.tsx │ │ └── validateEmails.ts │ ├── CustomersList.tsx │ ├── Error404.tsx │ ├── Forbidden.tsx │ ├── Home.tsx │ ├── Invitation.tsx │ ├── InvitationInit.tsx │ ├── InvoiceOverview.tsx │ ├── InvoicesPage.tsx │ ├── OldAnalytics.tsx │ ├── PaymentDetails.tsx │ ├── PlanDetails.tsx │ ├── PlansList.tsx │ ├── SubscriptionDetails.tsx │ ├── __devOnly │ │ ├── DesignSystem.tsx │ │ └── fixtures.ts │ ├── analytics │ │ ├── Invoices.tsx │ │ ├── Mrr.tsx │ │ ├── NewAnalytics.tsx │ │ ├── PrepaidCredits.tsx │ │ ├── RevenueStreams.tsx │ │ ├── Usage.tsx │ │ └── UsageBillableMetric.tsx │ ├── auth │ │ ├── ForgotPassword.tsx │ │ ├── GoogleAuthCallback.tsx │ │ ├── Login.tsx │ │ ├── LoginOkta.tsx │ │ ├── OktaAuthCallback.tsx │ │ ├── PortalInit.tsx │ │ ├── ResetPassword.tsx │ │ └── SignUp.tsx │ ├── customerPortal │ │ └── CustomerPortal.tsx │ ├── developers │ │ └── ApiKeysForm.tsx │ ├── settings │ │ ├── AdyenIntegrationDetails.tsx │ │ ├── AdyenIntegrations.tsx │ │ ├── AnrokIntegrationDetails.tsx │ │ ├── AnrokIntegrations.tsx │ │ ├── Authentication │ │ │ ├── Authentication.tsx │ │ │ └── OktaAuthenticationDetails.tsx │ │ ├── AvalaraIntegrationDetails.tsx │ │ ├── AvalaraIntegrations.tsx │ │ ├── BillingEntity │ │ │ ├── BillingEntity.tsx │ │ │ ├── components │ │ │ │ └── BillingEntityHeader.tsx │ │ │ └── sections │ │ │ │ ├── BillingEntityCreateEdit.tsx │ │ │ │ ├── BillingEntityEmailScenarios.tsx │ │ │ │ ├── BillingEntityEmailScenariosConfig.tsx │ │ │ │ ├── BillingEntityInvoiceSettings.tsx │ │ │ │ ├── BillingEntityMain.tsx │ │ │ │ ├── general │ │ │ │ ├── BillingEntityGeneral.tsx │ │ │ │ ├── EditBillingEntityTimezoneDialog.tsx │ │ │ │ ├── InformationBlock.tsx │ │ │ │ └── TimezoneBlock.tsx │ │ │ │ └── taxes │ │ │ │ ├── ApplyTaxDialog.tsx │ │ │ │ ├── BillingEntityTaxesSettings.tsx │ │ │ │ └── RemoveTaxDialog.tsx │ │ ├── CashfreeIntegrationDetails.tsx │ │ ├── CashfreeIntegrations.tsx │ │ ├── Dunnings │ │ │ ├── CreateDunning.tsx │ │ │ └── Dunnings.tsx │ │ ├── GocardlessIntegrationDetails.tsx │ │ ├── GocardlessIntegrationOauthCallback.tsx │ │ ├── GocardlessIntegrations.tsx │ │ ├── HubspotIntegrationDetails.tsx │ │ ├── HubspotIntegrations.tsx │ │ ├── Integrations.tsx │ │ ├── Invoices │ │ │ ├── CreateCustomSection.tsx │ │ │ └── InvoiceSections.tsx │ │ ├── LagoTaxManagementIntegration.tsx │ │ ├── Members.tsx │ │ ├── MoneyhashIntegrationDetails.tsx │ │ ├── MoneyhashIntegrations.tsx │ │ ├── NetsuiteIntegrationDetails.tsx │ │ ├── NetsuiteIntegrations.tsx │ │ ├── SalesforceIntegrationDetails.tsx │ │ ├── SalesforceIntegrations.tsx │ │ ├── SettingsHomePage.tsx │ │ ├── StripeIntegrationDetails.tsx │ │ ├── StripeIntegrations.tsx │ │ ├── TaxesSettings.tsx │ │ ├── XeroIntegrationDetails.tsx │ │ └── XeroIntegrations.tsx │ └── wallet │ │ ├── CreateWallet.tsx │ │ ├── CreateWalletTopUp.tsx │ │ ├── components │ │ ├── SettingsSection.tsx │ │ └── TopUpSection.tsx │ │ ├── form.ts │ │ ├── index.ts │ │ └── types.ts ├── public │ ├── icons │ │ ├── alphabet.svg │ │ ├── apps.svg │ │ ├── arrow-bottom.svg │ │ ├── arrow-down-circle-filled.svg │ │ ├── arrow-indent.svg │ │ ├── arrow-left-right.svg │ │ ├── arrow-left.svg │ │ ├── arrow-right.svg │ │ ├── arrow-top.svg │ │ ├── arrow-up-circle-filled.svg │ │ ├── ascending.svg │ │ ├── at.svg │ │ ├── attachment-na.svg │ │ ├── bank.svg │ │ ├── bell.svg │ │ ├── board.svg │ │ ├── book.svg │ │ ├── box.svg │ │ ├── bulb.svg │ │ ├── burger.svg │ │ ├── calendar.svg │ │ ├── chart-bar.svg │ │ ├── checkmark.svg │ │ ├── chevron-down-filled.svg │ │ ├── chevron-down.svg │ │ ├── chevron-left.svg │ │ ├── chevron-right-filled.svg │ │ ├── chevron-right.svg │ │ ├── chevron-top.svg │ │ ├── chevron-up-down.svg │ │ ├── clock.svg │ │ ├── close-circle-filled.svg │ │ ├── close-circle-unfilled.svg │ │ ├── close.svg │ │ ├── code.svg │ │ ├── coin-dollar.svg │ │ ├── collect.svg │ │ ├── command.svg │ │ ├── company.svg │ │ ├── condition.svg │ │ ├── content-left-align.svg │ │ ├── coupon.svg │ │ ├── descending.svg │ │ ├── document.svg │ │ ├── dots-horizontal.svg │ │ ├── download.svg │ │ ├── duplicate.svg │ │ ├── error-filled.svg │ │ ├── error-unfilled.svg │ │ ├── eye-hidden.svg │ │ ├── eye.svg │ │ ├── filter.svg │ │ ├── flash-filled.svg │ │ ├── flash.svg │ │ ├── formulas.svg │ │ ├── globe.svg │ │ ├── google.svg │ │ ├── handle.svg │ │ ├── hashtag.svg │ │ ├── heart.svg │ │ ├── id.svg │ │ ├── info-circle.svg │ │ ├── key.svg │ │ ├── keyboard-enter.svg │ │ ├── laptop.svg │ │ ├── link.svg │ │ ├── lock.svg │ │ ├── log-out.svg │ │ ├── magnifying-glass.svg │ │ ├── mail.svg │ │ ├── map.svg │ │ ├── micro.svg │ │ ├── minus.svg │ │ ├── okta.svg │ │ ├── outside.svg │ │ ├── paperclip.svg │ │ ├── partially-filled.svg │ │ ├── pause-circle-filled.svg │ │ ├── pause-circle-unfilled.svg │ │ ├── pen.svg │ │ ├── percentage.svg │ │ ├── pivot.svg │ │ ├── play.svg │ │ ├── plug.svg │ │ ├── plus.svg │ │ ├── processing.svg │ │ ├── pulse.svg │ │ ├── push.svg │ │ ├── puzzle.svg │ │ ├── question-circle.svg │ │ ├── receipt.svg │ │ ├── reload.svg │ │ ├── requested.svg │ │ ├── resize-expand.svg │ │ ├── resize-reduce.svg │ │ ├── rocket.svg │ │ ├── rotate.svg │ │ ├── schema.svg │ │ ├── scissor.svg │ │ ├── settings.svg │ │ ├── share.svg │ │ ├── smartphone.svg │ │ ├── sparkles.svg │ │ ├── star-filled.svg │ │ ├── star-outlined-hidden.svg │ │ ├── stop.svg │ │ ├── switch-off.svg │ │ ├── switch.svg │ │ ├── sync.svg │ │ ├── table-horizontale.svg │ │ ├── table.svg │ │ ├── target.svg │ │ ├── terminal.svg │ │ ├── text.svg │ │ ├── trash.svg │ │ ├── unlock.svg │ │ ├── user-add.svg │ │ ├── user-multiple.svg │ │ ├── user.svg │ │ ├── validate-filled.svg │ │ ├── validate-unfilled.svg │ │ ├── wallet.svg │ │ ├── warning-filled.svg │ │ └── warning-unfilled.svg │ └── images │ │ ├── adyen.svg │ │ ├── airbyte.svg │ │ ├── anrok.svg │ │ ├── avalara.svg │ │ ├── cashfree.svg │ │ ├── gocardless-large.svg │ │ ├── gocardless.svg │ │ ├── hightouch.svg │ │ ├── hubspot.svg │ │ ├── lago-tax-management.svg │ │ ├── logo │ │ ├── lago-logo-grey.svg │ │ └── lago-logo.svg │ │ ├── maneki │ │ ├── empty.svg │ │ ├── error.svg │ │ ├── success.svg │ │ └── thinking.svg │ │ ├── moneyhash.svg │ │ ├── netsuite.svg │ │ ├── okta.svg │ │ ├── oso.svg │ │ ├── psp-icons.svg │ │ ├── salesforce.svg │ │ ├── segment.svg │ │ ├── stripe.svg │ │ └── xero.svg ├── styles │ ├── auth.tsx │ ├── colorsPalette.ts │ ├── customer.tsx │ ├── designSystem │ │ ├── DrawerContent.tsx │ │ ├── List.tsx │ │ ├── MenuPopper.tsx │ │ ├── PageHeader.tsx │ │ └── index.ts │ ├── index.ts │ ├── mainObjectsForm.tsx │ ├── muiTheme.ts │ └── utils.ts └── test-utils.tsx ├── start.dev.sh ├── tailwind.config.ts ├── translations ├── base.json ├── de.json ├── es.json ├── fr.json ├── it.json ├── nb.json └── sv.json ├── tsconfig.json ├── vite-env.d.ts └── vite.config.ts /.auto-changelog: -------------------------------------------------------------------------------- 1 | { 2 | "commitLimit": false, 3 | "unreleased": false 4 | } 5 | -------------------------------------------------------------------------------- /.env.template: -------------------------------------------------------------------------------- 1 | API_URL=http://localhost:3000 2 | APP_ENV=development 3 | CODEGEN_API=http://api.lago.dev/graphql 4 | CYPRESS_APP_URL=https://app.lago.dev 5 | GOCARDLESS_CLIENT_ID=__YOUR_GOCARDLESS_CLIENT_ID__ 6 | GOCARDLESS_CLIENT_SECRET=__YOUR_GOCARDLESS_SECRET__ 7 | LAGO_OAUTH_PROXY_URL=https://proxy.lago.dev 8 | SENTRY_DSN=__YOUR_SENTRY_DSN__ 9 | LAGO_DISABLE_PDF_GENERATION=false 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: "\U0001F41E bug" 6 | 7 | --- 8 | 9 | **Describe the bug** 10 | 11 | 12 | **To Reproduce** 13 | 19 | 20 | **Expected behavior** 21 | 22 | 23 | **Screenshots** 24 | 25 | 26 | **Environment infos** 27 | 30 | 31 | **Additional context** 32 | 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature 3 | about: New feature TODO related to a spec 4 | title: "[FEAT]" 5 | labels: "\U0001F6E0 feature" 6 | assignees: MorganeLecurieux 7 | 8 | --- 9 | 10 | 11 | 12 | 13 | ## TODO 14 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Context 2 | 3 | Include relevant motivation and context. 4 | 5 | ## Description 6 | 7 | Describe your changes in detail. 8 | 9 | 10 | Fixes LAGO-XXX -------------------------------------------------------------------------------- /.github/workflows/lago-internal.yml: -------------------------------------------------------------------------------- 1 | name: "Lago Internal" 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | lago-internal: 8 | name: Lago Internal 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Repository Dispatch 12 | uses: peter-evans/repository-dispatch@v3 13 | with: 14 | token: ${{ secrets.GH_TOKEN}} 15 | repository: getlago/lago-deploy 16 | event-type: front-push-main 17 | client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}' -------------------------------------------------------------------------------- /.github/workflows/linter.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | pull_request: 5 | types: [opened, reopened, review_requested, ready_for_review, synchronize] 6 | 7 | jobs: 8 | run-linters: 9 | name: Run linters 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Check out Git repository 14 | uses: actions/checkout@v4 15 | 16 | - name: Install pnpm 17 | uses: pnpm/action-setup@v4 18 | 19 | - name: Set up Node.js 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: 20 23 | cache: 'pnpm' 24 | 25 | - name: Install Node.js dependencies 26 | run: pnpm install 27 | 28 | - name: Run Translation Check 29 | run: | 30 | pnpm run translations:inspect 31 | 32 | - name: Run linters 33 | run: | 34 | pnpm run lint 35 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | pull_request: 5 | types: [opened, reopened, review_requested, ready_for_review, synchronize] 6 | 7 | jobs: 8 | run-tests: 9 | if: github.event.pull_request.draft == false 10 | name: Run tests 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Check out Git repository 14 | uses: actions/checkout@v4 15 | 16 | - name: Install pnpm 17 | uses: pnpm/action-setup@v4 18 | 19 | - name: Set up Node.js 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: 20 23 | cache: 'pnpm' 24 | 25 | - name: Install Node.js dependencies 26 | run: pnpm install 27 | 28 | - name: Run tests 29 | run: | 30 | pnpm run test 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .DS_Store 4 | .env 5 | coverage 6 | cypress/downloads 7 | .swc/ 8 | .pnpm-store/ 9 | node-jiti/ 10 | v8-compile-cache* -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .github 4 | generated 5 | globals.d.ts 6 | ci 7 | cypress/downloads 8 | cypress/screenshots -------------------------------------------------------------------------------- /.prettierrc.mjs: -------------------------------------------------------------------------------- 1 | import lagoPrettierConfig from 'lago-configs/prettier' 2 | 3 | /** 4 | * @type {import("prettier").Config} 5 | */ 6 | const config = { 7 | ...lagoPrettierConfig, 8 | tailwindConfig: './tailwind.config.ts', 9 | } 10 | 11 | export default config 12 | -------------------------------------------------------------------------------- /.sentrycliignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .DS_Store 4 | .env 5 | coverage 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "styled-components.vscode-styled-components", 8 | "kumar-harsh.graphql-for-vscode", 9 | "dbaeumer.vscode-eslint", 10 | "esbenp.prettier-vscode", 11 | "ionutvmi.path-autocomplete", 12 | "christian-kohler.path-intellisense", 13 | "dsznajder.es7-react-js-snippets", 14 | "bradlc.vscode-tailwindcss" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.css": "tailwindcss" 4 | }, 5 | "tailwindCSS.experimental.classRegex": [ 6 | ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], 7 | ["cx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"], 8 | ["tw\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"] 9 | ], 10 | "eslint.useFlatConfig": true, 11 | "javascript.preferences.importModuleSpecifier": "non-relative", 12 | "typescript.preferences.importModuleSpecifier": "non-relative", 13 | } 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine as build 2 | 3 | WORKDIR /app 4 | 5 | COPY . . 6 | 7 | RUN apk add python3 build-base && \ 8 | corepack enable && corepack prepare pnpm@latest --activate && \ 9 | rm -rf /app/node_modules && \ 10 | pnpm install && pnpm build 11 | 12 | FROM nginx:1.27-alpine 13 | 14 | WORKDIR /usr/share/nginx/html 15 | 16 | RUN apk add --no-cache bash 17 | RUN apk update && apk upgrade libx11 nghttp2 openssl tiff curl busybox 18 | 19 | COPY --from=build /app/dist . 20 | COPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf 21 | COPY ./nginx/gzip.conf /etc/nginx/conf.d/gzip.conf 22 | COPY ./.env.sh ./.env.sh 23 | 24 | EXPOSE 80 25 | 26 | ENTRYPOINT ["/bin/bash", "-c", "./.env.sh && nginx -g \"daemon off;\""] 27 | -------------------------------------------------------------------------------- /Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine 2 | 3 | WORKDIR /app 4 | 5 | RUN apk add --no-cache bash && \ 6 | corepack enable && corepack prepare pnpm@latest --activate 7 | 8 | CMD ["./start.dev.sh"] 9 | -------------------------------------------------------------------------------- /__mocks__/styleMock.cjs: -------------------------------------------------------------------------------- 1 | module.exports = {} 2 | -------------------------------------------------------------------------------- /babel.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ['@babel/preset-env', { targets: { node: 'current' }, modules: 'commonjs' }], 4 | '@babel/preset-typescript', 5 | ['@babel/preset-react', { runtime: 'automatic' }], 6 | ], 7 | } 8 | -------------------------------------------------------------------------------- /codegen.yml: -------------------------------------------------------------------------------- 1 | overwrite: true 2 | schema: 3 | - ${CODEGEN_API} 4 | - './src/core/apolloClient/graphqlResolvers.tsx' 5 | documents: ['src/**/*.tsx', 'src/**/*.ts'] 6 | generates: 7 | src/generated/graphql.tsx: 8 | plugins: 9 | - 'typescript' 10 | - 'typescript-operations' 11 | - 'typescript-react-apollo' 12 | config: 13 | reactApolloVersion: 3 14 | exportFragmentSpreadSubTypes: true 15 | -------------------------------------------------------------------------------- /cypress/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress' 2 | import dotenv from 'dotenv' 3 | import fs from 'node:fs' 4 | 5 | dotenv.config({ path: '../.env' }) 6 | 7 | export default defineConfig({ 8 | projectId: 'u863yi', 9 | retries: 3, 10 | e2e: { 11 | baseUrl: process.env.CYPRESS_APP_URL, 12 | experimentalRunAllSpecs: true, 13 | setupNodeEvents(on) { 14 | on('after:spec', (_spec, results) => { 15 | if (results && results.video) { 16 | // Do we have failures for any retry attempts? 17 | const failures = results.tests.some((test) => 18 | test.attempts.some((attempt) => attempt.state === 'failed'), 19 | ) 20 | if (!failures) { 21 | // delete the video if the spec passed and no tests retried 22 | fs.unlinkSync(results.video) 23 | } 24 | } 25 | }) 26 | }, 27 | }, 28 | }) 29 | -------------------------------------------------------------------------------- /cypress/support/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare namespace Cypress { 4 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 5 | interface Chainable { 6 | /** 7 | * Login user 8 | * @example 9 | * cy.login('usertest@lago.com', 'P@ssw0rd') 10 | */ 11 | login(email?: string, password?: string): Chainable 12 | } 13 | 14 | interface Cypress { 15 | mocha: any // for Cypress.mocha 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /cypress/support/reusableConstants.ts: -------------------------------------------------------------------------------- 1 | export const planWithChargesName = 'a plan with charges' 2 | export const planWithChargeCodeNew = 'new_code_plan_with_charges' 3 | export const customerName = 'George de la jungle' 4 | export const userEmail = 'user-e2e-tests@lago.com' 5 | export const userPassword = 'P@ssw0rdd' 6 | 7 | // Form variable 8 | export const TAX_TWENTY_CODE = 'twenty' 9 | export const TAX_TEN_CODE = 'ten' 10 | -------------------------------------------------------------------------------- /cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | "sourceMap": false, 6 | "types": ["cypress"] 7 | }, 8 | "exclude": ["node_modules"] 9 | } 10 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'eslint/config' 2 | import config from 'lago-configs/eslint' 3 | 4 | export default defineConfig([ 5 | { 6 | files: [ 7 | 'src/**/*.{js,ts,jsx,tsx}', 8 | 'scripts/**/*.{js,ts,jsx,tsx}', 9 | 'cypress/**/*.{js,ts,jsx,tsx}', 10 | ], 11 | extends: [config], 12 | }, 13 | ]) 14 | -------------------------------------------------------------------------------- /globals.d.ts: -------------------------------------------------------------------------------- 1 | interface Window { 2 | Cypress: any 3 | Lago: any 4 | } 5 | 6 | declare type AppEnvEnum = import('./src/core/constants/globalTypes').AppEnvEnum 7 | 8 | declare var APP_ENV: AppEnvEnum 9 | declare var API_URL: string 10 | declare var LAGO_DOMAIN: string 11 | declare var APP_VERSION: string 12 | declare var LAGO_OAUTH_PROXY_URL: string 13 | declare var LAGO_DISABLE_SIGNUP: string 14 | declare var NANGO_PUBLIC_KEY: string 15 | declare var SENTRY_DSN: string 16 | declare var LAGO_DISABLE_PDF_GENERATION: string 17 | 18 | declare module '*.svg' { 19 | const content: any 20 | export default content 21 | } 22 | declare module '*.png' { 23 | const value: any 24 | export default value 25 | } 26 | 27 | declare module '*.jpg' { 28 | const value: any 29 | export default value 30 | } 31 | 32 | declare module '*.jpeg' { 33 | const value: any 34 | export default value 35 | } 36 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 17 | 18 | 19 | <%- title %> 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /jest-setup.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom' 2 | -------------------------------------------------------------------------------- /nginx/gzip.conf: -------------------------------------------------------------------------------- 1 | gzip on; 2 | gzip_http_version 1.0; 3 | gzip_comp_level 5; # 1-9 4 | gzip_min_length 256; 5 | gzip_proxied any; 6 | gzip_vary on; 7 | 8 | # MIME-types 9 | gzip_types 10 | application/atom+xml 11 | application/javascript 12 | application/json 13 | application/rss+xml 14 | application/vnd.ms-fontobject 15 | application/x-font-ttf 16 | application/x-web-app-manifest+json 17 | application/xhtml+xml 18 | application/xml 19 | font/opentype 20 | image/svg+xml 21 | image/x-icon 22 | text/css 23 | text/plain 24 | text/x-component; -------------------------------------------------------------------------------- /nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | listen [::]:80; 4 | 5 | location / { 6 | add_header Content-Security-Policy "frame-ancestors *.force.com *.zive.app *.zive.dev *.localhost *.systeminit.com *.storecommander.com *.storecommander.eu *.dev.storecommander.eu;" always; 7 | root /usr/share/nginx/html; 8 | index index.html index.htm; 9 | try_files $uri $uri/ /index.html =404; 10 | } 11 | 12 | include /etc/nginx/extra-conf.d/*.conf; 13 | } -------------------------------------------------------------------------------- /packages/configs/.prettierrc.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: ['@trivago/prettier-plugin-sort-imports', 'prettier-plugin-tailwindcss'], 3 | semi: false, 4 | singleQuote: true, 5 | printWidth: 100, 6 | tabWidth: 2, 7 | importOrder: ['^@core/(.*)$', '^[~/]', '^./(.*)', '^../(..*)'], 8 | importOrderSeparation: true, 9 | importOrderSortSpecifiers: true, 10 | importOrderCaseInsensitive: true, 11 | tailwindFunctions: ['tw', 'clsx', 'cva'], 12 | overrides: [ 13 | { 14 | files: '*.svg', 15 | options: { parser: 'html' }, 16 | }, 17 | ], 18 | } 19 | -------------------------------------------------------------------------------- /packages/configs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "exclude": [ 4 | "node_modules", 5 | "dist", 6 | "**/*.spec.ts", 7 | "**/*.test.ts", 8 | "**/*.stories.tsx", 9 | "**/*.stories.ts" 10 | ], 11 | "include": ["./**/*.ts", "./**/*.tsx"], 12 | "compilerOptions": { 13 | // Language and module settings 14 | "target": "es5", 15 | "module": "esnext", 16 | "lib": ["es6", "dom", "esnext"], 17 | "moduleResolution": "bundler", 18 | "jsx": "react-jsx", 19 | 20 | // Project behavior 21 | "noEmit": true, 22 | "isolatedModules": true, 23 | "resolveJsonModule": true, 24 | "esModuleInterop": true, 25 | "allowSyntheticDefaultImports": true, 26 | "allowImportingTsExtensions": true, 27 | "allowUmdGlobalAccess": true, 28 | 29 | // Type safety and consistency 30 | "strict": true, 31 | "forceConsistentCasingInFileNames": true, 32 | "skipLibCheck": true, 33 | "sourceMap": true 34 | } 35 | } -------------------------------------------------------------------------------- /packages/design-system/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import config from 'lago-configs/eslint' 2 | 3 | export default config 4 | -------------------------------------------------------------------------------- /packages/design-system/playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 17 | Lago - Design System Playground 18 | 19 | 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /packages/design-system/playground/main.tsx: -------------------------------------------------------------------------------- 1 | import { StyledEngineProvider, ThemeProvider } from '@mui/material' 2 | import React from 'react' 3 | import ReactDOM from 'react-dom/client' 4 | import { BrowserRouter } from 'react-router-dom' 5 | 6 | import { theme } from '~/lib/muiTheme' 7 | import '~/style.css' 8 | 9 | import App from './App' 10 | 11 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 12 | ReactDOM.createRoot(document.getElementById('root')!).render( 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | , 22 | ) 23 | -------------------------------------------------------------------------------- /packages/design-system/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | import autoprefixer from 'autoprefixer' 2 | import cssnano from 'cssnano' 3 | import postcssPresetEnv from 'postcss-preset-env' 4 | import tailwindcss from 'tailwindcss' 5 | 6 | /** @type {import('postcss-load-config').Config} */ 7 | const config = { 8 | plugins: [ 9 | postcssPresetEnv, 10 | tailwindcss, 11 | autoprefixer, 12 | process.env.APP_ENV === 'production' ? cssnano : null, 13 | ].filter(Boolean), 14 | } 15 | 16 | export default config 17 | -------------------------------------------------------------------------------- /packages/design-system/public/images/adyen.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /packages/design-system/public/images/airbyte.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /packages/design-system/public/images/anrok.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /packages/design-system/public/images/cashfree.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /packages/design-system/public/images/gocardless-large.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/public/images/gocardless.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/public/images/hightouch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/design-system/public/images/moneyhash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/design-system/public/images/stripe.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /packages/design-system/src/components/Icon/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Icon' 2 | export * from './mapping' 3 | -------------------------------------------------------------------------------- /packages/design-system/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Accordion' 2 | export * from './Alert' 3 | export * from './Avatar' 4 | export * from './Button' 5 | export * from './ButtonLink' 6 | export * from './Chip' 7 | export * from './Dialog' 8 | export * from './Drawer' 9 | export * from './GenericPlaceholder' 10 | export * from './Icon' 11 | export * from './Popper' 12 | export * from './Selector' 13 | export * from './ShowMoreText' 14 | export * from './Skeleton' 15 | export * from './Tooltip' 16 | export * from './Typography' 17 | export * from './WarningDialog' 18 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/alphabet.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/apps.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/arrow-bottom.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/arrow-down-circle-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/arrow-indent.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/arrow-left-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/arrow-top.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/arrow-up-circle-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/bell.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/bulb.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/burger.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/chart-bar.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/checkmark.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/chevron-down-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/chevron-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/chevron-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/chevron-right-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/chevron-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/chevron-top.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/close-circle-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/coin-dollar.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/collect.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/company.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/condition.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/content-left-align.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/document.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/duplicate.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/error-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/error-unfilled.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/filter.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/flash-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/flash.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/handle.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/id.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/info-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/key.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/keyboard-enter.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/laptop.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/micro.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/minus.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/partially-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/pause-circle-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/pen.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/processing.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/pulse.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/push.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/receipt.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/schema.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/share.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/sparkles.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/stop.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/switch-off.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/switch.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/table-horizontale.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/table.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/target.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/text.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/trash.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/validate-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/validate-unfilled.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/icons/wallet.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/design-system/src/index.ts: -------------------------------------------------------------------------------- 1 | import './style.css' 2 | 3 | export * from './components' 4 | export * from './lib' 5 | -------------------------------------------------------------------------------- /packages/design-system/src/lib/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /packages/design-system/src/lib/conditionalWrapper.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react' 2 | 3 | interface ConditionalWrapperProps { 4 | condition: boolean 5 | validWrapper: (children: ReactNode) => JSX.Element | null 6 | invalidWrapper: (children: ReactNode) => JSX.Element | null 7 | children: ReactNode 8 | } 9 | 10 | export const ConditionalWrapper = ({ 11 | condition, 12 | validWrapper, 13 | invalidWrapper, 14 | children, 15 | }: ConditionalWrapperProps) => { 16 | return condition ? validWrapper(children) : invalidWrapper(children) 17 | } 18 | -------------------------------------------------------------------------------- /packages/design-system/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './conditionalWrapper' 2 | export * from './provider' 3 | export * from './tw' 4 | -------------------------------------------------------------------------------- /packages/design-system/src/lib/provider.tsx: -------------------------------------------------------------------------------- 1 | import { StyledEngineProvider, ThemeProvider } from '@mui/material' 2 | 3 | import { theme } from './muiTheme' 4 | 5 | export const DesignSystemProvider = ({ children }: { children: React.ReactNode }) => { 6 | return ( 7 | 8 | {children} 9 | 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /packages/design-system/src/lib/tw.ts: -------------------------------------------------------------------------------- 1 | import { cx, CxOptions } from 'class-variance-authority' 2 | import { twMerge } from 'tailwind-merge' 3 | 4 | export const tw = (...inputs: CxOptions) => { 5 | return twMerge(cx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /packages/design-system/src/style.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | html, 7 | body, 8 | #root { 9 | @apply m-0 h-full w-full overflow-auto p-0; 10 | } 11 | 12 | body { 13 | @apply relative font-sans text-base text-grey-600 subpixel-antialiased; 14 | } 15 | 16 | a { 17 | @apply text-blue no-underline visited:text-purple hover:underline focus:rounded focus:ring; 18 | } 19 | 20 | b { 21 | @apply text-base font-medium -tracking-[0.01em]; 22 | } 23 | 24 | ul { 25 | @apply m-0 list-disc pl-6; 26 | } 27 | 28 | li { 29 | @apply m-0 list-none; 30 | } 31 | 32 | svg { 33 | @apply inline-block; 34 | } 35 | 36 | span.line-break-anywhere { 37 | @apply break-all; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/design-system/src/types/svg.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | declare module '*.svg' { 3 | import { FC, SVGProps } from 'react' 4 | const content: FC & { title?: string; 'data-test'?: string }> 5 | 6 | export default content 7 | } 8 | 9 | declare module '*.png' { 10 | const value: any 11 | 12 | export default value 13 | } 14 | 15 | declare module '*.jpg' { 16 | const value: any 17 | 18 | export default value 19 | } 20 | 21 | declare module '*.jpeg' { 22 | const value: any 23 | 24 | export default value 25 | } 26 | -------------------------------------------------------------------------------- /packages/design-system/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import sharedConfig from 'lago-configs/tailwind' 2 | import { Config } from 'prettier' 3 | 4 | const config: Pick = { 5 | content: ['src/**/*.{js,ts,jsx,tsx}', 'playground/**/*.{js,ts,jsx,tsx}'], 6 | presets: [sharedConfig], 7 | } 8 | 9 | export default config 10 | -------------------------------------------------------------------------------- /packages/design-system/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src", "../../materialUiTheme.d.ts"], 4 | "exclude": ["**/__tests__/**", "**/*.test.{ts,tsx}", "playground", "dist", "node_modules"], 5 | "compilerOptions": { 6 | "declaration": true, 7 | "emitDeclarationOnly": true, 8 | "noEmit": false, 9 | "outDir": "./dist/types", 10 | "rootDir": "./src", 11 | "skipLibCheck": true, 12 | "composite": false 13 | }, 14 | } -------------------------------------------------------------------------------- /packages/design-system/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "lago-configs/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "baseUrl": ".", 6 | "paths": { 7 | "@mui/styled-engine": ["../../node_modules/@mui/styled-engine-sc"], 8 | "~/*": ["./src/*"], 9 | } 10 | }, 11 | "exclude": ["node_modules", "dist"], 12 | "references": [ 13 | { "path": "./tsconfig.playground.json" } 14 | ] 15 | } -------------------------------------------------------------------------------- /packages/design-system/tsconfig.playground.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "noEmit": false, 6 | "emitDeclarationOnly": true, 7 | "rootDir": ".", 8 | "baseUrl": ".", 9 | "paths": { 10 | "@mui/styled-engine": ["../../node_modules/@mui/styled-engine-sc"], 11 | "~/*": ["./src/*"], 12 | } 13 | }, 14 | "include": ["playground/**/*", "src/**/*", "../../materialUiTheme.d.ts"] 15 | } -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/configs/' 3 | - 'packages/design-system/' 4 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: [ 4 | require('postcss-preset-env'), 5 | require('tailwindcss'), 6 | require('autoprefixer'), 7 | process.env.APP_ENV === 'production' ? require('cssnano') : null, 8 | ], 9 | } 10 | 11 | module.exports = config 12 | -------------------------------------------------------------------------------- /scripts/codemod.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: ["error", { allow: ["info"] }] */ 2 | import { globSync } from 'glob' 3 | import { run as jscodeshift } from 'jscodeshift/src/Runner' 4 | import path from 'node:path' 5 | 6 | const SRC_DIR = './src/' 7 | 8 | const transformPath = path.resolve('./scripts/transforms/skeleton-migrate-width-values.js') 9 | const paths = globSync(path.join(SRC_DIR, '**/*.@(tsx)'), { 10 | ignore: ['**/node_modules/**', '**/graphql.tsx', '**/dist/**'], 11 | }) 12 | const options = { 13 | // dry: true, // dry run (no changes are made to files) 14 | verbose: 1, 15 | parser: 'tsx', 16 | } 17 | 18 | async function main() { 19 | try { 20 | await jscodeshift(transformPath, paths, options) 21 | } catch (e) { 22 | console.info('\u001b[' + 31 + 'm' + 'Codemod transform failed' + '\u001b[0m', e) 23 | process.exit(1) 24 | } 25 | } 26 | 27 | main() 28 | -------------------------------------------------------------------------------- /src/components/CodeSnippet/index.ts: -------------------------------------------------------------------------------- 1 | export { CodeSnippet } from './CodeSnippet' 2 | -------------------------------------------------------------------------------- /src/components/ConditionalWrapper.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react' 2 | 3 | interface ConditionalWrapperProps { 4 | condition: boolean 5 | validWrapper: (children: ReactNode) => JSX.Element | null 6 | invalidWrapper: (children: ReactNode) => JSX.Element | null 7 | children: ReactNode 8 | } 9 | 10 | export const ConditionalWrapper = ({ 11 | condition, 12 | validWrapper, 13 | invalidWrapper, 14 | children, 15 | }: ConditionalWrapperProps) => { 16 | return condition ? validWrapper(children) : invalidWrapper(children) 17 | } 18 | -------------------------------------------------------------------------------- /src/components/SearchInput.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | 3 | import { UseDebouncedSearch } from '~/hooks/useDebouncedSearch' 4 | import { tw } from '~/styles/utils' 5 | 6 | import { Icon } from './designSystem' 7 | import { TextInput } from './form' 8 | 9 | interface SearchInputProps { 10 | className?: string 11 | onChange: ReturnType['debouncedSearch'] 12 | placeholder?: string 13 | } 14 | 15 | export const SearchInput = ({ className, onChange, placeholder }: SearchInputProps) => { 16 | const [localValue, setLocalValue] = useState('') 17 | 18 | return ( 19 | { 24 | onChange && onChange(value) 25 | setLocalValue(value) 26 | }} 27 | InputProps={{ 28 | startAdornment: , 29 | }} 30 | cleanable 31 | /> 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /src/components/addOns/types.ts: -------------------------------------------------------------------------------- 1 | import { CreateAddOnInput, TaxOnAddOnEditCreateFragment } from '~/generated/graphql' 2 | 3 | export interface AddOnFormInput extends Omit { 4 | // NOTE: this is used for display purpose but will be replaced by taxCodes[] on save 5 | taxes?: TaxOnAddOnEditCreateFragment[] 6 | } 7 | -------------------------------------------------------------------------------- /src/components/analytics/usage/types.ts: -------------------------------------------------------------------------------- 1 | export enum UsageBreakdownType { 2 | Units = 'units', 3 | Amount = 'amount', 4 | } 5 | -------------------------------------------------------------------------------- /src/components/customerPortal/common/CustomerPortalLoading.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from '~/components/designSystem/Skeleton' 2 | 3 | const CustomerPortalLoading = () => ( 4 |
5 | 6 | 7 |
8 | ) 9 | 10 | export default CustomerPortalLoading 11 | -------------------------------------------------------------------------------- /src/components/customerPortal/common/PageTitle.tsx: -------------------------------------------------------------------------------- 1 | import { Typography } from '~/components/designSystem' 2 | import { Button } from '~/components/designSystem/Button' 3 | 4 | const PageTitle = ({ title, goHome }: { title: string; goHome: () => void }) => { 5 | return ( 6 |
7 |
16 | ) 17 | } 18 | 19 | export default PageTitle 20 | -------------------------------------------------------------------------------- /src/components/customerPortal/common/SectionTitle.tsx: -------------------------------------------------------------------------------- 1 | import TextButton from '~/components/customerPortal/common/TextButton' 2 | import { Skeleton, Typography } from '~/components/designSystem' 3 | import { tw } from '~/styles/utils' 4 | 5 | type SectionTitleProps = { 6 | className?: string 7 | title: string 8 | action?: { title: string; onClick: () => void } 9 | loading?: boolean 10 | } 11 | 12 | const SectionTitle = ({ className, title, action, loading }: SectionTitleProps) => ( 13 |
14 | {loading ? ( 15 |
16 | 17 |
18 | ) : ( 19 | <> 20 | {title} 21 | 22 | {action && } 23 | 24 | )} 25 |
26 | ) 27 | 28 | export default SectionTitle 29 | -------------------------------------------------------------------------------- /src/components/customerPortal/common/TextButton.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '~/components/designSystem' 2 | import { tw } from '~/styles/utils' 3 | 4 | type TextButtonProps = { 5 | className?: string 6 | onClick: () => void 7 | content: string 8 | } 9 | 10 | const TextButton = ({ className, content, onClick }: TextButtonProps) => ( 11 | 21 | ) 22 | 23 | export default TextButton 24 | -------------------------------------------------------------------------------- /src/components/customerPortal/utils.ts: -------------------------------------------------------------------------------- 1 | import { DateTime } from 'luxon' 2 | 3 | import { getTimezoneConfig } from '~/core/timezone/utils' 4 | import { LocaleEnum } from '~/core/translations' 5 | import { TimezoneEnum } from '~/generated/graphql' 6 | 7 | type PlanRenewalDateProps = { 8 | currentBillingPeriodEndingAt: string 9 | applicableTimezone?: TimezoneEnum | null 10 | locale?: LocaleEnum 11 | } 12 | 13 | const formatAndAddDay = ( 14 | date: string, 15 | timezone: TimezoneEnum | null | undefined, 16 | locale?: LocaleEnum, 17 | ) => { 18 | return DateTime.fromISO(date, { 19 | zone: getTimezoneConfig(timezone).name, 20 | locale: locale, 21 | }) 22 | .plus({ days: 1 }) 23 | .toLocaleString(DateTime.DATE_MED) 24 | } 25 | 26 | export const planRenewalDate = ({ 27 | currentBillingPeriodEndingAt, 28 | applicableTimezone, 29 | locale, 30 | }: PlanRenewalDateProps) => 31 | formatAndAddDay(currentBillingPeriodEndingAt, applicableTimezone, locale) 32 | -------------------------------------------------------------------------------- /src/components/customers/utils.ts: -------------------------------------------------------------------------------- 1 | import { Customer, CustomerTypeEnum } from '~/generated/graphql' 2 | 3 | export const getInitials = (str: string) => 4 | str.split(' ').reduce((acc, n) => (acc = acc + n[0]), '') 5 | 6 | export const computeCustomerInitials = ( 7 | customer?: Pick | null, 8 | ) => { 9 | const { name = '', firstname = '', lastname = '' } = customer ?? {} 10 | 11 | if (name) { 12 | return getInitials(name) 13 | } 14 | 15 | if (firstname || lastname) { 16 | return getInitials(`${firstname} ${lastname}`.trim()) 17 | } 18 | 19 | return '-' 20 | } 21 | 22 | export const TRANSLATIONS_MAP_CUSTOMER_TYPE: Record = { 23 | [CustomerTypeEnum.Individual]: 'text_1726129457108txzr4gdkvcz', 24 | [CustomerTypeEnum.Company]: 'text_1726129457108raohiy4kkt3', 25 | } 26 | -------------------------------------------------------------------------------- /src/components/designSystem/Card.tsx: -------------------------------------------------------------------------------- 1 | import { FC, PropsWithChildren } from 'react' 2 | 3 | import { tw } from '~/styles/utils' 4 | 5 | export interface CardProps { 6 | className?: string 7 | } 8 | 9 | export const Card: FC> = ({ children, className }) => { 10 | return ( 11 |
17 | {children} 18 |
19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /src/components/designSystem/Filters/Filters.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '~/components/designSystem' 2 | import { useInternationalization } from '~/hooks/core/useInternationalization' 3 | import { tw } from '~/styles/utils' 4 | 5 | import { ActiveFiltersList } from './ActiveFiltersList' 6 | import { FiltersPanelPopper } from './FiltersPanelPopper' 7 | import { useFilters } from './useFilters' 8 | 9 | interface FiltersProps { 10 | className?: string 11 | } 12 | 13 | export const Filters = ({ className }: FiltersProps) => { 14 | const { translate } = useInternationalization() 15 | 16 | const { hasAppliedFilters, resetFilters } = useFilters() 17 | 18 | return ( 19 |
20 | 21 | 22 | 23 | {hasAppliedFilters && ( 24 | 27 | )} 28 |
29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /src/components/designSystem/Filters/QuickFilters.tsx: -------------------------------------------------------------------------------- 1 | import { CustomerAccountTypeQuickFilter } from '~/components/designSystem/Filters/CustomerAccountTypeQuickFilter' 2 | import { TimeGranularitySelector } from '~/components/designSystem/Filters/TimeGranularitySelector' 3 | 4 | import { InvoiceStatusQuickFilter } from './InvoiceStatusQuickFilter' 5 | import { AvailableQuickFilters } from './types' 6 | import { useFilters } from './useFilters' 7 | 8 | export const QuickFilters = () => { 9 | const { quickFiltersType } = useFilters() 10 | 11 | return ( 12 |
13 | {quickFiltersType === AvailableQuickFilters.invoiceStatus ? ( 14 | 15 | ) : null} 16 | 17 | {quickFiltersType === AvailableQuickFilters.customerAccountType ? ( 18 | 19 | ) : null} 20 | 21 | {quickFiltersType === AvailableQuickFilters.timeGranularity ? ( 22 | 23 | ) : null} 24 |
25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /src/components/designSystem/Filters/filtersElements/FiltersItemActivityIds.tsx: -------------------------------------------------------------------------------- 1 | import { TextInput } from '~/components/form' 2 | import { useInternationalization } from '~/hooks/core/useInternationalization' 3 | 4 | import { FiltersFormValues } from '../types' 5 | 6 | type FiltersItemActivityIdsProps = { 7 | value: FiltersFormValues['filters'][0]['value'] 8 | setFilterValue: (value: string) => void 9 | } 10 | 11 | export const FiltersItemActivityIds = ({ value, setFilterValue }: FiltersItemActivityIdsProps) => { 12 | const { translate } = useInternationalization() 13 | 14 | return ( 15 | setFilterValue(val)} 19 | /> 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /src/components/designSystem/Filters/filtersElements/FiltersItemCountry.tsx: -------------------------------------------------------------------------------- 1 | import { ComboBox } from '~/components/form' 2 | import { CountryCodes } from '~/core/constants/countryCodes' 3 | import { CountryCode } from '~/generated/graphql' 4 | import { useInternationalization } from '~/hooks/core/useInternationalization' 5 | 6 | import { FiltersFormValues } from '../types' 7 | 8 | type FiltersItemCountryProps = { 9 | value: FiltersFormValues['filters'][0]['value'] 10 | setFilterValue: (value: string) => void 11 | } 12 | 13 | export const FiltersItemCountry = ({ value, setFilterValue }: FiltersItemCountryProps) => { 14 | const { translate } = useInternationalization() 15 | 16 | return ( 17 | ({ 21 | label: CountryCodes[countryCode], 22 | value: countryCode, 23 | }))} 24 | onChange={(currency) => setFilterValue(currency)} 25 | value={value} 26 | /> 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /src/components/designSystem/Filters/filtersElements/FiltersItemCurrency.tsx: -------------------------------------------------------------------------------- 1 | import { ComboBox } from '~/components/form' 2 | import { CurrencyEnum } from '~/generated/graphql' 3 | import { useInternationalization } from '~/hooks/core/useInternationalization' 4 | 5 | import { FiltersFormValues } from '../types' 6 | 7 | type FiltersItemCurrencyProps = { 8 | value: FiltersFormValues['filters'][0]['value'] 9 | setFilterValue: (value: string) => void 10 | } 11 | 12 | export const FiltersItemCurrency = ({ value, setFilterValue }: FiltersItemCurrencyProps) => { 13 | const { translate } = useInternationalization() 14 | 15 | return ( 16 | ({ 20 | value: currency, 21 | }))} 22 | onChange={(currency) => setFilterValue(currency)} 23 | value={value} 24 | /> 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /src/components/designSystem/Filters/filtersElements/FiltersItemResourceIds.tsx: -------------------------------------------------------------------------------- 1 | import { TextInput } from '~/components/form' 2 | import { useInternationalization } from '~/hooks/core/useInternationalization' 3 | 4 | import { FiltersFormValues } from '../types' 5 | 6 | type FiltersItemResourceIdsProps = { 7 | value: FiltersFormValues['filters'][0]['value'] 8 | setFilterValue: (value: string) => void 9 | } 10 | 11 | export const FiltersItemResourceIds = ({ value, setFilterValue }: FiltersItemResourceIdsProps) => { 12 | const { translate } = useInternationalization() 13 | 14 | return ( 15 | setFilterValue(val)} 19 | /> 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /src/components/designSystem/Filters/filtersElements/FiltersItemSelfBilled.tsx: -------------------------------------------------------------------------------- 1 | import { ComboBox } from '~/components/form' 2 | import { useInternationalization } from '~/hooks/core/useInternationalization' 3 | 4 | import { FiltersFormValues } from '../types' 5 | 6 | type FiltersItemSelfBilled = { 7 | value: FiltersFormValues['filters'][0]['value'] 8 | setFilterValue: (value: string) => void 9 | } 10 | 11 | export const FiltersItemSelfBilled = ({ value, setFilterValue }: FiltersItemSelfBilled) => { 12 | const { translate } = useInternationalization() 13 | 14 | return ( 15 | setFilterValue(selfBilled)} 29 | value={value} 30 | /> 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /src/components/designSystem/Filters/filtersElements/FiltersItemSubscription.tsx: -------------------------------------------------------------------------------- 1 | import { TextInput } from '~/components/form' 2 | import { useInternationalization } from '~/hooks/core/useInternationalization' 3 | 4 | import { FiltersFormValues } from '../types' 5 | 6 | type FiltersItemSubscriptionProps = { 7 | value: FiltersFormValues['filters'][0]['value'] 8 | setFilterValue: (value: string) => void 9 | } 10 | 11 | export const FiltersItemSubscription = ({ 12 | value, 13 | setFilterValue, 14 | }: FiltersItemSubscriptionProps) => { 15 | const { translate } = useInternationalization() 16 | 17 | return ( 18 | setFilterValue(val)} 22 | /> 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /src/components/designSystem/Filters/filtersElements/FiltersItemUserEmails.tsx: -------------------------------------------------------------------------------- 1 | import { TextInput } from '~/components/form' 2 | import { useInternationalization } from '~/hooks/core/useInternationalization' 3 | 4 | import { FiltersFormValues } from '../types' 5 | 6 | type FiltersItemUserEmailsProps = { 7 | value: FiltersFormValues['filters'][0]['value'] 8 | setFilterValue: (value: string) => void 9 | } 10 | 11 | export const FiltersItemUserEmails = ({ value, setFilterValue }: FiltersItemUserEmailsProps) => { 12 | const { translate } = useInternationalization() 13 | 14 | return ( 15 | setFilterValue(val)} 19 | /> 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /src/components/designSystem/Filters/index.ts: -------------------------------------------------------------------------------- 1 | import { FiltersProvider } from './context' 2 | import { Filters as Component } from './Filters' 3 | import { QuickFilters } from './QuickFilters' 4 | 5 | export * from './types' 6 | export * from './utils' 7 | 8 | export const Filters = { 9 | Provider: FiltersProvider, 10 | Component: Component, 11 | QuickFilters: QuickFilters, 12 | } 13 | -------------------------------------------------------------------------------- /src/components/designSystem/Icon/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Icon' 2 | export * from './mapping' 3 | -------------------------------------------------------------------------------- /src/components/designSystem/Table/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ChargeTable' 2 | export * from './HorizontalDataTable' 3 | export * from './Table' 4 | -------------------------------------------------------------------------------- /src/components/designSystem/Toasts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ToastContainer' 2 | export * from './Toast' 3 | -------------------------------------------------------------------------------- /src/components/designSystem/graphs/const.tsx: -------------------------------------------------------------------------------- 1 | export const REVENUE_STREAMS_GRAPH_COLORS = { 2 | subscriptionFeeAmountCents: '#4C9AFF', 3 | usageBasedFeeAmountCents: '#FF9BE0', 4 | commitmentFeeAmountCents: '#79F2CA', 5 | oneOffFeeAmountCents: '#FFAB00', 6 | } 7 | -------------------------------------------------------------------------------- /src/components/designSystem/graphs/types.ts: -------------------------------------------------------------------------------- 1 | export type AreaChartDataType = { 2 | axisName: string 3 | value: number 4 | tooltipLabel: string 5 | } 6 | 7 | export type StackedBarChartDataType = { 8 | axisName: string 9 | value: number 10 | tooltipLabel: string 11 | } 12 | -------------------------------------------------------------------------------- /src/components/designSystem/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Accordion' 2 | export * from './Alert' 3 | export * from './Button' 4 | export * from './ButtonLink' 5 | export * from './Card' 6 | export * from './Chip' 7 | export * from './Dialog' 8 | export * from './Drawer' 9 | export * from './Icon' 10 | export * from './InfiniteScroll' 11 | export * from './NavigationTab' 12 | export * from './Popper' 13 | export * from './PreventClosingDrawerDialog' 14 | export * from './Selector' 15 | export * from './ShowMoreText' 16 | export * from './Skeleton' 17 | export * from './Status' 18 | export * from './Table' 19 | export * from './Toasts' 20 | export * from './Tooltip' 21 | export * from './Typography' 22 | export * from './VerticalMenu' 23 | -------------------------------------------------------------------------------- /src/components/developers/views.ts: -------------------------------------------------------------------------------- 1 | export { ActivityLogs } from '~/components/developers/activityLogs/ActivityLogs' 2 | export { ApiKeys } from '~/components/developers/apiKeys/ApiKeys' 3 | export { Events } from '~/components/developers/events/Events' 4 | export { WebhookLogs } from '~/components/developers/webhooks/WebhookLogs' 5 | export { Webhooks } from '~/components/developers/webhooks/Webhooks' 6 | -------------------------------------------------------------------------------- /src/components/form/AmountInput/index.ts: -------------------------------------------------------------------------------- 1 | export * from './AmountInput' 2 | export * from './AmountInputField' 3 | -------------------------------------------------------------------------------- /src/components/form/ButtonSelector/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './ButtonSelector' 2 | export * from './ButtonSelectorField' 3 | -------------------------------------------------------------------------------- /src/components/form/Checkbox/CheckboxField.tsx: -------------------------------------------------------------------------------- 1 | import { FormikProps } from 'formik' 2 | import _get from 'lodash/get' 3 | 4 | import { Checkbox, CheckboxProps } from './Checkbox' 5 | 6 | interface CheckboxFieldProps extends Omit { 7 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 8 | formikProps: FormikProps 9 | name: string 10 | onChange?: (value: boolean) => unknown 11 | } 12 | 13 | export const CheckboxField = ({ name, formikProps, onChange, ...props }: CheckboxFieldProps) => { 14 | const { values, errors, touched, setFieldValue } = formikProps 15 | 16 | return ( 17 | { 21 | setFieldValue(name, newValue) 22 | 23 | onChange && onChange(newValue) 24 | }} 25 | error={_get(touched, name) ? (_get(errors, name) as string) : undefined} 26 | {...props} 27 | /> 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /src/components/form/Checkbox/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CheckboxField' 2 | export * from './Checkbox' 3 | -------------------------------------------------------------------------------- /src/components/form/ComboBox/ComboBoxItem.tsx: -------------------------------------------------------------------------------- 1 | import { FC, PropsWithChildren } from 'react' 2 | 3 | import { tw } from '~/styles/utils' 4 | 5 | export const ComboboxItem: FC> = ({ 6 | children, 7 | className, 8 | virtualized, 9 | ...props 10 | }) => ( 11 |
18 | {children} 19 |
20 | ) 21 | -------------------------------------------------------------------------------- /src/components/form/ComboBox/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './ComboBox' 2 | export * from './ComboBoxField' 3 | export * from './ComboBoxItem' 4 | export * from './types' 5 | -------------------------------------------------------------------------------- /src/components/form/DatePicker/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DatePicker' 2 | export * from './DatePickerField' 3 | -------------------------------------------------------------------------------- /src/components/form/JsonEditor/index.ts: -------------------------------------------------------------------------------- 1 | export * from './JsonEditor' 2 | export * from './JsonEditorField' 3 | -------------------------------------------------------------------------------- /src/components/form/MultipleComboBox/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './MultipleComboBox' 2 | export * from './MultipleComboBoxField' 3 | export * from './types' 4 | -------------------------------------------------------------------------------- /src/components/form/Radio/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Radio' 2 | export * from './RadioField' 3 | export * from './RadioGroupField' 4 | -------------------------------------------------------------------------------- /src/components/form/Switch/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SwitchField' 2 | export * from './Switch' 3 | -------------------------------------------------------------------------------- /src/components/form/TextInput/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TextInputField' 2 | export * from './TextInput' 3 | -------------------------------------------------------------------------------- /src/components/form/index.ts: -------------------------------------------------------------------------------- 1 | export * from './AmountInput' 2 | export * from './ButtonSelector' 3 | export * from './Checkbox' 4 | export * from './ComboBox' 5 | export * from './DatePicker' 6 | export * from './JsonEditor' 7 | export * from './MultipleComboBox' 8 | export * from './Radio' 9 | export * from './Switch' 10 | export * from './TextInput' 11 | -------------------------------------------------------------------------------- /src/components/graphs/types.ts: -------------------------------------------------------------------------------- 1 | import { CurrencyEnum } from '~/generated/graphql' 2 | 3 | import { TPeriodScopeTranslationLookupValue } from './MonthSelectorDropdown' 4 | 5 | export type TGraphProps = { 6 | currency: CurrencyEnum | undefined 7 | period: TPeriodScopeTranslationLookupValue 8 | demoMode?: boolean 9 | className?: string 10 | blur?: boolean 11 | forceLoading?: boolean 12 | } 13 | -------------------------------------------------------------------------------- /src/components/invoices/details/InvoiceDetailsTablePeriodLine.tsx: -------------------------------------------------------------------------------- 1 | import { Typography } from '~/components/designSystem' 2 | 3 | type InvoiceDetailsTablePeriodLineProps = { 4 | canHaveUnitPrice: boolean 5 | isDraftInvoice: boolean 6 | period: string 7 | } 8 | 9 | export const InvoiceDetailsTablePeriodLine = ({ 10 | canHaveUnitPrice, 11 | isDraftInvoice, 12 | period, 13 | }: InvoiceDetailsTablePeriodLineProps) => { 14 | return ( 15 | 16 | 17 | 18 | {period} 19 | 20 | 21 | 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/components/invoices/types.ts: -------------------------------------------------------------------------------- 1 | import { CreateInvoiceInput, FeeInput, TaxInfosForCreateInvoiceFragment } from '~/generated/graphql' 2 | 3 | export type LocalFeeInput = FeeInput & { 4 | // NOTE: this is used for display purpose but will be replaced by taxCodes[] on save 5 | taxes?: TaxInfosForCreateInvoiceFragment[] | null 6 | } 7 | 8 | export interface InvoiceFormInput extends Omit { 9 | fees: LocalFeeInput[] 10 | } 11 | -------------------------------------------------------------------------------- /src/components/layouts/Charts.tsx: -------------------------------------------------------------------------------- 1 | import { PropsWithChildren } from 'react' 2 | 3 | import { tw } from '~/styles/utils' 4 | 5 | export const ChartWrapper = ({ 6 | children, 7 | className, 8 | blur, 9 | }: PropsWithChildren & { className?: string; blur?: boolean }) => ( 10 |
15 | {children} 16 |
17 | ) 18 | -------------------------------------------------------------------------------- /src/components/layouts/FullscreenPage.tsx: -------------------------------------------------------------------------------- 1 | import { FC, PropsWithChildren } from 'react' 2 | 3 | const Wrapper: FC = ({ children }) => { 4 | return
{children}
5 | } 6 | 7 | export const FullscreenPage = { 8 | Wrapper, 9 | } 10 | -------------------------------------------------------------------------------- /src/components/plans/__tests__/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { transformFilterObjectToString } from '~/components/plans/utils' 2 | import { ALL_FILTER_VALUES } from '~/core/constants/form' 3 | 4 | describe('utils', () => { 5 | describe('transformFilterObjectToString', () => { 6 | it('should return a string with the filter object keys and no values', () => { 7 | const filter = { 8 | key: 'key', 9 | } 10 | const result = transformFilterObjectToString(filter.key) 11 | 12 | expect(result).toBe(`{ "${filter.key}": "${ALL_FILTER_VALUES}" }`) 13 | }) 14 | 15 | it('should return a string with the filter object keys and value', () => { 16 | const filter = { 17 | key: 'key', 18 | value: 'value', 19 | } 20 | const result = transformFilterObjectToString(filter.key, filter.value) 21 | 22 | expect(result).toBe(`{ "${filter.key}": "${filter.value}" }`) 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /src/components/plans/utils.ts: -------------------------------------------------------------------------------- 1 | import { ALL_FILTER_VALUES } from '~/core/constants/form' 2 | 3 | export const transformFilterObjectToString = (key: string, value?: string): string => { 4 | return `{ "${[key]}": "${value || ALL_FILTER_VALUES}" }` 5 | } 6 | -------------------------------------------------------------------------------- /src/components/settings/integrations/IntegrationItemHeader.tsx: -------------------------------------------------------------------------------- 1 | import { Typography } from '~/components/designSystem' 2 | import { useInternationalization } from '~/hooks/core/useInternationalization' 3 | 4 | type TIntegrationItemHeaderProps = { 5 | columnName: string 6 | } 7 | 8 | const IntegrationItemHeader = ({ columnName }: TIntegrationItemHeaderProps) => { 9 | const { translate } = useInternationalization() 10 | 11 | return ( 12 |
13 | 14 | {columnName} 15 | 16 | 17 | {translate('text_6630e3210c13c500cd398e97')} 18 | 19 |
20 | ) 21 | } 22 | 23 | export default IntegrationItemHeader 24 | -------------------------------------------------------------------------------- /src/components/taxes/types.ts: -------------------------------------------------------------------------------- 1 | import { TaxCreateInput, TaxUpdateInput } from '~/generated/graphql' 2 | 3 | export type TaxFormInput = TaxCreateInput | Omit 4 | -------------------------------------------------------------------------------- /src/core/apolloClient/index.ts: -------------------------------------------------------------------------------- 1 | export * from './init' 2 | export * from './cache' 3 | export * from './cacheUtils' 4 | export * from './errorUtils' 5 | export * from './reactiveVars' 6 | -------------------------------------------------------------------------------- /src/core/apolloClient/reactiveVars/authTokenVar.ts: -------------------------------------------------------------------------------- 1 | import { makeVar } from '@apollo/client' 2 | 3 | import { getItemFromLS, setItemFromLS } from '~/core/apolloClient/cacheUtils' 4 | 5 | export const AUTH_TOKEN_LS_KEY = 'authToken' 6 | 7 | /** ----------------- VAR ----------------- */ 8 | export const authTokenVar = makeVar(getItemFromLS(AUTH_TOKEN_LS_KEY)) 9 | 10 | export const updateAuthTokenVar = (token?: string) => { 11 | setItemFromLS(AUTH_TOKEN_LS_KEY, token) 12 | authTokenVar(token) 13 | } 14 | -------------------------------------------------------------------------------- /src/core/apolloClient/reactiveVars/customerPortalTokenVar.ts: -------------------------------------------------------------------------------- 1 | import { makeVar } from '@apollo/client' 2 | 3 | import { getItemFromLS, setItemFromLS } from '~/core/apolloClient/cacheUtils' 4 | 5 | export const CUSTOMER_PORTAL_TOKEN_LS_KEY = 'customerPortalToken' 6 | 7 | /** ----------------- VAR ----------------- */ 8 | export const customerPortalTokenVar = makeVar(getItemFromLS(CUSTOMER_PORTAL_TOKEN_LS_KEY)) 9 | 10 | export const updateCustomerPortalTokenVar = (token?: string) => { 11 | setItemFromLS(CUSTOMER_PORTAL_TOKEN_LS_KEY, token) 12 | customerPortalTokenVar(token) 13 | } 14 | -------------------------------------------------------------------------------- /src/core/apolloClient/reactiveVars/index.ts: -------------------------------------------------------------------------------- 1 | export * from './authTokenVar' 2 | export * from './customerPortalTokenVar' 3 | export * from './envGlobalVar' 4 | export * from './internationalizationVar' 5 | export * from './locationHistoryVar' 6 | export * from './duplicatePlanVar' 7 | export * from './toastVar' 8 | -------------------------------------------------------------------------------- /src/core/constants/globalTypes.ts: -------------------------------------------------------------------------------- 1 | export enum AppEnvEnum { 2 | production = 'production', 3 | staging = 'staging', 4 | development = 'development', 5 | qa = 'qa', 6 | } 7 | -------------------------------------------------------------------------------- /src/core/constants/localStorageKeys.ts: -------------------------------------------------------------------------------- 1 | export const LAST_PRIVATE_VISITED_ROUTE_WHILE_NOT_CONNECTED_LS_KEY = 'lastPrivateVisitedRoute' 2 | export const ORGANIZATION_LS_KEY_ID = 'currentOrganization' 3 | -------------------------------------------------------------------------------- /src/core/constants/paymentTerm.ts: -------------------------------------------------------------------------------- 1 | export enum NetPaymentTermValuesEnum { 2 | zero = '0', 3 | thirty = '30', 4 | sixty = '60', 5 | ninety = '90', 6 | custom = 'custom', 7 | } 8 | -------------------------------------------------------------------------------- /src/core/constants/statusCouponMapping.ts: -------------------------------------------------------------------------------- 1 | import { StatusProps, StatusType } from '~/components/designSystem' 2 | import { CouponStatusEnum } from '~/generated/graphql' 3 | 4 | export const couponStatusMapping = (type?: CouponStatusEnum | undefined): StatusProps => { 5 | switch (type) { 6 | case CouponStatusEnum.Active: 7 | return { 8 | type: StatusType.success, 9 | label: 'active', 10 | } 11 | default: 12 | return { 13 | type: StatusType.danger, 14 | label: 'terminated', 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/core/constants/statusSubscriptionMapping.ts: -------------------------------------------------------------------------------- 1 | import { StatusProps, StatusType } from '~/components/designSystem' 2 | import { StatusTypeEnum } from '~/generated/graphql' 3 | 4 | export const subscriptionStatusMapping = (status?: StatusTypeEnum | null): StatusProps => { 5 | switch (status) { 6 | case StatusTypeEnum.Active: 7 | return { 8 | type: StatusType.success, 9 | label: 'active', 10 | } 11 | case StatusTypeEnum.Pending: 12 | return { 13 | type: StatusType.default, 14 | label: 'pending', 15 | } 16 | case StatusTypeEnum.Canceled: 17 | return { 18 | type: StatusType.disabled, 19 | label: 'canceled', 20 | } 21 | case StatusTypeEnum.Terminated: 22 | return { 23 | type: StatusType.danger, 24 | label: 'terminated', 25 | } 26 | default: 27 | return { 28 | type: StatusType.default, 29 | label: 'pending', 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/core/constants/statusWebhookMapping.ts: -------------------------------------------------------------------------------- 1 | import { StatusProps, StatusType } from '~/components/designSystem' 2 | import { WebhookStatusEnum } from '~/generated/graphql' 3 | 4 | export const statusWebhookMapping = (status?: WebhookStatusEnum | null): StatusProps => { 5 | switch (status) { 6 | case WebhookStatusEnum.Pending: 7 | return { 8 | type: StatusType.default, 9 | label: 'pending', 10 | } 11 | case WebhookStatusEnum.Failed: 12 | return { 13 | type: StatusType.danger, 14 | label: 'failed', 15 | } 16 | case WebhookStatusEnum.Succeeded: 17 | return { 18 | type: StatusType.success, 19 | label: 'delivered', 20 | } 21 | default: 22 | return { 23 | type: StatusType.default, 24 | label: 'pending', 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/core/formats/__tests__/countryDataForCombobox.test.ts: -------------------------------------------------------------------------------- 1 | import { countryDataForCombobox } from '~/core/formats/countryDataForCombobox' 2 | 3 | describe('Core > format', () => { 4 | describe('countryDataForCombobox()', () => { 5 | it('should retirn a list of element to be displayed in a combobox', () => { 6 | expect(countryDataForCombobox[0]).toStrictEqual({ label: 'Andorra', value: 'AD' }) 7 | }) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /src/core/formats/__tests__/obfuscate.test.ts: -------------------------------------------------------------------------------- 1 | import { obfuscateValue } from '~/core/formats/obfuscate' 2 | 3 | describe('obfuscate', () => { 4 | it('should obfuscate a given string', () => { 5 | expect(obfuscateValue('I')).toBe('••••••••I') 6 | expect(obfuscateValue('hello')).toBe('••••••••llo') 7 | expect(obfuscateValue('11199999-9999-4522-9acd-8659999d9ae8')).toBe('••••••••ae8') 8 | expect(obfuscateValue('11199999-9999-4522-9acd-8659999d9ae8', { prefixLength: 3 })).toBe( 9 | '•••ae8', 10 | ) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /src/core/formats/countryDataForCombobox.ts: -------------------------------------------------------------------------------- 1 | import { BasicComboBoxData } from '~/components/form' 2 | import { CountryCodes } from '~/core/constants/countryCodes' 3 | 4 | export const countryDataForCombobox: BasicComboBoxData[] = ( 5 | Object.keys(CountryCodes) as Array 6 | ).map((countryKey) => { 7 | return { 8 | value: countryKey, 9 | label: CountryCodes[countryKey], 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /src/core/formats/formatCreditNotesItems.ts: -------------------------------------------------------------------------------- 1 | import _ from 'lodash' 2 | 3 | import { CreditNoteItem } from '~/generated/graphql' 4 | 5 | const formatCreditNotesItems = (items: CreditNoteItem[] | null | undefined) => { 6 | return Object.values( 7 | _.chain(items) 8 | .groupBy((item) => item?.fee?.subscription?.id) 9 | .map((item) => Object.values(_.groupBy(item, (element) => element?.fee?.charge?.id))) 10 | .value(), 11 | ) 12 | } 13 | 14 | export default formatCreditNotesItems 15 | -------------------------------------------------------------------------------- /src/core/formats/obfuscate.ts: -------------------------------------------------------------------------------- 1 | export const obfuscateValue = ( 2 | value: string, 3 | options: { prefixLength: number } = { 4 | prefixLength: 8, 5 | }, 6 | ) => `${'•'.repeat(options.prefixLength)}${value.slice(-3)}` 7 | -------------------------------------------------------------------------------- /src/core/router/types.ts: -------------------------------------------------------------------------------- 1 | import type { RouteObject } from 'react-router-dom' 2 | 3 | import { TMembershipPermissions } from '~/hooks/usePermissions' 4 | 5 | export interface CustomRouteObject extends Omit { 6 | path?: string | string[] 7 | private?: boolean 8 | onlyPublic?: boolean 9 | invitation?: boolean 10 | redirect?: string 11 | children?: CustomRouteObject[] 12 | permissions?: Array 13 | } 14 | -------------------------------------------------------------------------------- /src/core/router/utils.tsx: -------------------------------------------------------------------------------- 1 | import { ComponentType, lazy } from 'react' 2 | 3 | const retry = ( 4 | fn: Function, 5 | retriesLeft: number = 2, 6 | interval: number = 1000, 7 | ): Promise<{ default: ComponentType }> => { 8 | return new Promise((resolve) => { 9 | fn() 10 | .then(resolve) 11 | .catch(() => { 12 | setTimeout(() => { 13 | if (retriesLeft === 0) { 14 | return window.location.reload() // refresh the page as last resort 15 | } 16 | 17 | retry(fn, retriesLeft - 1, interval) 18 | }, interval) 19 | }) 20 | }) 21 | } 22 | 23 | export const lazyLoad = (fn: Function) => lazy(() => retry(fn)) 24 | -------------------------------------------------------------------------------- /src/core/serializers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './serializePlanInput' 2 | export * from './serializeCreditNoteInput' 3 | -------------------------------------------------------------------------------- /src/core/timezone/__tests__/config.test.ts: -------------------------------------------------------------------------------- 1 | import { TimeZonesConfig } from '~/core/timezone/config' 2 | 3 | describe('Timezone fongis', () => { 4 | describe('TimeZonesConfig', () => { 5 | it('returns expected config values', () => { 6 | expect(TimeZonesConfig['TZ_ASIA_TOKYO']).toStrictEqual({ 7 | name: 'Asia/Tokyo', 8 | offset: '+9:00', 9 | offsetInMinute: 540, 10 | }) 11 | expect(TimeZonesConfig['TZ_AMERICA_ARGENTINA_BUENOS_AIRES']).toStrictEqual({ 12 | name: 'America/Argentina/Buenos_Aires', 13 | offset: '-3:00', 14 | offsetInMinute: -180, 15 | }) 16 | expect(TimeZonesConfig['TZ_UTC']).toStrictEqual({ 17 | name: 'UTC', 18 | offset: '±0:00', 19 | offsetInMinute: 0, 20 | }) 21 | }) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /src/core/timezone/index.ts: -------------------------------------------------------------------------------- 1 | export * from './config' 2 | export * from './utils' 3 | -------------------------------------------------------------------------------- /src/core/translations/documentLocales.ts: -------------------------------------------------------------------------------- 1 | type documentLocalesType = { 2 | [key: string]: string 3 | } 4 | 5 | export const DocumentLocales: documentLocalesType = { 6 | fr: 'French', 7 | en: 'English', 8 | de: 'German', 9 | nb: 'Norwegian (Bokmål)', 10 | it: 'Italian', 11 | es: 'Spanish', 12 | sv: 'Swedish', 13 | } 14 | -------------------------------------------------------------------------------- /src/core/translations/index.ts: -------------------------------------------------------------------------------- 1 | export * from './utils' 2 | export * from './documentLocales' 3 | export * from './types' 4 | -------------------------------------------------------------------------------- /src/core/translations/types.ts: -------------------------------------------------------------------------------- 1 | export type TranslateData = Record 2 | 3 | export type Translations = Record | undefined 4 | 5 | export interface Translation { 6 | [key: string]: string 7 | } 8 | 9 | export enum LocaleEnum { 10 | en = 'en', 11 | fr = 'fr', // French 12 | nb = 'nb', // Norwegian 13 | de = 'de', // German 14 | it = 'it', // Italian 15 | es = 'es', // Spanish 16 | sv = 'sv', // Swedish 17 | } 18 | export type Locale = keyof typeof LocaleEnum 19 | -------------------------------------------------------------------------------- /src/core/utils/__tests__/responsiveProps.test.ts: -------------------------------------------------------------------------------- 1 | import { setResponsiveProperty } from '~/core/utils/responsiveProps' 2 | 3 | describe('setResponsiveProperty', () => { 4 | it('should return a responsive style object', () => { 5 | expect(setResponsiveProperty('margin', 0)).toEqual({ 6 | margin: '0px', 7 | }) 8 | 9 | expect(setResponsiveProperty('margin', { default: 4 })).toEqual({ 10 | margin: '4px', 11 | }) 12 | 13 | expect( 14 | setResponsiveProperty('margin', { 15 | default: 4, 16 | md: 48, 17 | }), 18 | ).toEqual({ 19 | margin: '4px', 20 | '@media (min-width:776px)': { 21 | margin: '48px', 22 | }, 23 | }) 24 | 25 | expect( 26 | setResponsiveProperty('margin', { 27 | default: 20, 28 | md: 30, 29 | lg: 10, 30 | }), 31 | ).toEqual({ 32 | margin: '20px', 33 | '@media (min-width:776px)': { 34 | margin: '30px', 35 | }, 36 | '@media (min-width:1024px)': { 37 | margin: '10px', 38 | }, 39 | }) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /src/core/utils/billingEntityNumberPreview.ts: -------------------------------------------------------------------------------- 1 | import { DateTime } from 'luxon' 2 | 3 | import { BillingEntityDocumentNumberingEnum } from '~/generated/graphql' 4 | 5 | export const getBillingEntityNumberPreview = ( 6 | documentNumbering: BillingEntityDocumentNumberingEnum, 7 | documentNumberPrefix: string, 8 | ) => { 9 | const date = DateTime.now().toFormat('yyyyMM') 10 | 11 | const numberEnding = { 12 | [BillingEntityDocumentNumberingEnum.PerCustomer]: '001-001', 13 | [BillingEntityDocumentNumberingEnum.PerBillingEntity]: `${date}-001`, 14 | } 15 | 16 | return `${documentNumberPrefix}-${numberEnding[documentNumbering]}` 17 | } 18 | -------------------------------------------------------------------------------- /src/core/utils/dateUtils.ts: -------------------------------------------------------------------------------- 1 | import { DateTime } from 'luxon' 2 | 3 | /** 4 | * Converts a date string to the end of day in ISO format 5 | * @param dateString - The input date string in ISO format 6 | * @returns The date string converted to end of day in ISO format, or empty string if input is falsy 7 | */ 8 | export const endOfDayIso = (dateString: string | null | undefined): string => { 9 | if (!dateString) return '' 10 | 11 | return DateTime.fromISO(dateString).endOf('day').toISO() || '' 12 | } 13 | -------------------------------------------------------------------------------- /src/core/utils/downloadFiles.ts: -------------------------------------------------------------------------------- 1 | import { addToast } from '~/core/apolloClient' 2 | 3 | export const handleDownloadFile = (fileUrl?: string | null) => { 4 | const showError = () => { 5 | addToast({ 6 | severity: 'danger', 7 | translateKey: 'text_62b31e1f6a5b8b1b745ece48', 8 | }) 9 | } 10 | 11 | if (!fileUrl) return showError() 12 | 13 | // We open a window, add url then focus on different lines, in order to prevent browsers to block page opening 14 | // It could be seen as unexpected popup as not immediatly done on user action 15 | // https://stackoverflow.com/questions/2587677/avoid-browser-popup-blockers 16 | // Also, we need to use setTimeout to avoid Safari blocking the popup 17 | setTimeout(() => { 18 | const myWindow = window.open('', '_blank') 19 | 20 | if (myWindow?.location?.href) { 21 | myWindow.location.href = fileUrl 22 | return myWindow?.focus() 23 | } 24 | 25 | myWindow?.close() 26 | showError() 27 | }, 0) 28 | } 29 | -------------------------------------------------------------------------------- /src/core/utils/urlUtils.ts: -------------------------------------------------------------------------------- 1 | export const addValuesToUrlState = ({ 2 | url, 3 | values, 4 | stateType, 5 | }: { 6 | url: string 7 | values: Record 8 | stateType: 'string' | 'object' 9 | }) => { 10 | const urlObj = new URL(url) 11 | const urlSearchParams = urlObj.searchParams 12 | 13 | const oldState = urlSearchParams.get('state') || ('{}' as string) 14 | 15 | let state = {} 16 | 17 | if (stateType === 'string') { 18 | state = { state: oldState, ...values } 19 | } else if (stateType === 'object') { 20 | const parsedState = JSON.parse(oldState) 21 | 22 | state = { ...parsedState, ...values } 23 | } 24 | 25 | urlSearchParams.set('state', decodeURI(JSON.stringify(state))) 26 | urlObj.search = urlSearchParams.toString() 27 | 28 | return urlObj.toString() 29 | } 30 | -------------------------------------------------------------------------------- /src/formValidation/schema.d.ts: -------------------------------------------------------------------------------- 1 | import { AnyObject, Flags, Maybe, Schema } from 'yup' 2 | 3 | declare module 'yup' { 4 | interface StringSchema< 5 | TType extends Maybe = string | undefined, 6 | TContext extends AnyObject = AnyObject, 7 | TDefault = undefined, 8 | TFlags extends Flags = '', 9 | > extends Schema { 10 | domain(message: string): this 11 | } 12 | } 13 | 14 | export {} 15 | -------------------------------------------------------------------------------- /src/hooks/auth/useIsAuthenticated.ts: -------------------------------------------------------------------------------- 1 | import { useReactiveVar } from '@apollo/client' 2 | 3 | import { authTokenVar, customerPortalTokenVar } from '~/core/apolloClient' 4 | 5 | type useIsAuthenticatedReturn = () => { 6 | isAuthenticated: boolean 7 | isPortalAuthenticated: boolean 8 | token?: string 9 | } 10 | 11 | export const useIsAuthenticated: useIsAuthenticatedReturn = () => { 12 | const token = useReactiveVar(authTokenVar) 13 | const portalToken = useReactiveVar(customerPortalTokenVar) 14 | 15 | return { 16 | isAuthenticated: !!token, 17 | isPortalAuthenticated: !!portalToken, 18 | token, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/hooks/core/useInternationalization.ts: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react' 2 | 3 | import { updateIntlLocale, useInternationalizationVar } from '~/core/apolloClient' 4 | import { envGlobalVar } from '~/core/apolloClient' 5 | import { Locale, TranslateData, translateKey } from '~/core/translations' 6 | 7 | const { appEnv } = envGlobalVar() 8 | 9 | export type TranslateFunc = (key: string, data?: TranslateData, plural?: number) => string 10 | 11 | type UseInternationalization = () => { 12 | locale: Locale 13 | translate: TranslateFunc 14 | updateLocale: (locale: Locale) => void 15 | } 16 | 17 | export const useInternationalization: UseInternationalization = () => { 18 | const { translations, locale } = useInternationalizationVar() 19 | 20 | return { 21 | locale, 22 | translate: useCallback( 23 | (key, data, plural = 0) => { 24 | return translateKey({ translations, locale, appEnv }, key, data, plural) 25 | }, 26 | [translations, locale], 27 | ), 28 | updateLocale: updateIntlLocale, 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/hooks/useSalesForceConfig.ts: -------------------------------------------------------------------------------- 1 | import { useSearchParams } from 'react-router-dom' 2 | 3 | type TEventData = { 4 | action: string 5 | rel: string 6 | [key: string]: string 7 | } 8 | 9 | type TUseSalesForceConfigReturn = { 10 | isRunningInSalesForceIframe: boolean 11 | emitSalesForceEvent: (data: TEventData) => void 12 | } 13 | 14 | export const useSalesForceConfig = (): TUseSalesForceConfigReturn => { 15 | const [searchParams] = useSearchParams() 16 | 17 | const isRunningInSalesForceIframe = !!searchParams.get('sfdc') 18 | 19 | const emitSalesForceEvent = (data: TEventData) => { 20 | window.parent.postMessage(JSON.stringify(data), '*') 21 | } 22 | 23 | return { isRunningInSalesForceIframe, emitSalesForceEvent } 24 | } 25 | -------------------------------------------------------------------------------- /src/main.css: -------------------------------------------------------------------------------- 1 | /* Import the design system styles */ 2 | @import 'lago-design-system/style'; 3 | 4 | /* Useful for components that are styled with tailwind in the app */ 5 | @tailwind base; 6 | @tailwind components; 7 | @tailwind utilities; 8 | 9 | a { 10 | @apply text-blue no-underline visited:text-purple hover:underline focus:rounded focus:ring; 11 | } 12 | -------------------------------------------------------------------------------- /src/pages/CustomerRequestOverduePayment/validateEmails.ts: -------------------------------------------------------------------------------- 1 | import { string } from 'yup' 2 | 3 | export const validateEmails = (emails: string): boolean => { 4 | const emailArray = emails.split(',').map((email) => email.trim()) 5 | 6 | for (const email of emailArray) { 7 | if (!string().email().isValidSync(email)) { 8 | return false 9 | } 10 | } 11 | return true 12 | } 13 | 14 | export const serializeEmails = (emails: string): string => { 15 | return emails.replaceAll(' ', '') 16 | } 17 | -------------------------------------------------------------------------------- /src/pages/Error404.tsx: -------------------------------------------------------------------------------- 1 | import { GenericPlaceholder } from '~/components/GenericPlaceholder' 2 | import { HOME_ROUTE } from '~/core/router' 3 | import { useInternationalization } from '~/hooks/core/useInternationalization' 4 | import { useLocationHistory } from '~/hooks/core/useLocationHistory' 5 | import ErrorImage from '~/public/images/maneki/error.svg' 6 | 7 | const Error404 = () => { 8 | const { translate } = useInternationalization() 9 | const { goBack } = useLocationHistory() 10 | 11 | return ( 12 |
13 | } 15 | title={translate('text_62bac37900192b773560e82d')} 16 | subtitle={translate('text_62bac37900192b773560e82f')} 17 | buttonTitle={translate('text_62bac37900192b773560e831')} 18 | buttonAction={() => goBack(HOME_ROUTE, { previousCount: -2 })} 19 | /> 20 |
21 | ) 22 | } 23 | 24 | export default Error404 25 | -------------------------------------------------------------------------------- /src/pages/Forbidden.tsx: -------------------------------------------------------------------------------- 1 | import { GenericPlaceholder } from '~/components/GenericPlaceholder' 2 | import { HOME_ROUTE } from '~/core/router' 3 | import { useInternationalization } from '~/hooks/core/useInternationalization' 4 | import { useLocationHistory } from '~/hooks/core/useLocationHistory' 5 | import ErrorImage from '~/public/images/maneki/error.svg' 6 | 7 | const Forbidden = () => { 8 | const { translate } = useInternationalization() 9 | const { goBack } = useLocationHistory() 10 | 11 | return ( 12 |
13 | } 15 | title={translate('text_66474faf77c70900619567c7')} 16 | subtitle={translate('text_66474fb55f1b6901c7ac7683')} 17 | buttonTitle={translate('text_62bac37900192b773560e831')} 18 | buttonAction={() => goBack(HOME_ROUTE, { previousCount: -2 })} 19 | /> 20 |
21 | ) 22 | } 23 | 24 | export default Forbidden 25 | -------------------------------------------------------------------------------- /src/pages/InvitationInit.tsx: -------------------------------------------------------------------------------- 1 | import { useApolloClient } from '@apollo/client' 2 | import { useEffect } from 'react' 3 | import { generatePath, Outlet, useNavigate, useParams } from 'react-router-dom' 4 | 5 | import { logOut } from '~/core/apolloClient' 6 | import { INVITATION_ROUTE_FORM } from '~/core/router' 7 | import { useIsAuthenticated } from '~/hooks/auth/useIsAuthenticated' 8 | 9 | const InvitationInit = () => { 10 | const { token } = useParams() 11 | const { isAuthenticated } = useIsAuthenticated() 12 | const navigate = useNavigate() 13 | const client = useApolloClient() 14 | 15 | useEffect(() => { 16 | const triggerLogout = async () => { 17 | await logOut(client, true) 18 | } 19 | 20 | triggerLogout() 21 | 22 | // We first logout the user and then redirect to the invitation form 23 | !isAuthenticated && navigate(generatePath(INVITATION_ROUTE_FORM, { token: token as string })) 24 | // eslint-disable-next-line react-hooks/exhaustive-deps 25 | }, [isAuthenticated]) 26 | 27 | return 28 | } 29 | 30 | export default InvitationInit 31 | -------------------------------------------------------------------------------- /src/pages/analytics/Usage.tsx: -------------------------------------------------------------------------------- 1 | import { useRef } from 'react' 2 | 3 | import UsageBreakdownSection from '~/components/analytics/usage/UsageBreakdownSection' 4 | import UsageOverviewSection from '~/components/analytics/usage/UsageOverviewSection' 5 | import { FullscreenPage } from '~/components/layouts/FullscreenPage' 6 | import { PremiumWarningDialog, PremiumWarningDialogRef } from '~/components/PremiumWarningDialog' 7 | 8 | const Usage = () => { 9 | const premiumWarningDialogRef = useRef(null) 10 | 11 | return ( 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ) 20 | } 21 | 22 | export default Usage 23 | -------------------------------------------------------------------------------- /src/pages/wallet/index.ts: -------------------------------------------------------------------------------- 1 | export { default as CreateWallet } from './CreateWallet' 2 | export { type TWalletDataForm } from './types' 3 | -------------------------------------------------------------------------------- /src/pages/wallet/types.ts: -------------------------------------------------------------------------------- 1 | import { CreateCustomerWalletInput, UpdateCustomerWalletInput } from '~/generated/graphql' 2 | 3 | export type TWalletDataForm = Omit & 4 | Omit 5 | -------------------------------------------------------------------------------- /src/public/icons/alphabet.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/apps.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/arrow-bottom.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/public/icons/arrow-down-circle-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/arrow-indent.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/public/icons/arrow-left-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/public/icons/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/public/icons/arrow-top.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/public/icons/arrow-up-circle-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/bell.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/bulb.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/burger.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/public/icons/chart-bar.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/public/icons/checkmark.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/chevron-down-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /src/public/icons/chevron-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/public/icons/chevron-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/public/icons/chevron-right-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /src/public/icons/chevron-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/public/icons/chevron-top.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/public/icons/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/close-circle-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/public/icons/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/public/icons/coin-dollar.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/public/icons/collect.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/company.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/condition.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /src/public/icons/content-left-align.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/document.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/duplicate.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/public/icons/error-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/public/icons/error-unfilled.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/filter.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/flash-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/flash.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/handle.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/public/icons/id.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/public/icons/info-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/key.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/keyboard-enter.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/laptop.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /src/public/icons/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/micro.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/minus.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/public/icons/partially-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/public/icons/pause-circle-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/pen.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/public/icons/processing.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/public/icons/pulse.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/public/icons/push.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/receipt.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/public/icons/schema.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/share.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/sparkles.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /src/public/icons/stop.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/public/icons/switch-off.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/switch.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/table-horizontale.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/table.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/target.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/text.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/trash.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/public/icons/validate-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/validate-unfilled.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/icons/wallet.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/images/adyen.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /src/public/images/airbyte.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /src/public/images/anrok.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /src/public/images/avalara.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/images/cashfree.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /src/public/images/gocardless-large.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/images/gocardless.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /src/public/images/hightouch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/public/images/moneyhash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/public/images/stripe.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /src/styles/customer.tsx: -------------------------------------------------------------------------------- 1 | import { FC, PropsWithChildren } from 'react' 2 | 3 | import { Typography, TypographyProps } from '~/components/designSystem' 4 | 5 | import { tw } from './utils' 6 | 7 | export const SectionHeader: FC< 8 | PropsWithChildren<{ hideBottomShadow?: boolean } & TypographyProps> 9 | > = ({ children, hideBottomShadow, className, ...props }) => ( 10 | 20 | {children} 21 | 22 | ) 23 | -------------------------------------------------------------------------------- /src/styles/designSystem/DrawerContent.tsx: -------------------------------------------------------------------------------- 1 | import { FC, PropsWithChildren } from 'react' 2 | 3 | import { tw } from '../utils' 4 | 5 | export const DrawerContent: FC> = ({ 6 | children, 7 | className, 8 | }) =>
{children}
9 | 10 | export const DrawerTitle: FC = ({ children }) => ( 11 |
{children}
12 | ) 13 | 14 | export const DrawerSubmitButton: FC = ({ children }) => ( 15 |
{children}
16 | ) 17 | -------------------------------------------------------------------------------- /src/styles/designSystem/MenuPopper.tsx: -------------------------------------------------------------------------------- 1 | import { FC, PropsWithChildren } from 'react' 2 | 3 | import { tw } from '~/styles/utils' 4 | 5 | export const MenuPopper: FC> = ({ 6 | className, 7 | children, 8 | }) =>
{children}
9 | -------------------------------------------------------------------------------- /src/styles/designSystem/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DrawerContent' 2 | export * from './List' 3 | export * from './MenuPopper' 4 | export * from './PageHeader' 5 | -------------------------------------------------------------------------------- /src/styles/index.ts: -------------------------------------------------------------------------------- 1 | export * from './muiTheme' 2 | export * from './colorsPalette' 3 | export * from './designSystem' 4 | -------------------------------------------------------------------------------- /src/styles/utils.ts: -------------------------------------------------------------------------------- 1 | import { cx, CxOptions } from 'class-variance-authority' 2 | import { twMerge } from 'tailwind-merge' 3 | 4 | export const tw = (...inputs: CxOptions) => { 5 | return twMerge(cx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /start.dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pnpm install 4 | pnpm run dev 5 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import sharedConfig from 'lago-configs/tailwind' 2 | import { Config } from 'prettier' 3 | 4 | const config: Pick = { 5 | content: ['src/**/*.{js,ts,jsx,tsx}'], 6 | presets: [sharedConfig], 7 | } 8 | 9 | export default config 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["**/*.tsx", "**/*.ts", "**/*.d.ts"], 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "baseUrl": "src", 6 | "paths": { 7 | "~/*": ["*"], 8 | "@mui/styled-engine": ["./node_modules/@mui/styled-engine-sc"] 9 | }, 10 | }, 11 | "exclude": ["node_modules", "cypress", "packages"], 12 | "extends": "lago-configs/tsconfig", 13 | } -------------------------------------------------------------------------------- /vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | --------------------------------------------------------------------------------