@carlonicora/nextjs-jsonapi 1.15.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ApiData-DPKNfY-9.d.mts +10 -0
- package/dist/ApiData-DPKNfY-9.d.ts +10 -0
- package/dist/ApiRequestDataTypeInterface-DIEOFn9s.d.mts +40 -0
- package/dist/ApiRequestDataTypeInterface-DIEOFn9s.d.ts +40 -0
- package/dist/{ApiResponseInterface-QLDnxLA9.d.ts → ApiResponseInterface-BKyod24U.d.ts} +3 -11
- package/dist/{ApiResponseInterface-B4QdWh-y.d.mts → ApiResponseInterface-Dqvu09tz.d.mts} +3 -11
- package/dist/{BlockNoteEditor-FGXYUAWI.js → BlockNoteEditor-34T5CY27.js} +17 -16
- package/dist/BlockNoteEditor-34T5CY27.js.map +1 -0
- package/dist/{BlockNoteEditor-ITJLAOXC.mjs → BlockNoteEditor-4Z6TZBJE.mjs} +7 -6
- package/dist/{BlockNoteEditor-ITJLAOXC.mjs.map → BlockNoteEditor-4Z6TZBJE.mjs.map} +1 -1
- package/dist/JsonApiContext-Bsm_Q2oe.d.mts +41 -0
- package/dist/JsonApiContext-Bsm_Q2oe.d.ts +41 -0
- package/dist/JsonApiRequest-54ZBO7WQ.js +24 -0
- package/dist/{JsonApiRequest-FXZCYIER.js.map → JsonApiRequest-54ZBO7WQ.js.map} +1 -1
- package/dist/{JsonApiRequest-HFWXMKMA.mjs → JsonApiRequest-XWQWTFEQ.mjs} +2 -2
- package/dist/chunk-3EPNHTMH.js +26 -0
- package/dist/chunk-3EPNHTMH.js.map +1 -0
- package/dist/{chunk-TGBXBUWM.mjs → chunk-3VM3WAOV.mjs} +8 -1
- package/dist/chunk-3VM3WAOV.mjs.map +1 -0
- package/dist/{chunk-6YD42BP6.js → chunk-7DTKRMYW.js} +31 -19
- package/dist/chunk-7DTKRMYW.js.map +1 -0
- package/dist/{chunk-PK5DRSUD.js → chunk-D7H7SRWB.js} +4104 -793
- package/dist/chunk-D7H7SRWB.js.map +1 -0
- package/dist/{chunk-JGVXZS7M.mjs → chunk-KUFWHMMY.mjs} +1592 -133
- package/dist/chunk-KUFWHMMY.mjs.map +1 -0
- package/dist/{chunk-SJIVGCNM.mjs → chunk-KX7YG6LY.mjs} +27 -15
- package/dist/chunk-KX7YG6LY.mjs.map +1 -0
- package/dist/{chunk-FPZPD4JI.js → chunk-LI6CPNJI.js} +17 -10
- package/dist/chunk-LI6CPNJI.js.map +1 -0
- package/dist/{chunk-WAFOKMKT.mjs → chunk-SXPXC2TY.mjs} +3729 -418
- package/dist/chunk-SXPXC2TY.mjs.map +1 -0
- package/dist/{chunk-C6QXZGL7.js → chunk-UYY34W7R.js} +1622 -163
- package/dist/chunk-UYY34W7R.js.map +1 -0
- package/dist/chunk-VOXD3ZLY.mjs +26 -0
- package/dist/chunk-VOXD3ZLY.mjs.map +1 -0
- package/dist/client/index.d.mts +11 -45
- package/dist/client/index.d.ts +11 -45
- package/dist/client/index.js +10 -6
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +13 -9
- package/dist/components/index.d.mts +254 -9
- package/dist/components/index.d.ts +254 -9
- package/dist/components/index.js +85 -6
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +84 -5
- package/dist/{config-C5tGGrYf.d.mts → config--nwiW74Z.d.ts} +7 -2
- package/dist/{config-eceYM5kN.d.ts → config-BKSQmUWU.d.mts} +7 -2
- package/dist/{content.interface-CxBBC7ec.d.mts → content.interface-4VICFRA0.d.ts} +2 -1
- package/dist/{content.interface-TB2MfJGs.d.ts → content.interface-CFc97-Cj.d.mts} +2 -1
- package/dist/contexts/index.d.mts +3 -2
- package/dist/contexts/index.d.ts +3 -2
- package/dist/contexts/index.js +7 -6
- package/dist/contexts/index.js.map +1 -1
- package/dist/contexts/index.mjs +6 -5
- package/dist/core/index.d.mts +524 -18
- package/dist/core/index.d.ts +524 -18
- package/dist/core/index.js +54 -4
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +53 -3
- package/dist/index.d.mts +16 -12
- package/dist/index.d.ts +16 -12
- package/dist/index.js +57 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +58 -6
- package/dist/{notification.interface-lG6UpTpt.d.mts → notification.interface-BGaPiCUM.d.mts} +3 -42
- package/dist/{notification.interface-lG6UpTpt.d.ts → notification.interface-CqwaOIgM.d.ts} +3 -42
- package/dist/{s3.service-DP_hsssD.d.mts → s3.service-BYs88XEE.d.ts} +21 -2
- package/dist/{s3.service-Dq-PTUNa.d.ts → s3.service-C0BjOdvn.d.mts} +21 -2
- package/dist/scripts/generate-web-module/generator.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/generator.js +66 -0
- package/dist/scripts/generate-web-module/generator.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/index.d.ts +8 -0
- package/dist/scripts/generate-web-module/templates/index.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/templates/index.js +18 -1
- package/dist/scripts/generate-web-module/templates/index.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/project/bootstrapper.template.d.ts +7 -0
- package/dist/scripts/generate-web-module/templates/project/bootstrapper.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/project/bootstrapper.template.js +141 -0
- package/dist/scripts/generate-web-module/templates/project/bootstrapper.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/project/env.template.d.ts +7 -0
- package/dist/scripts/generate-web-module/templates/project/env.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/project/env.template.js +110 -0
- package/dist/scripts/generate-web-module/templates/project/env.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/project/main-layout.template.d.ts +7 -0
- package/dist/scripts/generate-web-module/templates/project/main-layout.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/project/main-layout.template.js +101 -0
- package/dist/scripts/generate-web-module/templates/project/main-layout.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/project/middleware-env.template.d.ts +7 -0
- package/dist/scripts/generate-web-module/templates/project/middleware-env.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/project/middleware-env.template.js +66 -0
- package/dist/scripts/generate-web-module/templates/project/middleware-env.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/project/settings-container.template.d.ts +7 -0
- package/dist/scripts/generate-web-module/templates/project/settings-container.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/project/settings-container.template.js +257 -0
- package/dist/scripts/generate-web-module/templates/project/settings-container.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/project/settings-context.template.d.ts +7 -0
- package/dist/scripts/generate-web-module/templates/project/settings-context.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/project/settings-context.template.js +124 -0
- package/dist/scripts/generate-web-module/templates/project/settings-context.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/project/settings-module-page.template.d.ts +7 -0
- package/dist/scripts/generate-web-module/templates/project/settings-module-page.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/project/settings-module-page.template.js +78 -0
- package/dist/scripts/generate-web-module/templates/project/settings-module-page.template.js.map +1 -0
- package/dist/scripts/generate-web-module/templates/project/settings-page.template.d.ts +7 -0
- package/dist/scripts/generate-web-module/templates/project/settings-page.template.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/templates/project/settings-page.template.js +75 -0
- package/dist/scripts/generate-web-module/templates/project/settings-page.template.js.map +1 -0
- package/dist/scripts/generate-web-module/types/template-data.interface.d.ts +1 -1
- package/dist/scripts/generate-web-module/types/template-data.interface.d.ts.map +1 -1
- package/dist/server/index.d.mts +6 -4
- package/dist/server/index.d.ts +6 -4
- package/dist/server/index.js +13 -13
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +3 -3
- package/dist/stripe-subscription.interface-B-TM40Io.d.ts +226 -0
- package/dist/stripe-subscription.interface-DDxnpj0F.d.mts +226 -0
- package/dist/testing/index.d.mts +338 -0
- package/dist/testing/index.d.ts +338 -0
- package/dist/testing/index.js +323 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/index.mjs +323 -0
- package/dist/testing/index.mjs.map +1 -0
- package/dist/{useSocket-Bua6MwLi.d.mts → useSocket-BNj9PrRw.d.mts} +1 -1
- package/dist/{useSocket-D5dhUp4m.d.ts → useSocket-Dwt8cz1x.d.ts} +1 -1
- package/package.json +29 -3
- package/scripts/generate-web-module/generator.ts +83 -0
- package/scripts/generate-web-module/templates/index.ts +10 -0
- package/scripts/generate-web-module/templates/project/bootstrapper.template.ts +108 -0
- package/scripts/generate-web-module/templates/project/env.template.ts +77 -0
- package/scripts/generate-web-module/templates/project/main-layout.template.tsx +68 -0
- package/scripts/generate-web-module/templates/project/middleware-env.template.ts +33 -0
- package/scripts/generate-web-module/templates/project/settings-container.template.tsx +224 -0
- package/scripts/generate-web-module/templates/project/settings-context.template.tsx +91 -0
- package/scripts/generate-web-module/templates/project/settings-module-page.template.tsx +45 -0
- package/scripts/generate-web-module/templates/project/settings-page.template.tsx +42 -0
- package/scripts/generate-web-module/types/template-data.interface.ts +1 -1
- package/src/client/config.ts +9 -0
- package/src/client/hooks/__tests__/useJsonApiGet.test.tsx +229 -0
- package/src/client/hooks/__tests__/useJsonApiMutation.test.tsx +348 -0
- package/src/client/hooks/__tests__/useRehydration.test.ts +188 -0
- package/src/components/forms/__tests__/FormCheckbox.test.tsx +238 -0
- package/src/components/forms/__tests__/FormDate.test.tsx +212 -0
- package/src/components/forms/__tests__/FormInput.test.tsx +292 -0
- package/src/components/forms/__tests__/FormSelect.test.tsx +173 -0
- package/src/components/index.ts +7 -0
- package/src/components/tables/__tests__/ContentListTable.test.tsx +411 -0
- package/src/core/abstracts/AbstractService.ts +104 -0
- package/src/core/endpoint/EndpointCreator.ts +7 -4
- package/src/core/endpoint/__tests__/EndpointCreator.test.ts +168 -0
- package/src/core/factories/__tests__/JsonApiDataFactory.test.ts +109 -0
- package/src/core/factories/__tests__/RehydrationFactory.test.ts +151 -0
- package/src/core/index.ts +12 -4
- package/src/core/interfaces/ApiResponseInterface.ts +1 -0
- package/src/core/registry/ModuleRegistry.ts +11 -2
- package/src/core/registry/__tests__/DataClassRegistry.test.ts +136 -0
- package/src/core/registry/__tests__/ModuleRegistrar.test.ts +159 -0
- package/src/core/utils/translateResponse.ts +17 -0
- package/src/features/auth/components/details/LandingComponent.tsx +14 -12
- package/src/features/billing/components/cards/BillingUsageSummaryCard.tsx +97 -0
- package/src/features/billing/components/cards/CustomerInfoCard.tsx +112 -0
- package/src/features/billing/components/cards/InvoicesSummaryCard.tsx +114 -0
- package/src/features/billing/components/cards/PaymentMethodSummaryCard.tsx +119 -0
- package/src/features/billing/components/cards/SubscriptionSummaryCard.tsx +146 -0
- package/src/features/billing/components/cards/index.ts +5 -0
- package/src/features/billing/components/containers/BillingDashboardContainer.tsx +427 -0
- package/src/features/billing/components/containers/index.ts +1 -0
- package/src/features/billing/components/index.ts +6 -0
- package/src/features/billing/components/modals/BillingDetailModal.tsx +36 -0
- package/src/features/billing/components/modals/index.ts +1 -0
- package/src/features/billing/components/providers/StripeProvider.tsx +48 -0
- package/src/features/billing/components/providers/index.ts +1 -0
- package/src/features/billing/components/utils/currency.ts +49 -0
- package/src/features/billing/components/utils/date.ts +21 -0
- package/src/features/billing/components/utils/index.ts +2 -0
- package/src/features/billing/components/widgets/BillingAlertBanner.tsx +63 -0
- package/src/features/billing/components/widgets/index.ts +1 -0
- package/src/features/billing/data/Billing.ts +17 -0
- package/src/features/billing/data/billing.service.ts +58 -0
- package/src/features/billing/data/index.ts +5 -0
- package/src/features/billing/index.ts +3 -0
- package/src/features/billing/modules/billing.module.ts +9 -0
- package/src/features/billing/modules/index.ts +1 -0
- package/src/features/billing/stripe-customer/components/containers/PaymentMethodsContainer.tsx +79 -0
- package/src/features/billing/stripe-customer/components/containers/index.ts +1 -0
- package/src/features/billing/stripe-customer/components/details/PaymentMethodCard.tsx +151 -0
- package/src/features/billing/stripe-customer/components/details/index.ts +1 -0
- package/src/features/billing/stripe-customer/components/forms/PaymentMethodEditor.tsx +186 -0
- package/src/features/billing/stripe-customer/components/forms/index.ts +1 -0
- package/src/features/billing/stripe-customer/components/index.ts +4 -0
- package/src/features/billing/stripe-customer/components/lists/PaymentMethodsList.tsx +19 -0
- package/src/features/billing/stripe-customer/components/lists/index.ts +1 -0
- package/src/features/billing/stripe-customer/data/index.ts +5 -0
- package/src/features/billing/stripe-customer/data/payment-method.interface.ts +27 -0
- package/src/features/billing/stripe-customer/data/payment-method.ts +119 -0
- package/src/features/billing/stripe-customer/data/stripe-customer.interface.ts +16 -0
- package/src/features/billing/stripe-customer/data/stripe-customer.service.ts +128 -0
- package/src/features/billing/stripe-customer/data/stripe-customer.ts +71 -0
- package/src/features/billing/stripe-customer/index.ts +3 -0
- package/src/features/billing/stripe-customer/stripe-customer.module.ts +9 -0
- package/src/features/billing/stripe-customer/stripe-payment-method.module.ts +9 -0
- package/src/features/billing/stripe-invoice/components/containers/InvoicesContainer.tsx +66 -0
- package/src/features/billing/stripe-invoice/components/containers/index.ts +1 -0
- package/src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx +172 -0
- package/src/features/billing/stripe-invoice/components/details/index.ts +1 -0
- package/src/features/billing/stripe-invoice/components/index.ts +4 -0
- package/src/features/billing/stripe-invoice/components/lists/InvoicesList.tsx +84 -0
- package/src/features/billing/stripe-invoice/components/lists/index.ts +1 -0
- package/src/features/billing/stripe-invoice/components/widgets/InvoiceStatusBadge.tsx +41 -0
- package/src/features/billing/stripe-invoice/components/widgets/index.ts +1 -0
- package/src/features/billing/stripe-invoice/data/index.ts +3 -0
- package/src/features/billing/stripe-invoice/data/stripe-invoice.interface.ts +65 -0
- package/src/features/billing/stripe-invoice/data/stripe-invoice.service.ts +64 -0
- package/src/features/billing/stripe-invoice/data/stripe-invoice.ts +177 -0
- package/src/features/billing/stripe-invoice/index.ts +2 -0
- package/src/features/billing/stripe-invoice/stripe-invoice.module.ts +9 -0
- package/src/features/billing/stripe-price/components/forms/PriceEditor.tsx +304 -0
- package/src/features/billing/stripe-price/components/forms/index.ts +1 -0
- package/src/features/billing/stripe-price/components/index.ts +2 -0
- package/src/features/billing/stripe-price/components/lists/PricesList.tsx +283 -0
- package/src/features/billing/stripe-price/components/lists/index.ts +1 -0
- package/src/features/billing/stripe-price/data/index.ts +3 -0
- package/src/features/billing/stripe-price/data/stripe-price.interface.ts +48 -0
- package/src/features/billing/stripe-price/data/stripe-price.service.ts +123 -0
- package/src/features/billing/stripe-price/data/stripe-price.ts +156 -0
- package/src/features/billing/stripe-price/index.ts +2 -0
- package/src/features/billing/stripe-price/stripe-price.module.ts +9 -0
- package/src/features/billing/stripe-product/components/containers/ProductsAdminContainer.tsx +86 -0
- package/src/features/billing/stripe-product/components/containers/index.ts +1 -0
- package/src/features/billing/stripe-product/components/forms/ProductEditor.tsx +100 -0
- package/src/features/billing/stripe-product/components/forms/index.ts +1 -0
- package/src/features/billing/stripe-product/components/index.ts +3 -0
- package/src/features/billing/stripe-product/components/lists/ProductsList.tsx +206 -0
- package/src/features/billing/stripe-product/components/lists/index.ts +1 -0
- package/src/features/billing/stripe-product/data/index.ts +3 -0
- package/src/features/billing/stripe-product/data/stripe-product.interface.ts +18 -0
- package/src/features/billing/stripe-product/data/stripe-product.service.ts +112 -0
- package/src/features/billing/stripe-product/data/stripe-product.ts +74 -0
- package/src/features/billing/stripe-product/index.ts +2 -0
- package/src/features/billing/stripe-product/stripe-product.module.ts +9 -0
- package/src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx +304 -0
- package/src/features/billing/stripe-subscription/components/containers/index.ts +1 -0
- package/src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx +223 -0
- package/src/features/billing/stripe-subscription/components/details/index.ts +1 -0
- package/src/features/billing/stripe-subscription/components/forms/CancelSubscriptionDialog.tsx +116 -0
- package/src/features/billing/stripe-subscription/components/forms/SubscriptionEditor.tsx +331 -0
- package/src/features/billing/stripe-subscription/components/forms/index.ts +2 -0
- package/src/features/billing/stripe-subscription/components/index.ts +5 -0
- package/src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx +104 -0
- package/src/features/billing/stripe-subscription/components/lists/index.ts +1 -0
- package/src/features/billing/stripe-subscription/components/widgets/PricingCard.tsx +95 -0
- package/src/features/billing/stripe-subscription/components/widgets/PricingCardsGrid.tsx +110 -0
- package/src/features/billing/stripe-subscription/components/widgets/ProrationPreview.tsx +41 -0
- package/src/features/billing/stripe-subscription/components/widgets/SubscriptionStatusBadge.tsx +60 -0
- package/src/features/billing/stripe-subscription/components/widgets/index.ts +4 -0
- package/src/features/billing/stripe-subscription/data/index.ts +3 -0
- package/src/features/billing/stripe-subscription/data/stripe-subscription.interface.ts +66 -0
- package/src/features/billing/stripe-subscription/data/stripe-subscription.service.ts +193 -0
- package/src/features/billing/stripe-subscription/data/stripe-subscription.ts +135 -0
- package/src/features/billing/stripe-subscription/hooks/index.ts +1 -0
- package/src/features/billing/stripe-subscription/hooks/useConfirmSubscriptionPayment.ts +111 -0
- package/src/features/billing/stripe-subscription/index.ts +5 -0
- package/src/features/billing/stripe-subscription/stripe-subscription.module.ts +9 -0
- package/src/features/billing/stripe-usage/components/containers/UsageContainer.tsx +109 -0
- package/src/features/billing/stripe-usage/components/containers/index.ts +1 -0
- package/src/features/billing/stripe-usage/components/details/UsageSummaryCard.tsx +90 -0
- package/src/features/billing/stripe-usage/components/details/index.ts +1 -0
- package/src/features/billing/stripe-usage/components/index.ts +4 -0
- package/src/features/billing/stripe-usage/components/lists/UsageHistoryTable.tsx +72 -0
- package/src/features/billing/stripe-usage/components/lists/index.ts +1 -0
- package/src/features/billing/stripe-usage/components/widgets/UsageSummaryCards.tsx +19 -0
- package/src/features/billing/stripe-usage/components/widgets/index.ts +1 -0
- package/src/features/billing/stripe-usage/data/index.ts +3 -0
- package/src/features/billing/stripe-usage/data/stripe-usage.interface.ts +55 -0
- package/src/features/billing/stripe-usage/data/stripe-usage.service.ts +129 -0
- package/src/features/billing/stripe-usage/data/stripe-usage.ts +70 -0
- package/src/features/billing/stripe-usage/index.ts +2 -0
- package/src/features/billing/stripe-usage/stripe-usage.module.ts +9 -0
- package/src/features/company/components/forms/CompanyEditor.tsx +2 -2
- package/src/features/company/contexts/CompanyContext.tsx +2 -2
- package/src/features/feature/components/forms/FormFeatures.tsx +13 -106
- package/src/features/feature/data/feature.interface.ts +1 -1
- package/src/features/feature/data/feature.ts +4 -4
- package/src/features/index.ts +7 -0
- package/src/features/module/data/module.interface.ts +0 -1
- package/src/features/module/data/module.ts +0 -6
- package/src/features/user/components/lists/ContributorsList.tsx +2 -2
- package/src/features/user/components/widgets/UserAvatar.tsx +1 -1
- package/src/hooks/__tests__/useDataListRetriever.test.ts +321 -0
- package/src/hooks/__tests__/useDebounce.test.ts +170 -0
- package/src/index.ts +5 -2
- package/src/login/config.ts +27 -0
- package/src/login/index.ts +2 -0
- package/src/shadcnui/custom/link.tsx +16 -6
- package/src/testing/factories/createMockApiData.ts +143 -0
- package/src/testing/factories/createMockModule.ts +32 -0
- package/src/testing/factories/createMockResponse.ts +93 -0
- package/src/testing/factories/createMockService.ts +79 -0
- package/src/testing/index.ts +70 -0
- package/src/testing/matchers/jsonApiMatchers.ts +174 -0
- package/src/testing/providers/MockJsonApiProvider.tsx +58 -0
- package/src/testing/utils/renderWithProviders.tsx +76 -0
- package/src/utils/__tests__/date-formatter.test.ts +161 -0
- package/src/utils/__tests__/exists.test.ts +100 -0
- package/src/utils/blocknote-diff.util.ts +2 -1
- package/src/utils/blocknote-word-diff-renderer.util.ts +8 -7
- package/src/utils/cn.test.ts +44 -0
- package/dist/AuthComponent-hxOPs9o8.d.mts +0 -11
- package/dist/AuthComponent-hxOPs9o8.d.ts +0 -11
- package/dist/BlockNoteEditor-FGXYUAWI.js.map +0 -1
- package/dist/JsonApiRequest-FXZCYIER.js +0 -24
- package/dist/chunk-6YD42BP6.js.map +0 -1
- package/dist/chunk-C6QXZGL7.js.map +0 -1
- package/dist/chunk-FPZPD4JI.js.map +0 -1
- package/dist/chunk-JGVXZS7M.mjs.map +0 -1
- package/dist/chunk-PK5DRSUD.js.map +0 -1
- package/dist/chunk-SJIVGCNM.mjs.map +0 -1
- package/dist/chunk-TGBXBUWM.mjs.map +0 -1
- package/dist/chunk-WAFOKMKT.mjs.map +0 -1
- package/src/discord/config.ts +0 -15
- package/src/discord/index.ts +0 -1
- /package/dist/{JsonApiRequest-HFWXMKMA.mjs.map → JsonApiRequest-XWQWTFEQ.mjs.map} +0 -0
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getApiUrl,
|
|
3
3
|
getAppUrl,
|
|
4
|
+
getI18nLink,
|
|
4
5
|
getRoleId,
|
|
6
|
+
getStripePublishableKey,
|
|
5
7
|
getTrackablePages,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
isDiscordAuthEnabled,
|
|
9
|
+
isInternalAuthEnabled,
|
|
10
|
+
isRegistrationAllowed,
|
|
8
11
|
useI18nDateFnsLocale,
|
|
9
12
|
useI18nLocale,
|
|
10
13
|
useI18nRouter,
|
|
11
14
|
useI18nTranslations
|
|
12
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-KX7YG6LY.mjs";
|
|
13
16
|
import {
|
|
14
17
|
AuthService,
|
|
15
18
|
ClientAbstractService,
|
|
@@ -22,6 +25,12 @@ import {
|
|
|
22
25
|
RehydrationFactory,
|
|
23
26
|
RoleService,
|
|
24
27
|
S3Service,
|
|
28
|
+
StripeCustomerService,
|
|
29
|
+
StripeInvoiceService,
|
|
30
|
+
StripePriceService,
|
|
31
|
+
StripeProductService,
|
|
32
|
+
StripeSubscriptionService,
|
|
33
|
+
StripeUsageService,
|
|
25
34
|
UserService,
|
|
26
35
|
checkPermissions,
|
|
27
36
|
cn,
|
|
@@ -30,7 +39,10 @@ import {
|
|
|
30
39
|
rehydrate,
|
|
31
40
|
useComposedRefs,
|
|
32
41
|
useIsMobile
|
|
33
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-KUFWHMMY.mjs";
|
|
43
|
+
import {
|
|
44
|
+
JsonApiContext
|
|
45
|
+
} from "./chunk-VOXD3ZLY.mjs";
|
|
34
46
|
import {
|
|
35
47
|
__name
|
|
36
48
|
} from "./chunk-PAWJFY3S.mjs";
|
|
@@ -4792,11 +4804,11 @@ function KanbanOverlay(props) {
|
|
|
4792
4804
|
__name(KanbanOverlay, "KanbanOverlay");
|
|
4793
4805
|
|
|
4794
4806
|
// src/shadcnui/custom/link.tsx
|
|
4795
|
-
import NextLink from "next/link";
|
|
4796
4807
|
import * as React33 from "react";
|
|
4797
4808
|
import { jsx as jsx46 } from "react/jsx-runtime";
|
|
4798
|
-
var Link2 = React33.forwardRef(({ className, ...props }, ref) => {
|
|
4799
|
-
|
|
4809
|
+
var Link2 = React33.forwardRef(({ className, href, children, ...props }, ref) => {
|
|
4810
|
+
const NextIntlLink = getI18nLink();
|
|
4811
|
+
return /* @__PURE__ */ jsx46(NextIntlLink, { prefetch: false, ref, href, className: cn(`font-medium`, className), ...props, children });
|
|
4800
4812
|
});
|
|
4801
4813
|
Link2.displayName = "Link";
|
|
4802
4814
|
|
|
@@ -5175,7 +5187,7 @@ function UserEditorInternal({ user, propagateChanges, adminCreated, trigger, onR
|
|
|
5175
5187
|
useEffect9(() => {
|
|
5176
5188
|
if (!companyFromContext && userCompany) setCompany(userCompany);
|
|
5177
5189
|
}, [company]);
|
|
5178
|
-
const
|
|
5190
|
+
const formSchema2 = z.object({
|
|
5179
5191
|
id: z.uuidv4(),
|
|
5180
5192
|
name: z.string().min(1, { message: t(`foundations.user.fields.name.error`) }),
|
|
5181
5193
|
email: z.string().min(1, { message: t(`generic.fields.email.error`) }),
|
|
@@ -5188,7 +5200,7 @@ function UserEditorInternal({ user, propagateChanges, adminCreated, trigger, onR
|
|
|
5188
5200
|
avatar: z.string().optional()
|
|
5189
5201
|
});
|
|
5190
5202
|
const form = useForm({
|
|
5191
|
-
resolver: zodResolver(
|
|
5203
|
+
resolver: zodResolver(formSchema2),
|
|
5192
5204
|
defaultValues: {
|
|
5193
5205
|
id: user?.id || v4(),
|
|
5194
5206
|
name: user?.name || "",
|
|
@@ -6331,12 +6343,12 @@ function CompanyConfigurationEditorInternal({ company }) {
|
|
|
6331
6343
|
setOpen(false);
|
|
6332
6344
|
form.reset();
|
|
6333
6345
|
}, "close");
|
|
6334
|
-
const
|
|
6346
|
+
const formSchema2 = z2.object({
|
|
6335
6347
|
isManagedKnowledge: z2.boolean().optional(),
|
|
6336
6348
|
allowPublicBot: z2.boolean().optional()
|
|
6337
6349
|
});
|
|
6338
6350
|
const form = useForm2({
|
|
6339
|
-
resolver: zodResolver2(
|
|
6351
|
+
resolver: zodResolver2(formSchema2),
|
|
6340
6352
|
defaultValues,
|
|
6341
6353
|
shouldUnregister: false
|
|
6342
6354
|
});
|
|
@@ -6510,7 +6522,7 @@ function CompanyEditorInternal({ company, propagateChanges, onRevalidate }) {
|
|
|
6510
6522
|
const [contentType, setContentType] = useState16(null);
|
|
6511
6523
|
const t = useTranslations22();
|
|
6512
6524
|
const generateUrl = usePageUrlGenerator();
|
|
6513
|
-
const
|
|
6525
|
+
const formSchema2 = z3.object({
|
|
6514
6526
|
id: z3.uuidv4(),
|
|
6515
6527
|
name: z3.string().min(1, {
|
|
6516
6528
|
message: t(`foundations.company.fields.name.error`)
|
|
@@ -6520,7 +6532,7 @@ function CompanyEditorInternal({ company, propagateChanges, onRevalidate }) {
|
|
|
6520
6532
|
logo: z3.string().optional()
|
|
6521
6533
|
});
|
|
6522
6534
|
const form = useForm3({
|
|
6523
|
-
resolver: zodResolver3(
|
|
6535
|
+
resolver: zodResolver3(formSchema2),
|
|
6524
6536
|
defaultValues: {
|
|
6525
6537
|
id: company?.id || v42(),
|
|
6526
6538
|
name: company?.name || "",
|
|
@@ -6574,7 +6586,7 @@ function CompanyEditorInternal({ company, propagateChanges, onRevalidate }) {
|
|
|
6574
6586
|
if (hasRole(getRoleId().Administrator)) {
|
|
6575
6587
|
setFeatures(allfeatures);
|
|
6576
6588
|
} else {
|
|
6577
|
-
setFeatures(allfeatures.filter((feature) => feature.
|
|
6589
|
+
setFeatures(allfeatures.filter((feature) => feature.isCore));
|
|
6578
6590
|
}
|
|
6579
6591
|
}
|
|
6580
6592
|
__name(fetchFeatures, "fetchFeatures");
|
|
@@ -6735,7 +6747,6 @@ var CompanyProvider = /* @__PURE__ */ __name(({ children, dehydratedCompany }) =
|
|
|
6735
6747
|
if (company && hasRole(getRoleId().Administrator) && hasPermissionToModule({ module: Modules.Company, action: "delete" /* Delete */ }))
|
|
6736
6748
|
functions.push(/* @__PURE__ */ jsx79(CompanyDeleter, { company }, "companyDeleter"));
|
|
6737
6749
|
if (hasRole(getRoleId().Administrator) || hasPermissionToModule({ module: Modules.Company, action: "update" /* Update */ })) {
|
|
6738
|
-
if (company) functions.push(/* @__PURE__ */ jsx79(CompanyConfigurationEditor, { company }, "companyConfigurationEditor"));
|
|
6739
6750
|
functions.push(/* @__PURE__ */ jsx79(CompanyEditor, { company, propagateChanges: setCompany }, "companyEditor"));
|
|
6740
6751
|
}
|
|
6741
6752
|
if (functions.length > 0) response.functions = functions;
|
|
@@ -7664,7 +7675,7 @@ __name(AllowedUsersDetails, "AllowedUsersDetails");
|
|
|
7664
7675
|
import dynamic from "next/dynamic";
|
|
7665
7676
|
import React38 from "react";
|
|
7666
7677
|
import { jsx as jsx97 } from "react/jsx-runtime";
|
|
7667
|
-
var BlockNoteEditor = dynamic(() => import("./BlockNoteEditor-
|
|
7678
|
+
var BlockNoteEditor = dynamic(() => import("./BlockNoteEditor-4Z6TZBJE.mjs"), {
|
|
7668
7679
|
ssr: false
|
|
7669
7680
|
});
|
|
7670
7681
|
var BlockNoteEditorContainer = React38.memo(/* @__PURE__ */ __name(function EditorContainer(props) {
|
|
@@ -7951,7 +7962,7 @@ var DatePickerPopover = /* @__PURE__ */ __name(({
|
|
|
7951
7962
|
() => new Intl.DateTimeFormat(locale, { day: "2-digit", month: "2-digit", year: "numeric" }),
|
|
7952
7963
|
[locale]
|
|
7953
7964
|
);
|
|
7954
|
-
const
|
|
7965
|
+
const formatDate5 = /* @__PURE__ */ __name((date) => dateFormatter.format(date), "formatDate");
|
|
7955
7966
|
const datePlaceholder = useMemo7(() => {
|
|
7956
7967
|
const parts = dateFormatter.formatToParts(new Date(2e3, 0, 1));
|
|
7957
7968
|
return parts.map((part) => {
|
|
@@ -7970,7 +7981,7 @@ var DatePickerPopover = /* @__PURE__ */ __name(({
|
|
|
7970
7981
|
return part.value;
|
|
7971
7982
|
}).join("");
|
|
7972
7983
|
}, [dateFormatter]);
|
|
7973
|
-
const [inputValue, setInputValue] = useState28(() => value ?
|
|
7984
|
+
const [inputValue, setInputValue] = useState28(() => value ? formatDate5(value) : "");
|
|
7974
7985
|
const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
|
|
7975
7986
|
const yearOptions = Array.from({ length: currentYear - 1900 + 11 }, (_, i) => 1900 + i);
|
|
7976
7987
|
const monthNames = useMemo7(() => {
|
|
@@ -7993,7 +8004,7 @@ var DatePickerPopover = /* @__PURE__ */ __name(({
|
|
|
7993
8004
|
const handleCalendarSelect = /* @__PURE__ */ __name((selectedDate) => {
|
|
7994
8005
|
onSelect(selectedDate);
|
|
7995
8006
|
if (selectedDate) {
|
|
7996
|
-
setInputValue(
|
|
8007
|
+
setInputValue(formatDate5(selectedDate));
|
|
7997
8008
|
setDisplayMonth(selectedDate);
|
|
7998
8009
|
} else {
|
|
7999
8010
|
setInputValue("");
|
|
@@ -8528,7 +8539,7 @@ function FormDate({
|
|
|
8528
8539
|
() => new Intl.DateTimeFormat(locale, { day: "2-digit", month: "2-digit", year: "numeric" }),
|
|
8529
8540
|
[locale]
|
|
8530
8541
|
);
|
|
8531
|
-
const
|
|
8542
|
+
const formatDate5 = /* @__PURE__ */ __name((date) => dateFormatter.format(date), "formatDate");
|
|
8532
8543
|
const datePlaceholder = useMemo8(() => {
|
|
8533
8544
|
const parts = dateFormatter.formatToParts(new Date(2e3, 0, 1));
|
|
8534
8545
|
return parts.map((part) => {
|
|
@@ -8549,7 +8560,7 @@ function FormDate({
|
|
|
8549
8560
|
}, [dateFormatter]);
|
|
8550
8561
|
const [inputValue, setInputValue] = useState31(() => {
|
|
8551
8562
|
const currentValue = form.getValues(id);
|
|
8552
|
-
return currentValue ?
|
|
8563
|
+
return currentValue ? formatDate5(currentValue) : "";
|
|
8553
8564
|
});
|
|
8554
8565
|
const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
|
|
8555
8566
|
const yearOptions = Array.from({ length: currentYear - 1900 + 1 }, (_, i) => 1900 + i);
|
|
@@ -8575,7 +8586,7 @@ function FormDate({
|
|
|
8575
8586
|
const handleCalendarSelect = /* @__PURE__ */ __name((selectedDate, field) => {
|
|
8576
8587
|
field.onChange(selectedDate);
|
|
8577
8588
|
if (selectedDate) {
|
|
8578
|
-
setInputValue(
|
|
8589
|
+
setInputValue(formatDate5(selectedDate));
|
|
8579
8590
|
setDisplayMonth(selectedDate);
|
|
8580
8591
|
} else {
|
|
8581
8592
|
setInputValue("");
|
|
@@ -8712,7 +8723,7 @@ function FormDateTime({
|
|
|
8712
8723
|
}),
|
|
8713
8724
|
[locale]
|
|
8714
8725
|
);
|
|
8715
|
-
const
|
|
8726
|
+
const formatDateTime2 = /* @__PURE__ */ __name((date) => dateTimeFormatter.format(date), "formatDateTime");
|
|
8716
8727
|
const [selectedHours, setSelectedHours] = useState32((/* @__PURE__ */ new Date()).getHours());
|
|
8717
8728
|
const [selectedMinutes, setSelectedMinutes] = useState32(roundToNearestFiveMinutes((/* @__PURE__ */ new Date()).getMinutes()));
|
|
8718
8729
|
const hoursOptions = Array.from({ length: 24 }, (_, i) => {
|
|
@@ -8758,7 +8769,7 @@ function FormDateTime({
|
|
|
8758
8769
|
variant: "outline",
|
|
8759
8770
|
className: cn("w-full pl-3 text-left font-normal", !field.value && "text-muted-foreground"),
|
|
8760
8771
|
children: [
|
|
8761
|
-
field.value ?
|
|
8772
|
+
field.value ? formatDateTime2(field.value) : /* @__PURE__ */ jsx110("span", { children: t(`generic.pick_date_time`) }),
|
|
8762
8773
|
/* @__PURE__ */ jsx110(CalendarIcon4, { className: "ml-auto h-4 w-4 opacity-50" })
|
|
8763
8774
|
]
|
|
8764
8775
|
}
|
|
@@ -9371,118 +9382,35 @@ var dropzone = {
|
|
|
9371
9382
|
|
|
9372
9383
|
// src/features/feature/components/forms/FormFeatures.tsx
|
|
9373
9384
|
import { jsx as jsx120, jsxs as jsxs72 } from "react/jsx-runtime";
|
|
9374
|
-
function FormFeatures({
|
|
9375
|
-
form,
|
|
9376
|
-
name,
|
|
9377
|
-
features,
|
|
9378
|
-
featureField = "featureIds",
|
|
9379
|
-
moduleField = "moduleIds"
|
|
9380
|
-
}) {
|
|
9385
|
+
function FormFeatures({ form, name, features, featureField = "featureIds" }) {
|
|
9381
9386
|
const selectedFeatures = form.watch(featureField);
|
|
9382
|
-
const selectedModules = form.watch(moduleField);
|
|
9383
9387
|
const toggleFeature = /* @__PURE__ */ __name((feature, checked) => {
|
|
9384
9388
|
let newFeatureIds = [...selectedFeatures];
|
|
9385
|
-
let newModuleIds = [...selectedModules];
|
|
9386
9389
|
if (checked) {
|
|
9387
9390
|
if (!newFeatureIds.includes(feature.id)) {
|
|
9388
9391
|
newFeatureIds.push(feature.id);
|
|
9389
9392
|
}
|
|
9390
|
-
feature.modules.forEach((module) => {
|
|
9391
|
-
if (!newModuleIds.includes(module.id)) {
|
|
9392
|
-
newModuleIds.push(module.id);
|
|
9393
|
-
}
|
|
9394
|
-
});
|
|
9395
9393
|
} else {
|
|
9396
9394
|
newFeatureIds = newFeatureIds.filter((id) => id !== feature.id);
|
|
9397
|
-
feature.modules.forEach((module) => {
|
|
9398
|
-
newModuleIds = newModuleIds.filter((id) => id !== module.id);
|
|
9399
|
-
});
|
|
9400
9395
|
}
|
|
9401
9396
|
form.setValue(featureField, newFeatureIds);
|
|
9402
|
-
form.setValue(moduleField, newModuleIds);
|
|
9403
9397
|
}, "toggleFeature");
|
|
9404
|
-
const
|
|
9405
|
-
const modulesForFeature = feature.modules.map((m) => m.id);
|
|
9406
|
-
let newModuleIds = [...selectedModules];
|
|
9407
|
-
if (checked) {
|
|
9408
|
-
if (!selectedFeatures.includes(feature.id)) {
|
|
9409
|
-
newModuleIds = newModuleIds.filter((id) => !modulesForFeature.includes(id));
|
|
9410
|
-
newModuleIds.push(module.id);
|
|
9411
|
-
form.setValue(featureField, [...selectedFeatures, feature.id]);
|
|
9412
|
-
} else {
|
|
9413
|
-
if (!newModuleIds.includes(module.id)) {
|
|
9414
|
-
newModuleIds.push(module.id);
|
|
9415
|
-
}
|
|
9416
|
-
}
|
|
9417
|
-
} else {
|
|
9418
|
-
newModuleIds = newModuleIds.filter((id) => id !== module.id);
|
|
9419
|
-
const remaining = feature.modules.filter((m) => newModuleIds.includes(m.id));
|
|
9420
|
-
if (remaining.length === 0) {
|
|
9421
|
-
form.setValue(
|
|
9422
|
-
featureField,
|
|
9423
|
-
selectedFeatures.filter((id) => id !== feature.id)
|
|
9424
|
-
);
|
|
9425
|
-
}
|
|
9426
|
-
}
|
|
9427
|
-
form.setValue(moduleField, newModuleIds);
|
|
9428
|
-
}, "toggleModule");
|
|
9429
|
-
const isFeatureChecked = /* @__PURE__ */ __name((feature) => selectedFeatures.includes(feature.id) || feature.modules.every((module) => selectedModules.includes(module.id)), "isFeatureChecked");
|
|
9398
|
+
const isFeatureChecked = /* @__PURE__ */ __name((feature) => selectedFeatures.includes(feature.id), "isFeatureChecked");
|
|
9430
9399
|
return /* @__PURE__ */ jsxs72("div", { className: "flex w-full flex-col", children: [
|
|
9431
|
-
name && /* @__PURE__ */ jsx120("h2", { className: "mb-
|
|
9432
|
-
features.map((feature) => /* @__PURE__ */
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
/* @__PURE__ */ jsx120(
|
|
9446
|
-
Checkbox,
|
|
9447
|
-
{
|
|
9448
|
-
id: feature.id,
|
|
9449
|
-
checked: isFeatureChecked(feature),
|
|
9450
|
-
onCheckedChange: (val) => {
|
|
9451
|
-
toggleFeature(feature, val === true);
|
|
9452
|
-
}
|
|
9453
|
-
}
|
|
9454
|
-
),
|
|
9455
|
-
/* @__PURE__ */ jsx120(FormLabel, { htmlFor: feature.id, className: "ml-3 cursor-pointer font-normal", children: feature.name })
|
|
9456
|
-
] }),
|
|
9457
|
-
feature.modules.filter((module) => !module.isCore).length > 0 && /* @__PURE__ */ jsx120(AccordionTrigger, { asChild: true, children: /* @__PURE__ */ jsx120("div", { className: "w-full" }) })
|
|
9458
|
-
]
|
|
9459
|
-
}
|
|
9460
|
-
),
|
|
9461
|
-
feature.modules.filter((module) => !module.isCore).length > 0 && /* @__PURE__ */ jsx120(AccordionContent, { className: "pl-6", children: feature.modules.filter((module) => !module.isCore).sort((a, b) => a.name.localeCompare(b.name)).map((module) => /* @__PURE__ */ jsxs72(
|
|
9462
|
-
"div",
|
|
9463
|
-
{
|
|
9464
|
-
className: "flex items-center border-t py-2",
|
|
9465
|
-
onClick: (e) => e.stopPropagation(),
|
|
9466
|
-
children: [
|
|
9467
|
-
/* @__PURE__ */ jsx120(
|
|
9468
|
-
Checkbox,
|
|
9469
|
-
{
|
|
9470
|
-
id: module.id,
|
|
9471
|
-
checked: selectedModules.includes(module.id),
|
|
9472
|
-
onCheckedChange: (val) => {
|
|
9473
|
-
toggleModule(feature, module, val === true);
|
|
9474
|
-
}
|
|
9475
|
-
}
|
|
9476
|
-
),
|
|
9477
|
-
/* @__PURE__ */ jsx120(FormLabel, { htmlFor: module.id, className: "ml-3 cursor-pointer font-normal", children: module.name })
|
|
9478
|
-
]
|
|
9479
|
-
},
|
|
9480
|
-
module.id
|
|
9481
|
-
)) })
|
|
9482
|
-
] })
|
|
9483
|
-
},
|
|
9484
|
-
feature.id
|
|
9485
|
-
)),
|
|
9400
|
+
name && /* @__PURE__ */ jsx120("h2", { className: "mb-4 border-b text-lg font-semibold", children: name }),
|
|
9401
|
+
/* @__PURE__ */ jsx120(ScrollArea, { className: "h-[40vh]", children: /* @__PURE__ */ jsx120("div", { className: "flex flex-col gap-y-2 pr-4", children: features.filter((feature) => !feature.isCore).map((feature) => /* @__PURE__ */ jsxs72("div", { className: "flex items-center", children: [
|
|
9402
|
+
/* @__PURE__ */ jsx120(
|
|
9403
|
+
Checkbox,
|
|
9404
|
+
{
|
|
9405
|
+
id: feature.id,
|
|
9406
|
+
checked: isFeatureChecked(feature),
|
|
9407
|
+
onCheckedChange: (val) => {
|
|
9408
|
+
toggleFeature(feature, val === true);
|
|
9409
|
+
}
|
|
9410
|
+
}
|
|
9411
|
+
),
|
|
9412
|
+
/* @__PURE__ */ jsx120(FormLabel, { htmlFor: feature.id, className: "ml-3 cursor-pointer font-normal", children: feature.name })
|
|
9413
|
+
] }, feature.id)) }) }),
|
|
9486
9414
|
/* @__PURE__ */ jsx120(FormMessage, {})
|
|
9487
9415
|
] });
|
|
9488
9416
|
}
|
|
@@ -9645,22 +9573,6 @@ var cellUrl = /* @__PURE__ */ __name((params) => {
|
|
|
9645
9573
|
};
|
|
9646
9574
|
}, "cellUrl");
|
|
9647
9575
|
|
|
9648
|
-
// src/client/context/JsonApiContext.ts
|
|
9649
|
-
import { createContext as createContext15, useContext as useContext16 } from "react";
|
|
9650
|
-
var JsonApiContext = createContext15(null);
|
|
9651
|
-
function useJsonApiConfig() {
|
|
9652
|
-
const config = useContext16(JsonApiContext);
|
|
9653
|
-
if (!config) {
|
|
9654
|
-
throw new Error("useJsonApiConfig must be used within a JsonApiProvider");
|
|
9655
|
-
}
|
|
9656
|
-
return config;
|
|
9657
|
-
}
|
|
9658
|
-
__name(useJsonApiConfig, "useJsonApiConfig");
|
|
9659
|
-
function useJsonApiConfigOptional() {
|
|
9660
|
-
return useContext16(JsonApiContext);
|
|
9661
|
-
}
|
|
9662
|
-
__name(useJsonApiConfigOptional, "useJsonApiConfigOptional");
|
|
9663
|
-
|
|
9664
9576
|
// src/client/context/JsonApiProvider.tsx
|
|
9665
9577
|
import { useEffect as useEffect30, useMemo as useMemo10 } from "react";
|
|
9666
9578
|
import { jsx as jsx127 } from "react/jsx-runtime";
|
|
@@ -9688,7 +9600,7 @@ function useJsonApiGet(params) {
|
|
|
9688
9600
|
setLoading(true);
|
|
9689
9601
|
setError(null);
|
|
9690
9602
|
try {
|
|
9691
|
-
const { JsonApiGet } = await import("./JsonApiRequest-
|
|
9603
|
+
const { JsonApiGet } = await import("./JsonApiRequest-XWQWTFEQ.mjs");
|
|
9692
9604
|
const language = navigator.language.split("-")[0] || "en";
|
|
9693
9605
|
const apiResponse = await JsonApiGet({
|
|
9694
9606
|
classKey: params.classKey,
|
|
@@ -9793,7 +9705,7 @@ function useJsonApiMutation(config) {
|
|
|
9793
9705
|
setLoading(true);
|
|
9794
9706
|
setError(null);
|
|
9795
9707
|
try {
|
|
9796
|
-
const { JsonApiPost, JsonApiPut, JsonApiPatch, JsonApiDelete } = await import("./JsonApiRequest-
|
|
9708
|
+
const { JsonApiPost, JsonApiPut, JsonApiPatch, JsonApiDelete } = await import("./JsonApiRequest-XWQWTFEQ.mjs");
|
|
9797
9709
|
const language = navigator.language.split("-")[0] || "en";
|
|
9798
9710
|
let apiResponse;
|
|
9799
9711
|
switch (config.method) {
|
|
@@ -10440,7 +10352,7 @@ var ContentListTable = memo(/* @__PURE__ */ __name(function ContentListTable2(pr
|
|
|
10440
10352
|
import Image10 from "next/image";
|
|
10441
10353
|
|
|
10442
10354
|
// src/features/auth/contexts/AuthContext.tsx
|
|
10443
|
-
import { createContext as
|
|
10355
|
+
import { createContext as createContext15, useContext as useContext16, useMemo as useMemo17, useState as useState43 } from "react";
|
|
10444
10356
|
|
|
10445
10357
|
// src/features/auth/components/forms/Register.tsx
|
|
10446
10358
|
import { zodResolver as zodResolver5 } from "@hookform/resolvers/zod";
|
|
@@ -10455,7 +10367,7 @@ function Register() {
|
|
|
10455
10367
|
const t = useTranslations46();
|
|
10456
10368
|
const { setComponentType } = useAuthContext();
|
|
10457
10369
|
const [showConfirmation, setShowConfirmation] = useState42(false);
|
|
10458
|
-
const
|
|
10370
|
+
const formSchema2 = z5.object({
|
|
10459
10371
|
company: z5.string().min(1, {
|
|
10460
10372
|
message: t(`generic.errors.missing_company_name`)
|
|
10461
10373
|
}),
|
|
@@ -10470,7 +10382,7 @@ function Register() {
|
|
|
10470
10382
|
})
|
|
10471
10383
|
});
|
|
10472
10384
|
const form = useForm5({
|
|
10473
|
-
resolver: zodResolver5(
|
|
10385
|
+
resolver: zodResolver5(formSchema2),
|
|
10474
10386
|
defaultValues: {
|
|
10475
10387
|
company: "",
|
|
10476
10388
|
name: "",
|
|
@@ -10572,7 +10484,7 @@ __name(Register, "Register");
|
|
|
10572
10484
|
|
|
10573
10485
|
// src/features/auth/contexts/AuthContext.tsx
|
|
10574
10486
|
import { jsx as jsx135 } from "react/jsx-runtime";
|
|
10575
|
-
var AuthContext =
|
|
10487
|
+
var AuthContext = createContext15(void 0);
|
|
10576
10488
|
var AuthContextProvider = /* @__PURE__ */ __name(({
|
|
10577
10489
|
children,
|
|
10578
10490
|
initialComponentType,
|
|
@@ -10613,7 +10525,7 @@ var AuthContextProvider = /* @__PURE__ */ __name(({
|
|
|
10613
10525
|
);
|
|
10614
10526
|
}, "AuthContextProvider");
|
|
10615
10527
|
var useAuthContext = /* @__PURE__ */ __name(() => {
|
|
10616
|
-
const context =
|
|
10528
|
+
const context = useContext16(AuthContext);
|
|
10617
10529
|
if (context === void 0) {
|
|
10618
10530
|
throw new Error("useAuthContext must be used within a AuthComponentProvider");
|
|
10619
10531
|
}
|
|
@@ -10650,8 +10562,8 @@ function LandingComponent() {
|
|
|
10650
10562
|
/* @__PURE__ */ jsx137(CardDescription, { className: "flex w-full justify-center text-center text-sm", children: t(`generic.description`) })
|
|
10651
10563
|
] }),
|
|
10652
10564
|
/* @__PURE__ */ jsxs80(CardFooter, { className: "mt-4 flex w-full flex-col justify-between gap-y-4", children: [
|
|
10653
|
-
|
|
10654
|
-
/* @__PURE__ */ jsx137(
|
|
10565
|
+
isInternalAuthEnabled() && /* @__PURE__ */ jsxs80(Fragment21, { children: [
|
|
10566
|
+
isRegistrationAllowed() && /* @__PURE__ */ jsx137(
|
|
10655
10567
|
Link2,
|
|
10656
10568
|
{
|
|
10657
10569
|
href: "#",
|
|
@@ -10662,7 +10574,7 @@ function LandingComponent() {
|
|
|
10662
10574
|
),
|
|
10663
10575
|
/* @__PURE__ */ jsx137(Link2, { href: "#", className: "flex w-full justify-end", onClick: () => setComponentType(0 /* Login */), children: /* @__PURE__ */ jsx137(Button, { className: "w-full", variant: `outline`, "data-testid": "page-login-button-initial-login", children: t(`foundations.auth.buttons.login`) }) })
|
|
10664
10576
|
] }),
|
|
10665
|
-
|
|
10577
|
+
isDiscordAuthEnabled() && /* @__PURE__ */ jsx137(Link2, { href: `${getApiUrl()}auth/discord`, className: "flex w-full justify-end", children: /* @__PURE__ */ jsx137(Button, { className: "w-full", variant: `outline`, "data-testid": "page-login-button-initial-login", children: "Login with Discord" }) })
|
|
10666
10578
|
] })
|
|
10667
10579
|
] });
|
|
10668
10580
|
}
|
|
@@ -10701,7 +10613,7 @@ function AcceptInvitation() {
|
|
|
10701
10613
|
setError(t(`foundations.auth.errors.invalid_invitation_code`));
|
|
10702
10614
|
}
|
|
10703
10615
|
}, []);
|
|
10704
|
-
const
|
|
10616
|
+
const formSchema2 = z6.object({
|
|
10705
10617
|
password: z6.string().min(1, {
|
|
10706
10618
|
message: t(`foundations.user.fields.password.error`)
|
|
10707
10619
|
}),
|
|
@@ -10713,7 +10625,7 @@ function AcceptInvitation() {
|
|
|
10713
10625
|
path: ["passwordRetype"]
|
|
10714
10626
|
});
|
|
10715
10627
|
const form = useForm6({
|
|
10716
|
-
resolver: zodResolver6(
|
|
10628
|
+
resolver: zodResolver6(formSchema2),
|
|
10717
10629
|
defaultValues: {
|
|
10718
10630
|
password: "",
|
|
10719
10631
|
passwordRetype: ""
|
|
@@ -10860,13 +10772,13 @@ function ForgotPassword() {
|
|
|
10860
10772
|
const t = useTranslations50();
|
|
10861
10773
|
const { setComponentType } = useAuthContext();
|
|
10862
10774
|
const [showConfirmation, setShowConfirmation] = useState47(false);
|
|
10863
|
-
const
|
|
10775
|
+
const formSchema2 = z7.object({
|
|
10864
10776
|
email: z7.string().email({
|
|
10865
10777
|
message: t(`generic.errors.invalid_email`)
|
|
10866
10778
|
})
|
|
10867
10779
|
});
|
|
10868
10780
|
const form = useForm7({
|
|
10869
|
-
resolver: zodResolver7(
|
|
10781
|
+
resolver: zodResolver7(formSchema2),
|
|
10870
10782
|
defaultValues: {
|
|
10871
10783
|
email: ""
|
|
10872
10784
|
}
|
|
@@ -10932,14 +10844,14 @@ function Login() {
|
|
|
10932
10844
|
const { setComponentType } = useAuthContext();
|
|
10933
10845
|
const generateUrl = usePageUrlGenerator();
|
|
10934
10846
|
const router = useI18nRouter();
|
|
10935
|
-
const
|
|
10847
|
+
const formSchema2 = z8.object({
|
|
10936
10848
|
email: z8.string().email({
|
|
10937
10849
|
message: t(`generic.errors.invalid_email`)
|
|
10938
10850
|
}),
|
|
10939
10851
|
password: z8.string().min(3, { message: t(`foundations.auth.errors.password_too_short`) })
|
|
10940
10852
|
});
|
|
10941
10853
|
const form = useForm8({
|
|
10942
|
-
resolver: zodResolver8(
|
|
10854
|
+
resolver: zodResolver8(formSchema2),
|
|
10943
10855
|
defaultValues: {
|
|
10944
10856
|
email: "",
|
|
10945
10857
|
password: ""
|
|
@@ -11099,7 +11011,7 @@ function ResetPassword() {
|
|
|
11099
11011
|
setError(t(`foundations.auth.errors.invalid_password_reset_code`));
|
|
11100
11012
|
}
|
|
11101
11013
|
}, []);
|
|
11102
|
-
const
|
|
11014
|
+
const formSchema2 = z9.object({
|
|
11103
11015
|
password: z9.string().min(1, {
|
|
11104
11016
|
message: t(`foundations.user.fields.password.error`)
|
|
11105
11017
|
}),
|
|
@@ -11111,7 +11023,7 @@ function ResetPassword() {
|
|
|
11111
11023
|
path: ["passwordRetype"]
|
|
11112
11024
|
});
|
|
11113
11025
|
const form = useForm9({
|
|
11114
|
-
resolver: zodResolver9(
|
|
11026
|
+
resolver: zodResolver9(formSchema2),
|
|
11115
11027
|
defaultValues: {
|
|
11116
11028
|
password: "",
|
|
11117
11029
|
passwordRetype: ""
|
|
@@ -11154,217 +11066,3580 @@ function ResetPassword() {
|
|
|
11154
11066
|
}
|
|
11155
11067
|
__name(ResetPassword, "ResetPassword");
|
|
11156
11068
|
|
|
11157
|
-
// src/features/
|
|
11158
|
-
import {
|
|
11069
|
+
// src/features/billing/components/cards/SubscriptionSummaryCard.tsx
|
|
11070
|
+
import { ChevronRight, CreditCard } from "lucide-react";
|
|
11159
11071
|
import { jsx as jsx144, jsxs as jsxs86 } from "react/jsx-runtime";
|
|
11160
|
-
function
|
|
11161
|
-
|
|
11162
|
-
|
|
11163
|
-
|
|
11164
|
-
|
|
11165
|
-
|
|
11166
|
-
|
|
11167
|
-
|
|
11168
|
-
|
|
11169
|
-
|
|
11170
|
-
|
|
11171
|
-
|
|
11172
|
-
|
|
11173
|
-
|
|
11174
|
-
|
|
11175
|
-
|
|
11176
|
-
|
|
11177
|
-
|
|
11178
|
-
|
|
11179
|
-
|
|
11180
|
-
/* @__PURE__ */ jsx144("div", { className: "text-xs", children: content.abstract })
|
|
11181
|
-
] })
|
|
11182
|
-
] }),
|
|
11183
|
-
/* @__PURE__ */ jsx144(ContributorsList, { content })
|
|
11184
|
-
] }) });
|
|
11185
|
-
}
|
|
11186
|
-
__name(ContentsListElement, "ContentsListElement");
|
|
11187
|
-
|
|
11188
|
-
// src/features/content/components/lists/ContentsListById.tsx
|
|
11189
|
-
import { useTranslations as useTranslations54 } from "next-intl";
|
|
11190
|
-
import { jsx as jsx145 } from "react/jsx-runtime";
|
|
11191
|
-
function ContentsListById({ contentIds }) {
|
|
11192
|
-
const t = useTranslations54();
|
|
11193
|
-
const data = useDataListRetriever({
|
|
11194
|
-
module: Modules.Content,
|
|
11195
|
-
retriever: /* @__PURE__ */ __name((params) => ContentService.findMany(params), "retriever"),
|
|
11196
|
-
retrieverParams: { contentIds }
|
|
11072
|
+
function getStatusBadgeVariant(status) {
|
|
11073
|
+
switch (status) {
|
|
11074
|
+
case "active" /* ACTIVE */:
|
|
11075
|
+
return "default";
|
|
11076
|
+
case "trialing" /* TRIALING */:
|
|
11077
|
+
return "secondary";
|
|
11078
|
+
case "past_due" /* PAST_DUE */:
|
|
11079
|
+
case "unpaid" /* UNPAID */:
|
|
11080
|
+
case "canceled" /* CANCELED */:
|
|
11081
|
+
return "destructive";
|
|
11082
|
+
default:
|
|
11083
|
+
return "outline";
|
|
11084
|
+
}
|
|
11085
|
+
}
|
|
11086
|
+
__name(getStatusBadgeVariant, "getStatusBadgeVariant");
|
|
11087
|
+
function formatDate(date) {
|
|
11088
|
+
return new Date(date).toLocaleDateString(void 0, {
|
|
11089
|
+
year: "numeric",
|
|
11090
|
+
month: "short",
|
|
11091
|
+
day: "numeric"
|
|
11197
11092
|
});
|
|
11198
|
-
return /* @__PURE__ */ jsx145(
|
|
11199
|
-
ContentListTable,
|
|
11200
|
-
{
|
|
11201
|
-
data,
|
|
11202
|
-
fields: ["name" /* name */, "authors" /* authors */, "updatedAt" /* updatedAt */],
|
|
11203
|
-
tableGeneratorType: Modules.Content,
|
|
11204
|
-
title: t(`generic.relevant`)
|
|
11205
|
-
}
|
|
11206
|
-
);
|
|
11207
11093
|
}
|
|
11208
|
-
__name(
|
|
11209
|
-
|
|
11210
|
-
|
|
11211
|
-
|
|
11212
|
-
|
|
11213
|
-
|
|
11214
|
-
|
|
11215
|
-
|
|
11216
|
-
|
|
11217
|
-
|
|
11218
|
-
|
|
11219
|
-
|
|
11220
|
-
|
|
11221
|
-
|
|
11222
|
-
{
|
|
11223
|
-
|
|
11224
|
-
|
|
11225
|
-
|
|
11226
|
-
|
|
11227
|
-
|
|
11094
|
+
__name(formatDate, "formatDate");
|
|
11095
|
+
function formatPrice(amount, currency) {
|
|
11096
|
+
if (amount === void 0) return "N/A";
|
|
11097
|
+
const currencyCode = currency?.toUpperCase() || "USD";
|
|
11098
|
+
return new Intl.NumberFormat(void 0, {
|
|
11099
|
+
style: "currency",
|
|
11100
|
+
currency: currencyCode
|
|
11101
|
+
}).format(amount / 100);
|
|
11102
|
+
}
|
|
11103
|
+
__name(formatPrice, "formatPrice");
|
|
11104
|
+
function formatPlanName(subscription) {
|
|
11105
|
+
const productName = subscription.price?.product?.name || "";
|
|
11106
|
+
const nickname = subscription.price?.nickname || "";
|
|
11107
|
+
if (productName && nickname) {
|
|
11108
|
+
return `${productName} - ${nickname}`;
|
|
11109
|
+
}
|
|
11110
|
+
return productName || nickname || "Subscription";
|
|
11111
|
+
}
|
|
11112
|
+
__name(formatPlanName, "formatPlanName");
|
|
11113
|
+
function SubscriptionSummaryCard({
|
|
11114
|
+
subscriptions,
|
|
11115
|
+
loading,
|
|
11116
|
+
error,
|
|
11117
|
+
onManageClick
|
|
11118
|
+
}) {
|
|
11119
|
+
if (loading) {
|
|
11120
|
+
return /* @__PURE__ */ jsxs86(Card, { children: [
|
|
11121
|
+
/* @__PURE__ */ jsxs86(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
|
|
11122
|
+
/* @__PURE__ */ jsx144(CardTitle, { className: "text-sm font-medium", children: "Subscriptions" }),
|
|
11123
|
+
/* @__PURE__ */ jsx144(CreditCard, { className: "h-4 w-4 text-muted-foreground" })
|
|
11124
|
+
] }),
|
|
11125
|
+
/* @__PURE__ */ jsxs86(CardContent, { children: [
|
|
11126
|
+
/* @__PURE__ */ jsx144(Skeleton, { className: "h-6 w-32 mb-2" }),
|
|
11127
|
+
/* @__PURE__ */ jsx144(Skeleton, { className: "h-4 w-24 mb-1" }),
|
|
11128
|
+
/* @__PURE__ */ jsx144(Skeleton, { className: "h-4 w-40" })
|
|
11129
|
+
] })
|
|
11130
|
+
] });
|
|
11131
|
+
}
|
|
11132
|
+
if (error) {
|
|
11133
|
+
return /* @__PURE__ */ jsxs86(Card, { children: [
|
|
11134
|
+
/* @__PURE__ */ jsxs86(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
|
|
11135
|
+
/* @__PURE__ */ jsx144(CardTitle, { className: "text-sm font-medium", children: "Subscriptions" }),
|
|
11136
|
+
/* @__PURE__ */ jsx144(CreditCard, { className: "h-4 w-4 text-muted-foreground" })
|
|
11137
|
+
] }),
|
|
11138
|
+
/* @__PURE__ */ jsx144(CardContent, { children: /* @__PURE__ */ jsx144("p", { className: "text-sm text-destructive", children: error }) })
|
|
11139
|
+
] });
|
|
11140
|
+
}
|
|
11141
|
+
const activeSubscriptions = subscriptions.filter(
|
|
11142
|
+
(sub) => sub.status === "active" /* ACTIVE */ || sub.status === "trialing" /* TRIALING */
|
|
11228
11143
|
);
|
|
11144
|
+
const primarySubscription = activeSubscriptions[0];
|
|
11145
|
+
return /* @__PURE__ */ jsxs86(Card, { className: "cursor-pointer hover:bg-accent/50 transition-colors", onClick: onManageClick, children: [
|
|
11146
|
+
/* @__PURE__ */ jsxs86(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
|
|
11147
|
+
/* @__PURE__ */ jsx144(CardTitle, { className: "text-sm font-medium", children: "Subscriptions" }),
|
|
11148
|
+
/* @__PURE__ */ jsx144(CreditCard, { className: "h-4 w-4 text-muted-foreground" })
|
|
11149
|
+
] }),
|
|
11150
|
+
/* @__PURE__ */ jsx144(CardContent, { children: subscriptions.length === 0 ? /* @__PURE__ */ jsxs86("div", { className: "space-y-2", children: [
|
|
11151
|
+
/* @__PURE__ */ jsx144("p", { className: "text-xl font-bold text-muted-foreground", children: "No active plan" }),
|
|
11152
|
+
/* @__PURE__ */ jsx144("p", { className: "text-xs text-muted-foreground", children: "Subscribe to get started" }),
|
|
11153
|
+
/* @__PURE__ */ jsxs86(
|
|
11154
|
+
Button,
|
|
11155
|
+
{
|
|
11156
|
+
variant: "outline",
|
|
11157
|
+
size: "sm",
|
|
11158
|
+
className: "mt-2",
|
|
11159
|
+
onClick: (e) => {
|
|
11160
|
+
e.stopPropagation();
|
|
11161
|
+
onManageClick();
|
|
11162
|
+
},
|
|
11163
|
+
children: [
|
|
11164
|
+
"View Plans",
|
|
11165
|
+
/* @__PURE__ */ jsx144(ChevronRight, { className: "h-4 w-4 ml-1" })
|
|
11166
|
+
]
|
|
11167
|
+
}
|
|
11168
|
+
)
|
|
11169
|
+
] }) : primarySubscription ? /* @__PURE__ */ jsxs86("div", { className: "space-y-2", children: [
|
|
11170
|
+
/* @__PURE__ */ jsxs86("div", { className: "flex items-center gap-2", children: [
|
|
11171
|
+
/* @__PURE__ */ jsx144("p", { className: "text-xl font-bold", children: formatPlanName(primarySubscription) }),
|
|
11172
|
+
/* @__PURE__ */ jsx144(Badge, { variant: primarySubscription.cancelAtPeriodEnd ? "secondary" : getStatusBadgeVariant(primarySubscription.status), children: primarySubscription.cancelAtPeriodEnd ? "Canceling" : primarySubscription.status })
|
|
11173
|
+
] }),
|
|
11174
|
+
/* @__PURE__ */ jsxs86("p", { className: "text-sm text-muted-foreground", children: [
|
|
11175
|
+
formatPrice(primarySubscription.price?.unitAmount, primarySubscription.price?.currency),
|
|
11176
|
+
primarySubscription.price?.recurring && /* @__PURE__ */ jsxs86("span", { children: [
|
|
11177
|
+
"/",
|
|
11178
|
+
primarySubscription.price.recurring.interval
|
|
11179
|
+
] })
|
|
11180
|
+
] }),
|
|
11181
|
+
/* @__PURE__ */ jsx144("p", { className: "text-xs text-muted-foreground", children: primarySubscription.cancelAtPeriodEnd ? `Cancels on ${formatDate(primarySubscription.currentPeriodEnd)}` : `Renews on ${formatDate(primarySubscription.currentPeriodEnd)}` }),
|
|
11182
|
+
activeSubscriptions.length > 1 && /* @__PURE__ */ jsxs86("p", { className: "text-xs text-muted-foreground", children: [
|
|
11183
|
+
"+",
|
|
11184
|
+
activeSubscriptions.length - 1,
|
|
11185
|
+
" more subscription(s)"
|
|
11186
|
+
] })
|
|
11187
|
+
] }) : null })
|
|
11188
|
+
] });
|
|
11229
11189
|
}
|
|
11230
|
-
__name(
|
|
11231
|
-
|
|
11232
|
-
// src/features/
|
|
11233
|
-
import {
|
|
11234
|
-
import { jsx as
|
|
11235
|
-
|
|
11236
|
-
|
|
11237
|
-
|
|
11190
|
+
__name(SubscriptionSummaryCard, "SubscriptionSummaryCard");
|
|
11191
|
+
|
|
11192
|
+
// src/features/billing/components/cards/PaymentMethodSummaryCard.tsx
|
|
11193
|
+
import { Wallet, ChevronRight as ChevronRight2 } from "lucide-react";
|
|
11194
|
+
import { jsx as jsx145, jsxs as jsxs87 } from "react/jsx-runtime";
|
|
11195
|
+
function getCardBrandIcon(brand) {
|
|
11196
|
+
const brandMap = {
|
|
11197
|
+
visa: "Visa",
|
|
11198
|
+
mastercard: "Mastercard",
|
|
11199
|
+
amex: "Amex",
|
|
11200
|
+
discover: "Discover",
|
|
11201
|
+
diners: "Diners",
|
|
11202
|
+
jcb: "JCB",
|
|
11203
|
+
unionpay: "UnionPay"
|
|
11204
|
+
};
|
|
11205
|
+
return brandMap[brand.toLowerCase()] || brand;
|
|
11206
|
+
}
|
|
11207
|
+
__name(getCardBrandIcon, "getCardBrandIcon");
|
|
11208
|
+
function PaymentMethodSummaryCard({
|
|
11209
|
+
paymentMethods,
|
|
11210
|
+
defaultPaymentMethodId,
|
|
11211
|
+
loading,
|
|
11212
|
+
error,
|
|
11213
|
+
onManageClick
|
|
11214
|
+
}) {
|
|
11215
|
+
if (loading) {
|
|
11216
|
+
return /* @__PURE__ */ jsxs87(Card, { children: [
|
|
11217
|
+
/* @__PURE__ */ jsxs87(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
|
|
11218
|
+
/* @__PURE__ */ jsx145(CardTitle, { className: "text-sm font-medium", children: "Payment Method" }),
|
|
11219
|
+
/* @__PURE__ */ jsx145(Wallet, { className: "h-4 w-4 text-muted-foreground" })
|
|
11220
|
+
] }),
|
|
11221
|
+
/* @__PURE__ */ jsxs87(CardContent, { children: [
|
|
11222
|
+
/* @__PURE__ */ jsx145(Skeleton, { className: "h-6 w-32 mb-2" }),
|
|
11223
|
+
/* @__PURE__ */ jsx145(Skeleton, { className: "h-4 w-24" })
|
|
11224
|
+
] })
|
|
11225
|
+
] });
|
|
11238
11226
|
}
|
|
11239
|
-
|
|
11240
|
-
|
|
11241
|
-
|
|
11227
|
+
if (error) {
|
|
11228
|
+
return /* @__PURE__ */ jsxs87(Card, { children: [
|
|
11229
|
+
/* @__PURE__ */ jsxs87(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
|
|
11230
|
+
/* @__PURE__ */ jsx145(CardTitle, { className: "text-sm font-medium", children: "Payment Method" }),
|
|
11231
|
+
/* @__PURE__ */ jsx145(Wallet, { className: "h-4 w-4 text-muted-foreground" })
|
|
11232
|
+
] }),
|
|
11233
|
+
/* @__PURE__ */ jsx145(CardContent, { children: /* @__PURE__ */ jsx145("p", { className: "text-sm text-destructive", children: error }) })
|
|
11234
|
+
] });
|
|
11242
11235
|
}
|
|
11243
|
-
|
|
11244
|
-
|
|
11236
|
+
const defaultMethod = paymentMethods.find((pm) => pm.id === defaultPaymentMethodId) || paymentMethods[0];
|
|
11237
|
+
return /* @__PURE__ */ jsxs87(Card, { className: "cursor-pointer hover:bg-accent/50 transition-colors", onClick: onManageClick, children: [
|
|
11238
|
+
/* @__PURE__ */ jsxs87(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
|
|
11239
|
+
/* @__PURE__ */ jsx145(CardTitle, { className: "text-sm font-medium", children: "Payment Method" }),
|
|
11240
|
+
/* @__PURE__ */ jsx145(Wallet, { className: "h-4 w-4 text-muted-foreground" })
|
|
11241
|
+
] }),
|
|
11242
|
+
/* @__PURE__ */ jsx145(CardContent, { children: paymentMethods.length === 0 ? /* @__PURE__ */ jsxs87("div", { className: "space-y-2", children: [
|
|
11243
|
+
/* @__PURE__ */ jsx145("p", { className: "text-xl font-bold text-muted-foreground", children: "No payment method" }),
|
|
11244
|
+
/* @__PURE__ */ jsx145("p", { className: "text-xs text-muted-foreground", children: "Add a card to enable subscriptions" }),
|
|
11245
|
+
/* @__PURE__ */ jsxs87(Button, { variant: "outline", size: "sm", className: "mt-2", onClick: (e) => {
|
|
11246
|
+
e.stopPropagation();
|
|
11247
|
+
onManageClick();
|
|
11248
|
+
}, children: [
|
|
11249
|
+
"Add Card",
|
|
11250
|
+
/* @__PURE__ */ jsx145(ChevronRight2, { className: "h-4 w-4 ml-1" })
|
|
11251
|
+
] })
|
|
11252
|
+
] }) : defaultMethod?.card ? /* @__PURE__ */ jsxs87("div", { className: "space-y-2", children: [
|
|
11253
|
+
/* @__PURE__ */ jsxs87("p", { className: "text-xl font-bold", children: [
|
|
11254
|
+
getCardBrandIcon(defaultMethod.card.brand),
|
|
11255
|
+
" ****",
|
|
11256
|
+
defaultMethod.card.last4
|
|
11257
|
+
] }),
|
|
11258
|
+
/* @__PURE__ */ jsxs87("p", { className: "text-sm text-muted-foreground", children: [
|
|
11259
|
+
"Expires ",
|
|
11260
|
+
String(defaultMethod.card.expMonth).padStart(2, "0"),
|
|
11261
|
+
"/",
|
|
11262
|
+
defaultMethod.card.expYear
|
|
11263
|
+
] }),
|
|
11264
|
+
paymentMethods.length > 1 && /* @__PURE__ */ jsxs87("p", { className: "text-xs text-muted-foreground", children: [
|
|
11265
|
+
"+",
|
|
11266
|
+
paymentMethods.length - 1,
|
|
11267
|
+
" more card(s)"
|
|
11268
|
+
] })
|
|
11269
|
+
] }) : /* @__PURE__ */ jsxs87("div", { className: "space-y-2", children: [
|
|
11270
|
+
/* @__PURE__ */ jsx145("p", { className: "text-xl font-bold", children: defaultMethod?.type || "Payment Method" }),
|
|
11271
|
+
paymentMethods.length > 1 && /* @__PURE__ */ jsxs87("p", { className: "text-xs text-muted-foreground", children: [
|
|
11272
|
+
"+",
|
|
11273
|
+
paymentMethods.length - 1,
|
|
11274
|
+
" more method(s)"
|
|
11275
|
+
] })
|
|
11276
|
+
] }) })
|
|
11277
|
+
] });
|
|
11278
|
+
}
|
|
11279
|
+
__name(PaymentMethodSummaryCard, "PaymentMethodSummaryCard");
|
|
11280
|
+
|
|
11281
|
+
// src/features/billing/components/cards/CustomerInfoCard.tsx
|
|
11282
|
+
import { ExternalLink, User } from "lucide-react";
|
|
11283
|
+
import { useState as useState49 } from "react";
|
|
11284
|
+
import { jsx as jsx146, jsxs as jsxs88 } from "react/jsx-runtime";
|
|
11285
|
+
function formatBalance(balance, currency) {
|
|
11286
|
+
if (balance === void 0 || balance === 0) return "$0.00";
|
|
11287
|
+
const currencyCode = currency?.toUpperCase() || "USD";
|
|
11288
|
+
const displayBalance = -balance;
|
|
11289
|
+
return new Intl.NumberFormat(void 0, {
|
|
11290
|
+
style: "currency",
|
|
11291
|
+
currency: currencyCode
|
|
11292
|
+
}).format(displayBalance / 100);
|
|
11293
|
+
}
|
|
11294
|
+
__name(formatBalance, "formatBalance");
|
|
11295
|
+
function CustomerInfoCard({ customer, loading, error }) {
|
|
11296
|
+
const [portalLoading, setPortalLoading] = useState49(false);
|
|
11297
|
+
const handlePortalClick = /* @__PURE__ */ __name(async (e) => {
|
|
11298
|
+
e.stopPropagation();
|
|
11299
|
+
setPortalLoading(true);
|
|
11300
|
+
try {
|
|
11301
|
+
const { url } = await StripeCustomerService.createPortalSession();
|
|
11302
|
+
window.open(url, "_blank");
|
|
11303
|
+
} catch (err) {
|
|
11304
|
+
console.error("[CustomerInfoCard] Failed to create portal session:", err);
|
|
11305
|
+
} finally {
|
|
11306
|
+
setPortalLoading(false);
|
|
11307
|
+
}
|
|
11308
|
+
}, "handlePortalClick");
|
|
11309
|
+
if (loading) {
|
|
11310
|
+
return /* @__PURE__ */ jsxs88(Card, { children: [
|
|
11311
|
+
/* @__PURE__ */ jsxs88(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
|
|
11312
|
+
/* @__PURE__ */ jsx146(CardTitle, { className: "text-sm font-medium", children: "Billing Account" }),
|
|
11313
|
+
/* @__PURE__ */ jsx146(User, { className: "h-4 w-4 text-muted-foreground" })
|
|
11314
|
+
] }),
|
|
11315
|
+
/* @__PURE__ */ jsxs88(CardContent, { children: [
|
|
11316
|
+
/* @__PURE__ */ jsx146(Skeleton, { className: "h-6 w-32 mb-2" }),
|
|
11317
|
+
/* @__PURE__ */ jsx146(Skeleton, { className: "h-4 w-48 mb-1" }),
|
|
11318
|
+
/* @__PURE__ */ jsx146(Skeleton, { className: "h-4 w-24" })
|
|
11319
|
+
] })
|
|
11320
|
+
] });
|
|
11245
11321
|
}
|
|
11246
|
-
|
|
11247
|
-
|
|
11322
|
+
if (error) {
|
|
11323
|
+
return /* @__PURE__ */ jsxs88(Card, { children: [
|
|
11324
|
+
/* @__PURE__ */ jsxs88(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
|
|
11325
|
+
/* @__PURE__ */ jsx146(CardTitle, { className: "text-sm font-medium", children: "Billing Account" }),
|
|
11326
|
+
/* @__PURE__ */ jsx146(User, { className: "h-4 w-4 text-muted-foreground" })
|
|
11327
|
+
] }),
|
|
11328
|
+
/* @__PURE__ */ jsx146(CardContent, { children: /* @__PURE__ */ jsx146("p", { className: "text-sm text-destructive", children: error }) })
|
|
11329
|
+
] });
|
|
11248
11330
|
}
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
|
|
11252
|
-
/* @__PURE__ */
|
|
11253
|
-
/* @__PURE__ */
|
|
11254
|
-
|
|
11255
|
-
|
|
11256
|
-
|
|
11257
|
-
|
|
11258
|
-
|
|
11259
|
-
|
|
11260
|
-
)
|
|
11261
|
-
] }) });
|
|
11262
|
-
}
|
|
11263
|
-
return this.props.children;
|
|
11331
|
+
if (!customer) {
|
|
11332
|
+
return /* @__PURE__ */ jsxs88(Card, { children: [
|
|
11333
|
+
/* @__PURE__ */ jsxs88(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
|
|
11334
|
+
/* @__PURE__ */ jsx146(CardTitle, { className: "text-sm font-medium", children: "Billing Account" }),
|
|
11335
|
+
/* @__PURE__ */ jsx146(User, { className: "h-4 w-4 text-muted-foreground" })
|
|
11336
|
+
] }),
|
|
11337
|
+
/* @__PURE__ */ jsxs88(CardContent, { children: [
|
|
11338
|
+
/* @__PURE__ */ jsx146("p", { className: "text-xl font-bold text-muted-foreground", children: "Not set up" }),
|
|
11339
|
+
/* @__PURE__ */ jsx146("p", { className: "text-xs text-muted-foreground", children: "Billing account will be created when you subscribe" })
|
|
11340
|
+
] })
|
|
11341
|
+
] });
|
|
11264
11342
|
}
|
|
11265
|
-
|
|
11266
|
-
|
|
11267
|
-
|
|
11268
|
-
|
|
11269
|
-
|
|
11270
|
-
|
|
11271
|
-
|
|
11272
|
-
|
|
11273
|
-
|
|
11274
|
-
|
|
11275
|
-
|
|
11276
|
-
|
|
11277
|
-
|
|
11278
|
-
|
|
11279
|
-
|
|
11280
|
-
|
|
11343
|
+
return /* @__PURE__ */ jsxs88(Card, { children: [
|
|
11344
|
+
/* @__PURE__ */ jsxs88(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
|
|
11345
|
+
/* @__PURE__ */ jsx146(CardTitle, { className: "text-sm font-medium", children: "Billing Account" }),
|
|
11346
|
+
/* @__PURE__ */ jsx146(User, { className: "h-4 w-4 text-muted-foreground" })
|
|
11347
|
+
] }),
|
|
11348
|
+
/* @__PURE__ */ jsx146(CardContent, { children: /* @__PURE__ */ jsxs88("div", { className: "space-y-2", children: [
|
|
11349
|
+
customer.name && /* @__PURE__ */ jsx146("p", { className: "text-xl font-bold", children: customer.name }),
|
|
11350
|
+
customer.email && /* @__PURE__ */ jsx146("p", { className: "text-sm text-muted-foreground", children: customer.email }),
|
|
11351
|
+
customer.balance !== void 0 && customer.balance !== 0 && /* @__PURE__ */ jsxs88("p", { className: "text-sm", children: [
|
|
11352
|
+
/* @__PURE__ */ jsx146("span", { className: "text-muted-foreground", children: "Credit Balance: " }),
|
|
11353
|
+
/* @__PURE__ */ jsx146("span", { className: customer.balance < 0 ? "text-green-600" : "text-destructive", children: formatBalance(customer.balance, customer.currency) })
|
|
11354
|
+
] }),
|
|
11355
|
+
/* @__PURE__ */ jsxs88(Button, { variant: "outline", size: "sm", className: "mt-2", onClick: handlePortalClick, disabled: portalLoading, children: [
|
|
11356
|
+
portalLoading ? "Loading..." : "Manage in Stripe Portal",
|
|
11357
|
+
/* @__PURE__ */ jsx146(ExternalLink, { className: "h-4 w-4 ml-1" })
|
|
11358
|
+
] })
|
|
11359
|
+
] }) })
|
|
11360
|
+
] });
|
|
11361
|
+
}
|
|
11362
|
+
__name(CustomerInfoCard, "CustomerInfoCard");
|
|
11363
|
+
|
|
11364
|
+
// src/features/billing/components/cards/InvoicesSummaryCard.tsx
|
|
11365
|
+
import { ChevronRight as ChevronRight3, ReceiptIcon } from "lucide-react";
|
|
11366
|
+
import { jsx as jsx147, jsxs as jsxs89 } from "react/jsx-runtime";
|
|
11367
|
+
function getStatusBadgeVariant2(status) {
|
|
11368
|
+
switch (status) {
|
|
11369
|
+
case "paid" /* PAID */:
|
|
11370
|
+
return "default";
|
|
11371
|
+
case "open" /* OPEN */:
|
|
11372
|
+
return "secondary";
|
|
11373
|
+
case "uncollectible" /* UNCOLLECTIBLE */:
|
|
11374
|
+
case "void" /* VOID */:
|
|
11375
|
+
return "destructive";
|
|
11376
|
+
default:
|
|
11377
|
+
return "outline";
|
|
11378
|
+
}
|
|
11379
|
+
}
|
|
11380
|
+
__name(getStatusBadgeVariant2, "getStatusBadgeVariant");
|
|
11381
|
+
function formatDate2(date) {
|
|
11382
|
+
return new Date(date).toLocaleDateString(void 0, {
|
|
11383
|
+
year: "numeric",
|
|
11384
|
+
month: "short",
|
|
11385
|
+
day: "numeric"
|
|
11281
11386
|
});
|
|
11282
|
-
|
|
11283
|
-
|
|
11284
|
-
|
|
11285
|
-
|
|
11286
|
-
|
|
11287
|
-
|
|
11288
|
-
|
|
11289
|
-
|
|
11290
|
-
|
|
11387
|
+
}
|
|
11388
|
+
__name(formatDate2, "formatDate");
|
|
11389
|
+
function formatAmount(amount, currency) {
|
|
11390
|
+
const currencyCode = currency?.toUpperCase() || "USD";
|
|
11391
|
+
return new Intl.NumberFormat(void 0, {
|
|
11392
|
+
style: "currency",
|
|
11393
|
+
currency: currencyCode
|
|
11394
|
+
}).format(amount / 100);
|
|
11395
|
+
}
|
|
11396
|
+
__name(formatAmount, "formatAmount");
|
|
11397
|
+
function InvoicesSummaryCard({ invoices, loading, error, onViewAllClick }) {
|
|
11398
|
+
if (loading) {
|
|
11399
|
+
return /* @__PURE__ */ jsxs89(Card, { children: [
|
|
11400
|
+
/* @__PURE__ */ jsxs89(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
|
|
11401
|
+
/* @__PURE__ */ jsx147(CardTitle, { className: "text-sm font-medium", children: "Recent Invoices" }),
|
|
11402
|
+
/* @__PURE__ */ jsx147(ReceiptIcon, { className: "h-4 w-4 text-muted-foreground" })
|
|
11403
|
+
] }),
|
|
11404
|
+
/* @__PURE__ */ jsxs89(CardContent, { children: [
|
|
11405
|
+
/* @__PURE__ */ jsx147(Skeleton, { className: "h-6 w-24 mb-2" }),
|
|
11406
|
+
/* @__PURE__ */ jsx147(Skeleton, { className: "h-4 w-32 mb-1" }),
|
|
11407
|
+
/* @__PURE__ */ jsx147(Skeleton, { className: "h-4 w-20" })
|
|
11408
|
+
] })
|
|
11409
|
+
] });
|
|
11410
|
+
}
|
|
11411
|
+
if (error) {
|
|
11412
|
+
return /* @__PURE__ */ jsxs89(Card, { children: [
|
|
11413
|
+
/* @__PURE__ */ jsxs89(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
|
|
11414
|
+
/* @__PURE__ */ jsx147(CardTitle, { className: "text-sm font-medium", children: "Recent Invoices" }),
|
|
11415
|
+
/* @__PURE__ */ jsx147(ReceiptIcon, { className: "h-4 w-4 text-muted-foreground" })
|
|
11416
|
+
] }),
|
|
11417
|
+
/* @__PURE__ */ jsx147(CardContent, { children: /* @__PURE__ */ jsx147("p", { className: "text-sm text-destructive", children: error }) })
|
|
11418
|
+
] });
|
|
11419
|
+
}
|
|
11420
|
+
const latestInvoice = invoices[0];
|
|
11421
|
+
const paidInvoices = invoices.filter((inv) => inv.status === "paid" /* PAID */);
|
|
11422
|
+
const openInvoices = invoices.filter((inv) => inv.status === "open" /* OPEN */);
|
|
11423
|
+
return /* @__PURE__ */ jsxs89(Card, { className: "cursor-pointer hover:bg-accent/50 transition-colors", onClick: onViewAllClick, children: [
|
|
11424
|
+
/* @__PURE__ */ jsxs89(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
|
|
11425
|
+
/* @__PURE__ */ jsx147(CardTitle, { className: "text-sm font-medium", children: "Recent Invoices" }),
|
|
11426
|
+
/* @__PURE__ */ jsx147(ReceiptIcon, { className: "h-4 w-4 text-muted-foreground" })
|
|
11291
11427
|
] }),
|
|
11292
|
-
/* @__PURE__ */
|
|
11293
|
-
|
|
11294
|
-
|
|
11295
|
-
|
|
11296
|
-
|
|
11297
|
-
|
|
11298
|
-
|
|
11299
|
-
/* @__PURE__ */ jsx148("p", { className: "text-sm", children: t.rich(`foundations.notification.${notification.notificationType}.description`, {
|
|
11300
|
-
strong: /* @__PURE__ */ __name((chunks) => /* @__PURE__ */ jsx148("strong", { children: chunks }), "strong"),
|
|
11301
|
-
actor: notificationData.actor?.name ?? "",
|
|
11302
|
-
title: notificationData.title
|
|
11303
|
-
}) }),
|
|
11304
|
-
/* @__PURE__ */ jsx148("div", { className: "text-muted-foreground mt-1 w-full text-xs", children: new Date(notification.createdAt).toLocaleString() })
|
|
11428
|
+
/* @__PURE__ */ jsx147(CardContent, { children: invoices.length === 0 ? /* @__PURE__ */ jsxs89("div", { className: "space-y-2", children: [
|
|
11429
|
+
/* @__PURE__ */ jsx147("p", { className: "text-xl font-bold text-muted-foreground", children: "No invoices yet" }),
|
|
11430
|
+
/* @__PURE__ */ jsx147("p", { className: "text-xs text-muted-foreground", children: "Invoices will appear after your first billing cycle" })
|
|
11431
|
+
] }) : latestInvoice ? /* @__PURE__ */ jsxs89("div", { className: "space-y-2", children: [
|
|
11432
|
+
/* @__PURE__ */ jsxs89("div", { className: "flex items-center gap-2", children: [
|
|
11433
|
+
/* @__PURE__ */ jsx147("p", { className: "text-xl font-bold", children: formatAmount(latestInvoice.total, latestInvoice.currency) }),
|
|
11434
|
+
/* @__PURE__ */ jsx147(Badge, { variant: getStatusBadgeVariant2(latestInvoice.status), children: latestInvoice.status })
|
|
11305
11435
|
] }),
|
|
11306
|
-
/* @__PURE__ */
|
|
11307
|
-
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11311
|
-
|
|
11312
|
-
|
|
11313
|
-
|
|
11314
|
-
|
|
11315
|
-
|
|
11316
|
-
|
|
11317
|
-
|
|
11318
|
-
|
|
11319
|
-
children: /* @__PURE__ */ jsx148(ArchiveIcon, { className: "h-4 w-4 cursor-pointer" })
|
|
11320
|
-
}
|
|
11321
|
-
) }),
|
|
11322
|
-
/* @__PURE__ */ jsx148(TooltipContent, { children: t(`foundations.notification.buttons.archive`) })
|
|
11436
|
+
/* @__PURE__ */ jsx147("p", { className: "text-sm text-muted-foreground", children: latestInvoice.stripeInvoiceNumber || `Invoice from ${formatDate2(latestInvoice.periodStart)}` }),
|
|
11437
|
+
/* @__PURE__ */ jsxs89("div", { className: "flex items-center gap-4 text-xs text-muted-foreground", children: [
|
|
11438
|
+
paidInvoices.length > 0 && /* @__PURE__ */ jsxs89("span", { children: [
|
|
11439
|
+
paidInvoices.length,
|
|
11440
|
+
" paid"
|
|
11441
|
+
] }),
|
|
11442
|
+
openInvoices.length > 0 && /* @__PURE__ */ jsxs89("span", { className: "text-orange-600", children: [
|
|
11443
|
+
openInvoices.length,
|
|
11444
|
+
" open"
|
|
11445
|
+
] }),
|
|
11446
|
+
/* @__PURE__ */ jsxs89("span", { className: "flex items-center", children: [
|
|
11447
|
+
"View all",
|
|
11448
|
+
/* @__PURE__ */ jsx147(ChevronRight3, { className: "h-3 w-3 ml-1" })
|
|
11323
11449
|
] })
|
|
11324
11450
|
] })
|
|
11325
|
-
] })
|
|
11326
|
-
|
|
11451
|
+
] }) : null })
|
|
11452
|
+
] });
|
|
11327
11453
|
}
|
|
11328
|
-
__name(
|
|
11329
|
-
|
|
11330
|
-
// src/features/
|
|
11331
|
-
import {
|
|
11332
|
-
|
|
11333
|
-
|
|
11334
|
-
|
|
11454
|
+
__name(InvoicesSummaryCard, "InvoicesSummaryCard");
|
|
11455
|
+
|
|
11456
|
+
// src/features/billing/components/cards/BillingUsageSummaryCard.tsx
|
|
11457
|
+
import { Activity, ChevronRight as ChevronRight4 } from "lucide-react";
|
|
11458
|
+
import { jsx as jsx148, jsxs as jsxs90 } from "react/jsx-runtime";
|
|
11459
|
+
function formatNumber(value) {
|
|
11460
|
+
return new Intl.NumberFormat(void 0, {
|
|
11461
|
+
notation: "compact",
|
|
11462
|
+
compactDisplay: "short"
|
|
11463
|
+
}).format(value);
|
|
11464
|
+
}
|
|
11465
|
+
__name(formatNumber, "formatNumber");
|
|
11466
|
+
function BillingUsageSummaryCard({
|
|
11467
|
+
meters,
|
|
11468
|
+
summaries,
|
|
11469
|
+
loading,
|
|
11470
|
+
error,
|
|
11471
|
+
onViewDetailsClick
|
|
11472
|
+
}) {
|
|
11473
|
+
if (loading) {
|
|
11474
|
+
return /* @__PURE__ */ jsxs90(Card, { children: [
|
|
11475
|
+
/* @__PURE__ */ jsxs90(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
|
|
11476
|
+
/* @__PURE__ */ jsx148(CardTitle, { className: "text-sm font-medium", children: "Usage This Month" }),
|
|
11477
|
+
/* @__PURE__ */ jsx148(Activity, { className: "h-4 w-4 text-muted-foreground" })
|
|
11478
|
+
] }),
|
|
11479
|
+
/* @__PURE__ */ jsxs90(CardContent, { children: [
|
|
11480
|
+
/* @__PURE__ */ jsx148(Skeleton, { className: "h-6 w-24 mb-2" }),
|
|
11481
|
+
/* @__PURE__ */ jsx148(Skeleton, { className: "h-4 w-32" })
|
|
11482
|
+
] })
|
|
11483
|
+
] });
|
|
11484
|
+
}
|
|
11335
11485
|
if (error) {
|
|
11336
|
-
return /* @__PURE__ */
|
|
11337
|
-
/* @__PURE__ */
|
|
11338
|
-
"
|
|
11339
|
-
|
|
11486
|
+
return /* @__PURE__ */ jsxs90(Card, { children: [
|
|
11487
|
+
/* @__PURE__ */ jsxs90(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
|
|
11488
|
+
/* @__PURE__ */ jsx148(CardTitle, { className: "text-sm font-medium", children: "Usage This Month" }),
|
|
11489
|
+
/* @__PURE__ */ jsx148(Activity, { className: "h-4 w-4 text-muted-foreground" })
|
|
11340
11490
|
] }),
|
|
11341
|
-
/* @__PURE__ */
|
|
11342
|
-
] })
|
|
11491
|
+
/* @__PURE__ */ jsx148(CardContent, { children: /* @__PURE__ */ jsx148("p", { className: "text-sm text-destructive", children: error }) })
|
|
11492
|
+
] });
|
|
11343
11493
|
}
|
|
11344
|
-
const
|
|
11345
|
-
|
|
11346
|
-
|
|
11347
|
-
|
|
11348
|
-
|
|
11349
|
-
|
|
11350
|
-
|
|
11351
|
-
|
|
11494
|
+
const totalUsage = Object.values(summaries).reduce((acc, summary) => {
|
|
11495
|
+
return acc + (summary?.aggregatedValue || 0);
|
|
11496
|
+
}, 0);
|
|
11497
|
+
const primaryMeter = meters.find((m) => summaries[m.id]?.aggregatedValue);
|
|
11498
|
+
const primarySummary = primaryMeter ? summaries[primaryMeter.id] : null;
|
|
11499
|
+
return /* @__PURE__ */ jsxs90(Card, { className: "cursor-pointer hover:bg-accent/50 transition-colors", onClick: onViewDetailsClick, children: [
|
|
11500
|
+
/* @__PURE__ */ jsxs90(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2", children: [
|
|
11501
|
+
/* @__PURE__ */ jsx148(CardTitle, { className: "text-sm font-medium", children: "Usage This Month" }),
|
|
11502
|
+
/* @__PURE__ */ jsx148(Activity, { className: "h-4 w-4 text-muted-foreground" })
|
|
11503
|
+
] }),
|
|
11504
|
+
/* @__PURE__ */ jsx148(CardContent, { children: meters.length === 0 ? /* @__PURE__ */ jsxs90("div", { className: "space-y-2", children: [
|
|
11505
|
+
/* @__PURE__ */ jsx148("p", { className: "text-xl font-bold text-muted-foreground", children: "No meters" }),
|
|
11506
|
+
/* @__PURE__ */ jsx148("p", { className: "text-xs text-muted-foreground", children: "No usage meters are configured" })
|
|
11507
|
+
] }) : /* @__PURE__ */ jsxs90("div", { className: "space-y-2", children: [
|
|
11508
|
+
/* @__PURE__ */ jsxs90("p", { className: "text-xl font-bold", children: [
|
|
11509
|
+
formatNumber(totalUsage),
|
|
11510
|
+
" units"
|
|
11511
|
+
] }),
|
|
11512
|
+
primaryMeter && primarySummary && /* @__PURE__ */ jsxs90("p", { className: "text-sm text-muted-foreground", children: [
|
|
11513
|
+
primaryMeter.displayName,
|
|
11514
|
+
": ",
|
|
11515
|
+
formatNumber(primarySummary.aggregatedValue)
|
|
11516
|
+
] }),
|
|
11517
|
+
meters.length > 1 && /* @__PURE__ */ jsxs90("p", { className: "text-xs text-muted-foreground", children: [
|
|
11518
|
+
"Across ",
|
|
11519
|
+
meters.length,
|
|
11520
|
+
" meters"
|
|
11521
|
+
] }),
|
|
11522
|
+
/* @__PURE__ */ jsxs90("span", { className: "flex items-center text-xs text-muted-foreground", children: [
|
|
11523
|
+
"View details",
|
|
11524
|
+
/* @__PURE__ */ jsx148(ChevronRight4, { className: "h-3 w-3 ml-1" })
|
|
11525
|
+
] })
|
|
11526
|
+
] }) })
|
|
11527
|
+
] });
|
|
11528
|
+
}
|
|
11529
|
+
__name(BillingUsageSummaryCard, "BillingUsageSummaryCard");
|
|
11530
|
+
|
|
11531
|
+
// src/features/billing/components/containers/BillingDashboardContainer.tsx
|
|
11532
|
+
import { CreditCard as CreditCard4, Loader2 as Loader24, Wallet as Wallet2 } from "lucide-react";
|
|
11533
|
+
import { useCallback as useCallback18, useEffect as useEffect47, useState as useState62 } from "react";
|
|
11534
|
+
|
|
11535
|
+
// src/features/billing/stripe-customer/components/containers/PaymentMethodsContainer.tsx
|
|
11536
|
+
import { CreditCard as CreditCard2 } from "lucide-react";
|
|
11537
|
+
import { useEffect as useEffect42, useState as useState52 } from "react";
|
|
11538
|
+
|
|
11539
|
+
// src/features/billing/stripe-customer/components/forms/PaymentMethodEditor.tsx
|
|
11540
|
+
import { CardElement, useElements, useStripe } from "@stripe/react-stripe-js";
|
|
11541
|
+
import { useEffect as useEffect40, useState as useState50 } from "react";
|
|
11542
|
+
import { jsx as jsx149, jsxs as jsxs91 } from "react/jsx-runtime";
|
|
11543
|
+
function PaymentMethodEditor({ open, onOpenChange, onSuccess }) {
|
|
11544
|
+
const stripe = useStripe();
|
|
11545
|
+
const elements = useElements();
|
|
11546
|
+
const [setupIntent, setSetupIntent] = useState50(null);
|
|
11547
|
+
const [loading, setLoading] = useState50(true);
|
|
11548
|
+
const [isSubmitting, setIsSubmitting] = useState50(false);
|
|
11549
|
+
const [error, setError] = useState50(null);
|
|
11550
|
+
const [setAsDefault, setSetAsDefault] = useState50(true);
|
|
11551
|
+
useEffect40(() => {
|
|
11552
|
+
const fetchSetupIntent = /* @__PURE__ */ __name(async () => {
|
|
11553
|
+
setLoading(true);
|
|
11554
|
+
try {
|
|
11555
|
+
const intent = await StripeCustomerService.createSetupIntent();
|
|
11556
|
+
setSetupIntent(intent);
|
|
11557
|
+
} catch (err) {
|
|
11558
|
+
console.error("[PaymentMethodEditor] Failed to create setup intent:", err);
|
|
11559
|
+
setError("Failed to initialize payment form. Please try again.");
|
|
11560
|
+
} finally {
|
|
11561
|
+
setLoading(false);
|
|
11562
|
+
}
|
|
11563
|
+
}, "fetchSetupIntent");
|
|
11564
|
+
if (open) {
|
|
11565
|
+
fetchSetupIntent();
|
|
11566
|
+
}
|
|
11567
|
+
}, [open]);
|
|
11568
|
+
const handleSubmit = /* @__PURE__ */ __name(async (e) => {
|
|
11569
|
+
e.preventDefault();
|
|
11570
|
+
if (!stripe || !elements || !setupIntent) {
|
|
11571
|
+
return;
|
|
11572
|
+
}
|
|
11573
|
+
setIsSubmitting(true);
|
|
11574
|
+
setError(null);
|
|
11575
|
+
try {
|
|
11576
|
+
const cardElement = elements.getElement(CardElement);
|
|
11577
|
+
if (!cardElement) {
|
|
11578
|
+
throw new Error("Card element not found");
|
|
11579
|
+
}
|
|
11580
|
+
const { error: stripeError, setupIntent: confirmedSetupIntent } = await stripe.confirmCardSetup(
|
|
11581
|
+
setupIntent.clientSecret,
|
|
11582
|
+
{
|
|
11583
|
+
payment_method: {
|
|
11584
|
+
card: cardElement
|
|
11585
|
+
}
|
|
11586
|
+
}
|
|
11587
|
+
);
|
|
11588
|
+
if (stripeError) {
|
|
11589
|
+
console.error("[PaymentMethodEditor] Stripe error:", stripeError);
|
|
11590
|
+
setError(stripeError.message || "Failed to add payment method. Please check your card details.");
|
|
11591
|
+
setIsSubmitting(false);
|
|
11592
|
+
return;
|
|
11593
|
+
}
|
|
11594
|
+
if (setAsDefault && confirmedSetupIntent?.payment_method) {
|
|
11595
|
+
await StripeCustomerService.setDefaultPaymentMethod({
|
|
11596
|
+
paymentMethodId: typeof confirmedSetupIntent.payment_method === "string" ? confirmedSetupIntent.payment_method : confirmedSetupIntent.payment_method.id
|
|
11597
|
+
});
|
|
11598
|
+
}
|
|
11599
|
+
onSuccess();
|
|
11600
|
+
onOpenChange(false);
|
|
11601
|
+
} catch (err) {
|
|
11602
|
+
console.error("[PaymentMethodEditor] Error:", err);
|
|
11603
|
+
setError(err.message || "An unexpected error occurred. Please try again.");
|
|
11604
|
+
} finally {
|
|
11605
|
+
setIsSubmitting(false);
|
|
11606
|
+
}
|
|
11607
|
+
}, "handleSubmit");
|
|
11608
|
+
return /* @__PURE__ */ jsx149(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs91(DialogContent, { className: "max-w-md", children: [
|
|
11609
|
+
/* @__PURE__ */ jsxs91(DialogHeader, { children: [
|
|
11610
|
+
/* @__PURE__ */ jsx149(DialogTitle, { children: "Add Payment Method" }),
|
|
11611
|
+
/* @__PURE__ */ jsx149(DialogDescription, { children: "Add a new payment method to your account. Your card information is securely processed by Stripe." })
|
|
11612
|
+
] }),
|
|
11613
|
+
loading && /* @__PURE__ */ jsx149("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx149("p", { className: "text-muted-foreground", children: "Loading payment form..." }) }),
|
|
11614
|
+
!loading && setupIntent && /* @__PURE__ */ jsxs91("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
11615
|
+
/* @__PURE__ */ jsx149("div", { className: "rounded-md border border-gray-300 p-3", children: /* @__PURE__ */ jsx149(
|
|
11616
|
+
CardElement,
|
|
11617
|
+
{
|
|
11618
|
+
options: {
|
|
11619
|
+
style: {
|
|
11620
|
+
base: {
|
|
11621
|
+
fontSize: "16px",
|
|
11622
|
+
color: "#424770",
|
|
11623
|
+
"::placeholder": {
|
|
11624
|
+
color: "#aab7c4"
|
|
11625
|
+
}
|
|
11626
|
+
},
|
|
11627
|
+
invalid: {
|
|
11628
|
+
color: "#9e2146"
|
|
11629
|
+
}
|
|
11630
|
+
}
|
|
11631
|
+
}
|
|
11632
|
+
}
|
|
11633
|
+
) }),
|
|
11634
|
+
/* @__PURE__ */ jsxs91("div", { className: "flex items-center gap-x-2", children: [
|
|
11635
|
+
/* @__PURE__ */ jsx149(
|
|
11636
|
+
Checkbox,
|
|
11637
|
+
{
|
|
11638
|
+
id: "setAsDefault",
|
|
11639
|
+
checked: setAsDefault,
|
|
11640
|
+
onCheckedChange: (checked) => setSetAsDefault(!!checked)
|
|
11641
|
+
}
|
|
11642
|
+
),
|
|
11643
|
+
/* @__PURE__ */ jsx149(Label3, { htmlFor: "setAsDefault", className: "text-sm font-normal", children: "Set as default payment method" })
|
|
11644
|
+
] }),
|
|
11645
|
+
error && /* @__PURE__ */ jsx149(Alert, { variant: "destructive", className: "bg-red-50 border-red-200", children: /* @__PURE__ */ jsx149(AlertDescription, { children: error }) }),
|
|
11646
|
+
/* @__PURE__ */ jsxs91("div", { className: "flex justify-end gap-x-2", children: [
|
|
11647
|
+
/* @__PURE__ */ jsx149(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), disabled: isSubmitting, children: "Cancel" }),
|
|
11648
|
+
/* @__PURE__ */ jsx149(Button, { type: "submit", disabled: !stripe || isSubmitting, children: isSubmitting ? "Processing..." : "Add Card" })
|
|
11649
|
+
] })
|
|
11650
|
+
] }),
|
|
11651
|
+
!loading && !setupIntent && error && /* @__PURE__ */ jsx149(Alert, { variant: "destructive", className: "bg-red-50 border-red-200", children: /* @__PURE__ */ jsx149(AlertDescription, { children: error }) })
|
|
11652
|
+
] }) });
|
|
11653
|
+
}
|
|
11654
|
+
__name(PaymentMethodEditor, "PaymentMethodEditor");
|
|
11655
|
+
|
|
11656
|
+
// src/features/billing/stripe-customer/components/details/PaymentMethodCard.tsx
|
|
11657
|
+
import { MoreVertical } from "lucide-react";
|
|
11658
|
+
import { useEffect as useEffect41, useState as useState51 } from "react";
|
|
11659
|
+
import { Fragment as Fragment28, jsx as jsx150, jsxs as jsxs92 } from "react/jsx-runtime";
|
|
11660
|
+
var brandIcons = {
|
|
11661
|
+
visa: "\u{1F4B3}",
|
|
11662
|
+
mastercard: "\u{1F4B3}",
|
|
11663
|
+
amex: "\u{1F4B3}",
|
|
11664
|
+
discover: "\u{1F4B3}"
|
|
11665
|
+
};
|
|
11666
|
+
function PaymentMethodCard({ paymentMethod, onUpdate }) {
|
|
11667
|
+
const [loading, setLoading] = useState51(false);
|
|
11668
|
+
const [customer, setCustomer] = useState51(null);
|
|
11669
|
+
const [showRemoveDialog, setShowRemoveDialog] = useState51(false);
|
|
11670
|
+
useEffect41(() => {
|
|
11671
|
+
const loadCustomer = /* @__PURE__ */ __name(async () => {
|
|
11672
|
+
try {
|
|
11673
|
+
const fetchedCustomer = await StripeCustomerService.getCustomer();
|
|
11674
|
+
setCustomer(fetchedCustomer);
|
|
11675
|
+
} catch (error) {
|
|
11676
|
+
console.error("[PaymentMethodCard] Failed to load customer:", error);
|
|
11677
|
+
}
|
|
11678
|
+
}, "loadCustomer");
|
|
11679
|
+
loadCustomer();
|
|
11680
|
+
}, []);
|
|
11681
|
+
const isDefault = customer?.defaultPaymentMethodId === paymentMethod.id;
|
|
11682
|
+
const brand = paymentMethod.card?.brand || "card";
|
|
11683
|
+
const last4 = paymentMethod.card?.last4 || "****";
|
|
11684
|
+
const expMonth = paymentMethod.card?.expMonth || 0;
|
|
11685
|
+
const expYear = paymentMethod.card?.expYear || 0;
|
|
11686
|
+
const brandIcon = brandIcons[brand.toLowerCase()] || "\u{1F4B3}";
|
|
11687
|
+
const handleSetDefault = /* @__PURE__ */ __name(async () => {
|
|
11688
|
+
setLoading(true);
|
|
11689
|
+
try {
|
|
11690
|
+
await StripeCustomerService.setDefaultPaymentMethod({ paymentMethodId: paymentMethod.id });
|
|
11691
|
+
onUpdate();
|
|
11692
|
+
} catch (error) {
|
|
11693
|
+
console.error("[PaymentMethodCard] Failed to set as default:", error);
|
|
11694
|
+
} finally {
|
|
11695
|
+
setLoading(false);
|
|
11696
|
+
}
|
|
11697
|
+
}, "handleSetDefault");
|
|
11698
|
+
const handleRemove = /* @__PURE__ */ __name(async () => {
|
|
11699
|
+
setLoading(true);
|
|
11700
|
+
try {
|
|
11701
|
+
await StripeCustomerService.removePaymentMethod({ paymentMethodId: paymentMethod.id });
|
|
11702
|
+
setShowRemoveDialog(false);
|
|
11703
|
+
onUpdate();
|
|
11704
|
+
} catch (error) {
|
|
11705
|
+
console.error("[PaymentMethodCard] Failed to remove:", error);
|
|
11706
|
+
setLoading(false);
|
|
11707
|
+
}
|
|
11708
|
+
}, "handleRemove");
|
|
11709
|
+
return /* @__PURE__ */ jsxs92(Fragment28, { children: [
|
|
11710
|
+
/* @__PURE__ */ jsxs92(Card, { className: "relative", children: [
|
|
11711
|
+
isDefault && /* @__PURE__ */ jsx150(Badge, { className: "absolute right-2 top-2 bg-green-100 text-green-800 hover:bg-green-100", children: "Default" }),
|
|
11712
|
+
/* @__PURE__ */ jsxs92(CardHeader, { className: "flex flex-row items-center justify-between pb-2", children: [
|
|
11713
|
+
/* @__PURE__ */ jsxs92("div", { className: "flex items-center gap-x-2", children: [
|
|
11714
|
+
/* @__PURE__ */ jsx150("span", { className: "text-2xl", children: brandIcon }),
|
|
11715
|
+
/* @__PURE__ */ jsx150("span", { className: "text-sm font-medium capitalize", children: brand })
|
|
11716
|
+
] }),
|
|
11717
|
+
/* @__PURE__ */ jsxs92(DropdownMenu, { children: [
|
|
11718
|
+
/* @__PURE__ */ jsx150(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx150(Button, { variant: "ghost", size: "sm", disabled: loading, className: "h-8 w-8 p-0", children: /* @__PURE__ */ jsx150(MoreVertical, { className: "h-4 w-4" }) }) }),
|
|
11719
|
+
/* @__PURE__ */ jsxs92(DropdownMenuContent, { align: "end", children: [
|
|
11720
|
+
!isDefault && /* @__PURE__ */ jsx150(DropdownMenuItem, { onClick: handleSetDefault, disabled: loading, children: "Set as Default" }),
|
|
11721
|
+
/* @__PURE__ */ jsx150(DropdownMenuItem, { onClick: () => setShowRemoveDialog(true), disabled: loading, className: "text-red-600", children: "Remove" })
|
|
11722
|
+
] })
|
|
11723
|
+
] })
|
|
11724
|
+
] }),
|
|
11725
|
+
/* @__PURE__ */ jsx150(CardContent, { children: /* @__PURE__ */ jsxs92("div", { className: "flex flex-col gap-y-1", children: [
|
|
11726
|
+
/* @__PURE__ */ jsxs92("p", { className: "text-lg font-semibold", children: [
|
|
11727
|
+
"\u2022\u2022\u2022\u2022 ",
|
|
11728
|
+
last4
|
|
11729
|
+
] }),
|
|
11730
|
+
/* @__PURE__ */ jsxs92("p", { className: "text-sm text-muted-foreground", children: [
|
|
11731
|
+
"Expires ",
|
|
11732
|
+
String(expMonth).padStart(2, "0"),
|
|
11733
|
+
"/",
|
|
11734
|
+
expYear
|
|
11735
|
+
] })
|
|
11736
|
+
] }) })
|
|
11737
|
+
] }),
|
|
11738
|
+
/* @__PURE__ */ jsx150(AlertDialog, { open: showRemoveDialog, onOpenChange: setShowRemoveDialog, children: /* @__PURE__ */ jsxs92(AlertDialogContent, { children: [
|
|
11739
|
+
/* @__PURE__ */ jsxs92(AlertDialogHeader, { children: [
|
|
11740
|
+
/* @__PURE__ */ jsx150(AlertDialogTitle, { children: "Remove Payment Method" }),
|
|
11741
|
+
/* @__PURE__ */ jsxs92(AlertDialogDescription, { children: [
|
|
11742
|
+
"Are you sure you want to remove this payment method? This action cannot be undone.",
|
|
11743
|
+
isDefault && " This is your default payment method."
|
|
11744
|
+
] })
|
|
11745
|
+
] }),
|
|
11746
|
+
/* @__PURE__ */ jsxs92(AlertDialogFooter, { children: [
|
|
11747
|
+
/* @__PURE__ */ jsx150(AlertDialogCancel, { disabled: loading, children: "Cancel" }),
|
|
11748
|
+
/* @__PURE__ */ jsx150(AlertDialogAction, { onClick: handleRemove, disabled: loading, className: "bg-red-600 hover:bg-red-700", children: loading ? "Removing..." : "Remove" })
|
|
11749
|
+
] })
|
|
11750
|
+
] }) })
|
|
11751
|
+
] });
|
|
11752
|
+
}
|
|
11753
|
+
__name(PaymentMethodCard, "PaymentMethodCard");
|
|
11754
|
+
|
|
11755
|
+
// src/features/billing/stripe-customer/components/lists/PaymentMethodsList.tsx
|
|
11756
|
+
import { jsx as jsx151 } from "react/jsx-runtime";
|
|
11757
|
+
function PaymentMethodsList({ paymentMethods, onUpdate }) {
|
|
11758
|
+
return /* @__PURE__ */ jsx151("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3", children: paymentMethods.map((paymentMethod) => /* @__PURE__ */ jsx151(PaymentMethodCard, { paymentMethod, onUpdate }, paymentMethod.id)) });
|
|
11759
|
+
}
|
|
11760
|
+
__name(PaymentMethodsList, "PaymentMethodsList");
|
|
11761
|
+
|
|
11762
|
+
// src/features/billing/stripe-customer/components/containers/PaymentMethodsContainer.tsx
|
|
11763
|
+
import { jsx as jsx152, jsxs as jsxs93 } from "react/jsx-runtime";
|
|
11764
|
+
function PaymentMethodsContainer() {
|
|
11765
|
+
const [paymentMethods, setPaymentMethods] = useState52([]);
|
|
11766
|
+
const [loading, setLoading] = useState52(true);
|
|
11767
|
+
const [showAddPaymentMethod, setShowAddPaymentMethod] = useState52(false);
|
|
11768
|
+
const loadPaymentMethods = /* @__PURE__ */ __name(async () => {
|
|
11769
|
+
setLoading(true);
|
|
11770
|
+
try {
|
|
11771
|
+
const fetchedPaymentMethods = await StripeCustomerService.listPaymentMethods();
|
|
11772
|
+
setPaymentMethods(fetchedPaymentMethods);
|
|
11773
|
+
} catch (error) {
|
|
11774
|
+
console.error("[PaymentMethodsContainer] Failed to load payment methods:", error);
|
|
11775
|
+
} finally {
|
|
11776
|
+
setLoading(false);
|
|
11777
|
+
}
|
|
11778
|
+
}, "loadPaymentMethods");
|
|
11779
|
+
useEffect42(() => {
|
|
11780
|
+
loadPaymentMethods();
|
|
11781
|
+
}, []);
|
|
11782
|
+
if (loading) {
|
|
11783
|
+
return /* @__PURE__ */ jsx152("div", { className: "flex h-64 items-center justify-center", children: /* @__PURE__ */ jsx152("p", { className: "text-muted-foreground", children: "Loading payment methods..." }) });
|
|
11784
|
+
}
|
|
11785
|
+
return /* @__PURE__ */ jsxs93("div", { className: "flex w-full flex-col gap-y-6", children: [
|
|
11786
|
+
/* @__PURE__ */ jsxs93("div", { className: "flex items-center justify-between", children: [
|
|
11787
|
+
/* @__PURE__ */ jsxs93("div", { className: "flex items-center gap-x-3", children: [
|
|
11788
|
+
/* @__PURE__ */ jsx152(CreditCard2, { className: "h-8 w-8" }),
|
|
11789
|
+
/* @__PURE__ */ jsx152("h1", { className: "text-3xl font-bold", children: "Payment Methods" })
|
|
11790
|
+
] }),
|
|
11791
|
+
/* @__PURE__ */ jsx152(Button, { onClick: () => setShowAddPaymentMethod(true), children: "Add Payment Method" })
|
|
11792
|
+
] }),
|
|
11793
|
+
paymentMethods.length === 0 && /* @__PURE__ */ jsxs93("div", { className: "flex flex-col items-center justify-center gap-y-4 rounded-lg border-2 border-dashed border-gray-300 bg-muted/50 p-12", children: [
|
|
11794
|
+
/* @__PURE__ */ jsx152(CreditCard2, { className: "h-16 w-16 text-muted-foreground" }),
|
|
11795
|
+
/* @__PURE__ */ jsxs93("div", { className: "text-center", children: [
|
|
11796
|
+
/* @__PURE__ */ jsx152("h3", { className: "mb-2 text-xl font-semibold", children: "No payment methods" }),
|
|
11797
|
+
/* @__PURE__ */ jsx152("p", { className: "mb-4 text-muted-foreground", children: "Add a payment method to enable subscriptions and secure checkout." }),
|
|
11798
|
+
/* @__PURE__ */ jsx152(Button, { onClick: () => setShowAddPaymentMethod(true), children: "Add Your First Card" })
|
|
11799
|
+
] })
|
|
11800
|
+
] }),
|
|
11801
|
+
paymentMethods.length > 0 && /* @__PURE__ */ jsx152(PaymentMethodsList, { paymentMethods, onUpdate: loadPaymentMethods }),
|
|
11802
|
+
showAddPaymentMethod && /* @__PURE__ */ jsx152(
|
|
11803
|
+
PaymentMethodEditor,
|
|
11804
|
+
{
|
|
11805
|
+
open: showAddPaymentMethod,
|
|
11806
|
+
onOpenChange: setShowAddPaymentMethod,
|
|
11807
|
+
onSuccess: loadPaymentMethods
|
|
11808
|
+
}
|
|
11809
|
+
)
|
|
11810
|
+
] });
|
|
11811
|
+
}
|
|
11812
|
+
__name(PaymentMethodsContainer, "PaymentMethodsContainer");
|
|
11813
|
+
|
|
11814
|
+
// src/features/billing/stripe-invoice/components/containers/InvoicesContainer.tsx
|
|
11815
|
+
import { useEffect as useEffect43, useState as useState54 } from "react";
|
|
11816
|
+
|
|
11817
|
+
// src/features/billing/stripe-invoice/components/lists/InvoicesList.tsx
|
|
11818
|
+
import { useState as useState53 } from "react";
|
|
11819
|
+
|
|
11820
|
+
// src/features/billing/components/utils/currency.ts
|
|
11821
|
+
function formatInterval(price) {
|
|
11822
|
+
if (price.priceType === "one_time" || !price.recurring) {
|
|
11823
|
+
return "one-time";
|
|
11824
|
+
}
|
|
11825
|
+
const { interval, intervalCount } = price.recurring;
|
|
11826
|
+
if (intervalCount === 1) {
|
|
11827
|
+
return `/${interval}`;
|
|
11828
|
+
}
|
|
11829
|
+
const pluralInterval = interval === "day" ? "days" : interval === "week" ? "weeks" : interval === "month" ? "months" : "years";
|
|
11830
|
+
return `/${intervalCount} ${pluralInterval}`;
|
|
11831
|
+
}
|
|
11832
|
+
__name(formatInterval, "formatInterval");
|
|
11833
|
+
function formatCurrency(amount, currency) {
|
|
11834
|
+
if (amount === void 0) return "$0.00";
|
|
11835
|
+
const dollars = amount / 100;
|
|
11836
|
+
try {
|
|
11837
|
+
return new Intl.NumberFormat("en-US", {
|
|
11838
|
+
style: "currency",
|
|
11839
|
+
currency: currency.toUpperCase(),
|
|
11840
|
+
minimumFractionDigits: 2,
|
|
11841
|
+
maximumFractionDigits: 2
|
|
11842
|
+
}).format(dollars);
|
|
11843
|
+
} catch (error) {
|
|
11844
|
+
console.error("Error formatting currency:", error);
|
|
11845
|
+
return `$${dollars.toFixed(2)}`;
|
|
11846
|
+
}
|
|
11847
|
+
}
|
|
11848
|
+
__name(formatCurrency, "formatCurrency");
|
|
11849
|
+
|
|
11850
|
+
// src/features/billing/components/utils/date.ts
|
|
11851
|
+
function formatDate3(date) {
|
|
11852
|
+
if (!date) return "N/A";
|
|
11853
|
+
const dateObj = typeof date === "string" ? new Date(date) : date;
|
|
11854
|
+
try {
|
|
11855
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
11856
|
+
month: "short",
|
|
11857
|
+
day: "numeric",
|
|
11858
|
+
year: "numeric"
|
|
11859
|
+
}).format(dateObj);
|
|
11860
|
+
} catch (error) {
|
|
11861
|
+
console.error("Error formatting date:", error);
|
|
11862
|
+
return "Invalid Date";
|
|
11863
|
+
}
|
|
11864
|
+
}
|
|
11865
|
+
__name(formatDate3, "formatDate");
|
|
11866
|
+
|
|
11867
|
+
// src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx
|
|
11868
|
+
import { Download, ExternalLink as ExternalLink2, RefreshCw as RefreshCw2 } from "lucide-react";
|
|
11869
|
+
|
|
11870
|
+
// src/features/billing/stripe-invoice/components/widgets/InvoiceStatusBadge.tsx
|
|
11871
|
+
import { jsx as jsx153 } from "react/jsx-runtime";
|
|
11872
|
+
var statusConfig = {
|
|
11873
|
+
["draft" /* DRAFT */]: {
|
|
11874
|
+
label: "Draft",
|
|
11875
|
+
color: "bg-gray-100 text-gray-800"
|
|
11876
|
+
},
|
|
11877
|
+
["open" /* OPEN */]: {
|
|
11878
|
+
label: "Open",
|
|
11879
|
+
color: "bg-blue-100 text-blue-800"
|
|
11880
|
+
},
|
|
11881
|
+
["paid" /* PAID */]: {
|
|
11882
|
+
label: "Paid",
|
|
11883
|
+
color: "bg-green-100 text-green-800"
|
|
11884
|
+
},
|
|
11885
|
+
["void" /* VOID */]: {
|
|
11886
|
+
label: "Void",
|
|
11887
|
+
color: "bg-gray-100 text-gray-800"
|
|
11888
|
+
},
|
|
11889
|
+
["uncollectible" /* UNCOLLECTIBLE */]: {
|
|
11890
|
+
label: "Uncollectible",
|
|
11891
|
+
color: "bg-red-100 text-red-800"
|
|
11892
|
+
}
|
|
11893
|
+
};
|
|
11894
|
+
function InvoiceStatusBadge({ status }) {
|
|
11895
|
+
const config = statusConfig[status] || statusConfig["draft" /* DRAFT */];
|
|
11896
|
+
return /* @__PURE__ */ jsx153("span", { className: `${config.color} text-xs px-2 py-1 rounded-full font-medium`, children: config.label });
|
|
11897
|
+
}
|
|
11898
|
+
__name(InvoiceStatusBadge, "InvoiceStatusBadge");
|
|
11899
|
+
|
|
11900
|
+
// src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx
|
|
11901
|
+
import { jsx as jsx154, jsxs as jsxs94 } from "react/jsx-runtime";
|
|
11902
|
+
function InvoiceDetails({ invoice, open, onOpenChange, onInvoiceChange }) {
|
|
11903
|
+
const handleDownloadPDF = /* @__PURE__ */ __name(() => {
|
|
11904
|
+
if (invoice.stripePdfUrl) {
|
|
11905
|
+
window.open(invoice.stripePdfUrl, "_blank");
|
|
11906
|
+
}
|
|
11907
|
+
}, "handleDownloadPDF");
|
|
11908
|
+
const handleRetryPayment = /* @__PURE__ */ __name(async () => {
|
|
11909
|
+
}, "handleRetryPayment");
|
|
11910
|
+
const handleViewInStripe = /* @__PURE__ */ __name(() => {
|
|
11911
|
+
if (invoice.stripeHostedInvoiceUrl) {
|
|
11912
|
+
window.open(invoice.stripeHostedInvoiceUrl, "_blank");
|
|
11913
|
+
}
|
|
11914
|
+
}, "handleViewInStripe");
|
|
11915
|
+
const getInvoiceNumber = /* @__PURE__ */ __name(() => {
|
|
11916
|
+
if (invoice.stripeInvoiceNumber) {
|
|
11917
|
+
return invoice.stripeInvoiceNumber;
|
|
11918
|
+
}
|
|
11919
|
+
return invoice.stripeInvoiceId.slice(-8);
|
|
11920
|
+
}, "getInvoiceNumber");
|
|
11921
|
+
const productName = invoice.subscription?.price?.product?.name || "Subscription";
|
|
11922
|
+
return /* @__PURE__ */ jsx154(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs94(DialogContent, { className: "max-w-2xl", children: [
|
|
11923
|
+
/* @__PURE__ */ jsxs94(DialogHeader, { children: [
|
|
11924
|
+
/* @__PURE__ */ jsxs94(DialogTitle, { children: [
|
|
11925
|
+
"Invoice ",
|
|
11926
|
+
getInvoiceNumber()
|
|
11927
|
+
] }),
|
|
11928
|
+
/* @__PURE__ */ jsx154(DialogDescription, { children: formatDate3(invoice.periodStart) })
|
|
11929
|
+
] }),
|
|
11930
|
+
/* @__PURE__ */ jsxs94("div", { className: "space-y-6", children: [
|
|
11931
|
+
/* @__PURE__ */ jsxs94("div", { className: "flex items-center gap-x-3", children: [
|
|
11932
|
+
/* @__PURE__ */ jsx154("span", { className: "text-sm font-medium text-muted-foreground", children: "Status:" }),
|
|
11933
|
+
/* @__PURE__ */ jsx154(InvoiceStatusBadge, { status: invoice.status })
|
|
11934
|
+
] }),
|
|
11935
|
+
/* @__PURE__ */ jsxs94("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
11936
|
+
/* @__PURE__ */ jsxs94("div", { children: [
|
|
11937
|
+
/* @__PURE__ */ jsx154("span", { className: "text-sm font-medium text-muted-foreground", children: "Billing Period:" }),
|
|
11938
|
+
/* @__PURE__ */ jsxs94("p", { className: "font-medium", children: [
|
|
11939
|
+
formatDate3(invoice.periodStart),
|
|
11940
|
+
" - ",
|
|
11941
|
+
formatDate3(invoice.periodEnd)
|
|
11942
|
+
] })
|
|
11943
|
+
] }),
|
|
11944
|
+
invoice.dueDate && /* @__PURE__ */ jsxs94("div", { children: [
|
|
11945
|
+
/* @__PURE__ */ jsx154("span", { className: "text-sm font-medium text-muted-foreground", children: "Due Date:" }),
|
|
11946
|
+
/* @__PURE__ */ jsx154("p", { className: "font-medium", children: formatDate3(invoice.dueDate) })
|
|
11947
|
+
] }),
|
|
11948
|
+
invoice.paidAt && /* @__PURE__ */ jsxs94("div", { children: [
|
|
11949
|
+
/* @__PURE__ */ jsx154("span", { className: "text-sm font-medium text-muted-foreground", children: "Paid Date:" }),
|
|
11950
|
+
/* @__PURE__ */ jsx154("p", { className: "font-medium", children: formatDate3(invoice.paidAt) })
|
|
11951
|
+
] }),
|
|
11952
|
+
/* @__PURE__ */ jsxs94("div", { children: [
|
|
11953
|
+
/* @__PURE__ */ jsx154("span", { className: "text-sm font-medium text-muted-foreground", children: "Attempt Count:" }),
|
|
11954
|
+
/* @__PURE__ */ jsx154("p", { className: "font-medium", children: invoice.attemptCount })
|
|
11955
|
+
] })
|
|
11956
|
+
] }),
|
|
11957
|
+
/* @__PURE__ */ jsxs94("div", { children: [
|
|
11958
|
+
/* @__PURE__ */ jsx154("h4", { className: "text-sm font-medium text-muted-foreground mb-2", children: "Line Items" }),
|
|
11959
|
+
/* @__PURE__ */ jsx154("div", { className: "border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxs94("table", { className: "w-full", children: [
|
|
11960
|
+
/* @__PURE__ */ jsx154("thead", { className: "bg-muted", children: /* @__PURE__ */ jsxs94("tr", { children: [
|
|
11961
|
+
/* @__PURE__ */ jsx154("th", { className: "text-left p-3 text-sm font-medium", children: "Description" }),
|
|
11962
|
+
/* @__PURE__ */ jsx154("th", { className: "text-right p-3 text-sm font-medium", children: "Amount" })
|
|
11963
|
+
] }) }),
|
|
11964
|
+
/* @__PURE__ */ jsx154("tbody", { children: /* @__PURE__ */ jsxs94("tr", { className: "border-t", children: [
|
|
11965
|
+
/* @__PURE__ */ jsx154("td", { className: "p-3", children: productName }),
|
|
11966
|
+
/* @__PURE__ */ jsx154("td", { className: "p-3 text-right", children: formatCurrency(invoice.subtotal, invoice.currency) })
|
|
11967
|
+
] }) })
|
|
11968
|
+
] }) })
|
|
11969
|
+
] }),
|
|
11970
|
+
/* @__PURE__ */ jsxs94("div", { className: "space-y-2 border-t pt-4", children: [
|
|
11971
|
+
/* @__PURE__ */ jsxs94("div", { className: "flex justify-between", children: [
|
|
11972
|
+
/* @__PURE__ */ jsx154("span", { className: "text-sm font-medium text-muted-foreground", children: "Subtotal:" }),
|
|
11973
|
+
/* @__PURE__ */ jsx154("span", { className: "font-medium", children: formatCurrency(invoice.subtotal, invoice.currency) })
|
|
11974
|
+
] }),
|
|
11975
|
+
invoice.tax !== void 0 && invoice.tax > 0 && /* @__PURE__ */ jsxs94("div", { className: "flex justify-between", children: [
|
|
11976
|
+
/* @__PURE__ */ jsx154("span", { className: "text-sm font-medium text-muted-foreground", children: "Tax:" }),
|
|
11977
|
+
/* @__PURE__ */ jsx154("span", { className: "font-medium", children: formatCurrency(invoice.tax, invoice.currency) })
|
|
11978
|
+
] }),
|
|
11979
|
+
/* @__PURE__ */ jsxs94("div", { className: "flex justify-between text-lg font-semibold border-t pt-2", children: [
|
|
11980
|
+
/* @__PURE__ */ jsx154("span", { children: "Total:" }),
|
|
11981
|
+
/* @__PURE__ */ jsx154("span", { children: formatCurrency(invoice.total, invoice.currency) })
|
|
11982
|
+
] }),
|
|
11983
|
+
invoice.amountPaid > 0 && /* @__PURE__ */ jsxs94("div", { className: "flex justify-between", children: [
|
|
11984
|
+
/* @__PURE__ */ jsx154("span", { className: "text-sm font-medium text-muted-foreground", children: "Amount Paid:" }),
|
|
11985
|
+
/* @__PURE__ */ jsx154("span", { className: "font-medium text-green-600", children: formatCurrency(invoice.amountPaid, invoice.currency) })
|
|
11986
|
+
] }),
|
|
11987
|
+
invoice.amountRemaining > 0 && /* @__PURE__ */ jsxs94("div", { className: "flex justify-between", children: [
|
|
11988
|
+
/* @__PURE__ */ jsx154("span", { className: "text-sm font-medium text-muted-foreground", children: "Amount Due:" }),
|
|
11989
|
+
/* @__PURE__ */ jsx154("span", { className: "font-medium text-red-600", children: formatCurrency(invoice.amountRemaining, invoice.currency) })
|
|
11990
|
+
] })
|
|
11991
|
+
] }),
|
|
11992
|
+
/* @__PURE__ */ jsxs94("div", { className: "flex flex-wrap gap-2 pt-4 border-t", children: [
|
|
11993
|
+
invoice.stripePdfUrl && /* @__PURE__ */ jsxs94(Button, { variant: "outline", onClick: handleDownloadPDF, children: [
|
|
11994
|
+
/* @__PURE__ */ jsx154(Download, { className: "mr-2 h-4 w-4" }),
|
|
11995
|
+
"Download PDF"
|
|
11996
|
+
] }),
|
|
11997
|
+
invoice.status === "open" /* OPEN */ && invoice.attempted && /* @__PURE__ */ jsxs94(Button, { variant: "default", onClick: handleRetryPayment, children: [
|
|
11998
|
+
/* @__PURE__ */ jsx154(RefreshCw2, { className: "mr-2 h-4 w-4" }),
|
|
11999
|
+
"Retry Payment"
|
|
12000
|
+
] }),
|
|
12001
|
+
invoice.stripeHostedInvoiceUrl && /* @__PURE__ */ jsxs94(Button, { variant: "outline", onClick: handleViewInStripe, children: [
|
|
12002
|
+
/* @__PURE__ */ jsx154(ExternalLink2, { className: "mr-2 h-4 w-4" }),
|
|
12003
|
+
"View in Stripe"
|
|
12004
|
+
] })
|
|
12005
|
+
] })
|
|
12006
|
+
] })
|
|
12007
|
+
] }) });
|
|
12008
|
+
}
|
|
12009
|
+
__name(InvoiceDetails, "InvoiceDetails");
|
|
12010
|
+
|
|
12011
|
+
// src/features/billing/stripe-invoice/components/lists/InvoicesList.tsx
|
|
12012
|
+
import { Fragment as Fragment29, jsx as jsx155, jsxs as jsxs95 } from "react/jsx-runtime";
|
|
12013
|
+
function InvoicesList({ invoices, onInvoicesChange }) {
|
|
12014
|
+
const [selectedInvoice, setSelectedInvoice] = useState53(null);
|
|
12015
|
+
const handleRowClick = /* @__PURE__ */ __name((invoice) => {
|
|
12016
|
+
setSelectedInvoice(invoice);
|
|
12017
|
+
}, "handleRowClick");
|
|
12018
|
+
const getInvoiceNumber = /* @__PURE__ */ __name((invoice) => {
|
|
12019
|
+
if (invoice.stripeInvoiceNumber) {
|
|
12020
|
+
return invoice.stripeInvoiceNumber;
|
|
12021
|
+
}
|
|
12022
|
+
return invoice.stripeInvoiceId.slice(-8);
|
|
12023
|
+
}, "getInvoiceNumber");
|
|
12024
|
+
return /* @__PURE__ */ jsxs95(Fragment29, { children: [
|
|
12025
|
+
/* @__PURE__ */ jsx155("div", { className: "border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxs95(Table, { children: [
|
|
12026
|
+
/* @__PURE__ */ jsx155(TableHeader, { className: "bg-muted", children: /* @__PURE__ */ jsxs95(TableRow, { children: [
|
|
12027
|
+
/* @__PURE__ */ jsx155(TableHead, { children: "Invoice #" }),
|
|
12028
|
+
/* @__PURE__ */ jsx155(TableHead, { children: "Date" }),
|
|
12029
|
+
/* @__PURE__ */ jsx155(TableHead, { children: "Status" }),
|
|
12030
|
+
/* @__PURE__ */ jsx155(TableHead, { className: "text-right", children: "Amount" }),
|
|
12031
|
+
/* @__PURE__ */ jsx155(TableHead, { children: "Period" })
|
|
12032
|
+
] }) }),
|
|
12033
|
+
/* @__PURE__ */ jsx155(TableBody, { children: invoices.map((invoice) => {
|
|
12034
|
+
const invoiceNumber = getInvoiceNumber(invoice);
|
|
12035
|
+
const date = formatDate3(invoice.periodStart);
|
|
12036
|
+
const amount = formatCurrency(invoice.total, invoice.currency);
|
|
12037
|
+
const period = `${formatDate3(invoice.periodStart)} - ${formatDate3(invoice.periodEnd)}`;
|
|
12038
|
+
return /* @__PURE__ */ jsxs95(
|
|
12039
|
+
TableRow,
|
|
12040
|
+
{
|
|
12041
|
+
onClick: () => handleRowClick(invoice),
|
|
12042
|
+
className: "cursor-pointer hover:bg-muted/50",
|
|
12043
|
+
children: [
|
|
12044
|
+
/* @__PURE__ */ jsx155(TableCell, { className: "font-medium", children: invoiceNumber }),
|
|
12045
|
+
/* @__PURE__ */ jsx155(TableCell, { className: "text-muted-foreground text-sm", children: date }),
|
|
12046
|
+
/* @__PURE__ */ jsx155(TableCell, { children: /* @__PURE__ */ jsx155(InvoiceStatusBadge, { status: invoice.status }) }),
|
|
12047
|
+
/* @__PURE__ */ jsx155(TableCell, { className: "text-right font-medium", children: amount }),
|
|
12048
|
+
/* @__PURE__ */ jsx155(TableCell, { className: "text-muted-foreground text-sm", children: period })
|
|
12049
|
+
]
|
|
12050
|
+
},
|
|
12051
|
+
invoice.id
|
|
12052
|
+
);
|
|
12053
|
+
}) })
|
|
12054
|
+
] }) }),
|
|
12055
|
+
selectedInvoice && /* @__PURE__ */ jsx155(
|
|
12056
|
+
InvoiceDetails,
|
|
12057
|
+
{
|
|
12058
|
+
invoice: selectedInvoice,
|
|
12059
|
+
open: !!selectedInvoice,
|
|
12060
|
+
onOpenChange: (open) => !open && setSelectedInvoice(null),
|
|
12061
|
+
onInvoiceChange: () => {
|
|
12062
|
+
onInvoicesChange();
|
|
12063
|
+
setSelectedInvoice(null);
|
|
12064
|
+
}
|
|
12065
|
+
}
|
|
12066
|
+
)
|
|
12067
|
+
] });
|
|
12068
|
+
}
|
|
12069
|
+
__name(InvoicesList, "InvoicesList");
|
|
12070
|
+
|
|
12071
|
+
// src/features/billing/stripe-invoice/components/containers/InvoicesContainer.tsx
|
|
12072
|
+
import { jsx as jsx156, jsxs as jsxs96 } from "react/jsx-runtime";
|
|
12073
|
+
function InvoicesContainer() {
|
|
12074
|
+
const [invoices, setInvoices] = useState54([]);
|
|
12075
|
+
const [loading, setLoading] = useState54(true);
|
|
12076
|
+
const [statusFilter, setStatusFilter] = useState54("all");
|
|
12077
|
+
const loadInvoices = /* @__PURE__ */ __name(async () => {
|
|
12078
|
+
setLoading(true);
|
|
12079
|
+
try {
|
|
12080
|
+
const params = statusFilter !== "all" ? { status: statusFilter } : void 0;
|
|
12081
|
+
const data = await StripeInvoiceService.listInvoices(params);
|
|
12082
|
+
setInvoices(data);
|
|
12083
|
+
} catch (error) {
|
|
12084
|
+
console.error("[InvoicesContainer] Failed to load invoices:", error);
|
|
12085
|
+
setInvoices([]);
|
|
12086
|
+
} finally {
|
|
12087
|
+
setLoading(false);
|
|
12088
|
+
}
|
|
12089
|
+
}, "loadInvoices");
|
|
12090
|
+
useEffect43(() => {
|
|
12091
|
+
loadInvoices();
|
|
12092
|
+
}, [statusFilter]);
|
|
12093
|
+
const handleFilterChange = /* @__PURE__ */ __name((value) => {
|
|
12094
|
+
setStatusFilter(value);
|
|
12095
|
+
}, "handleFilterChange");
|
|
12096
|
+
return /* @__PURE__ */ jsxs96("div", { className: "space-y-4", children: [
|
|
12097
|
+
/* @__PURE__ */ jsx156(Tabs, { value: statusFilter, onValueChange: handleFilterChange, children: /* @__PURE__ */ jsxs96(TabsList, { children: [
|
|
12098
|
+
/* @__PURE__ */ jsx156(TabsTrigger, { value: "all", children: "All" }),
|
|
12099
|
+
/* @__PURE__ */ jsx156(TabsTrigger, { value: "paid" /* PAID */, children: "Paid" }),
|
|
12100
|
+
/* @__PURE__ */ jsx156(TabsTrigger, { value: "open" /* OPEN */, children: "Open" }),
|
|
12101
|
+
/* @__PURE__ */ jsx156(TabsTrigger, { value: "void" /* VOID */, children: "Void" }),
|
|
12102
|
+
/* @__PURE__ */ jsx156(TabsTrigger, { value: "uncollectible" /* UNCOLLECTIBLE */, children: "Uncollectible" })
|
|
12103
|
+
] }) }),
|
|
12104
|
+
loading && /* @__PURE__ */ jsx156("div", { className: "text-center py-8 text-muted-foreground", children: "Loading invoices..." }),
|
|
12105
|
+
!loading && invoices.length === 0 && /* @__PURE__ */ jsxs96("div", { className: "border border-dashed border-gray-300 rounded-lg p-8 text-center", children: [
|
|
12106
|
+
/* @__PURE__ */ jsx156("p", { className: "text-lg font-medium text-muted-foreground mb-2", children: "No invoices yet" }),
|
|
12107
|
+
/* @__PURE__ */ jsx156("p", { className: "text-sm text-muted-foreground", children: "Invoices will appear here after your first billing cycle" })
|
|
12108
|
+
] }),
|
|
12109
|
+
!loading && invoices.length > 0 && /* @__PURE__ */ jsx156(InvoicesList, { invoices, onInvoicesChange: loadInvoices })
|
|
12110
|
+
] });
|
|
12111
|
+
}
|
|
12112
|
+
__name(InvoicesContainer, "InvoicesContainer");
|
|
12113
|
+
|
|
12114
|
+
// src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx
|
|
12115
|
+
import { CheckCircle as CheckCircle2, CreditCard as CreditCard3, Loader2 as Loader23 } from "lucide-react";
|
|
12116
|
+
import { useEffect as useEffect45, useState as useState60 } from "react";
|
|
12117
|
+
import { v4 as v46 } from "uuid";
|
|
12118
|
+
|
|
12119
|
+
// src/features/billing/stripe-subscription/hooks/useConfirmSubscriptionPayment.ts
|
|
12120
|
+
import { useStripe as useStripe2 } from "@stripe/react-stripe-js";
|
|
12121
|
+
import { useCallback as useCallback17, useState as useState55 } from "react";
|
|
12122
|
+
function useConfirmSubscriptionPayment() {
|
|
12123
|
+
const stripe = useStripe2();
|
|
12124
|
+
const [isConfirming, setIsConfirming] = useState55(false);
|
|
12125
|
+
const confirmPayment = useCallback17(
|
|
12126
|
+
async (clientSecret) => {
|
|
12127
|
+
if (!stripe) {
|
|
12128
|
+
console.error("[useConfirmSubscriptionPayment] Stripe not initialized");
|
|
12129
|
+
return {
|
|
12130
|
+
success: false,
|
|
12131
|
+
error: "Payment system not initialized. Please refresh the page and try again."
|
|
12132
|
+
};
|
|
12133
|
+
}
|
|
12134
|
+
if (!clientSecret) {
|
|
12135
|
+
console.error("[useConfirmSubscriptionPayment] No client secret provided");
|
|
12136
|
+
return {
|
|
12137
|
+
success: false,
|
|
12138
|
+
error: "Payment confirmation failed. Missing payment details."
|
|
12139
|
+
};
|
|
12140
|
+
}
|
|
12141
|
+
setIsConfirming(true);
|
|
12142
|
+
try {
|
|
12143
|
+
const { error: stripeError, paymentIntent } = await stripe.confirmCardPayment(clientSecret);
|
|
12144
|
+
if (stripeError) {
|
|
12145
|
+
console.error("[useConfirmSubscriptionPayment] Stripe error:", stripeError);
|
|
12146
|
+
return {
|
|
12147
|
+
success: false,
|
|
12148
|
+
error: stripeError.message || "Payment confirmation failed. Please try again."
|
|
12149
|
+
};
|
|
12150
|
+
}
|
|
12151
|
+
if (paymentIntent?.status === "succeeded") {
|
|
12152
|
+
return { success: true };
|
|
12153
|
+
}
|
|
12154
|
+
if (paymentIntent?.status === "requires_action") {
|
|
12155
|
+
return {
|
|
12156
|
+
success: false,
|
|
12157
|
+
error: "Additional authentication required. Please complete the verification."
|
|
12158
|
+
};
|
|
12159
|
+
}
|
|
12160
|
+
return {
|
|
12161
|
+
success: false,
|
|
12162
|
+
error: "Payment could not be completed. Please try again."
|
|
12163
|
+
};
|
|
12164
|
+
} catch (err) {
|
|
12165
|
+
console.error("[useConfirmSubscriptionPayment] Unexpected error:", err);
|
|
12166
|
+
return {
|
|
12167
|
+
success: false,
|
|
12168
|
+
error: err.message || "An unexpected error occurred during payment confirmation."
|
|
12169
|
+
};
|
|
12170
|
+
} finally {
|
|
12171
|
+
setIsConfirming(false);
|
|
12172
|
+
}
|
|
12173
|
+
},
|
|
12174
|
+
[stripe]
|
|
12175
|
+
);
|
|
12176
|
+
return {
|
|
12177
|
+
confirmPayment,
|
|
12178
|
+
isConfirming
|
|
12179
|
+
};
|
|
12180
|
+
}
|
|
12181
|
+
__name(useConfirmSubscriptionPayment, "useConfirmSubscriptionPayment");
|
|
12182
|
+
|
|
12183
|
+
// src/features/billing/stripe-subscription/components/forms/CancelSubscriptionDialog.tsx
|
|
12184
|
+
import { zodResolver as zodResolver10 } from "@hookform/resolvers/zod";
|
|
12185
|
+
import { useState as useState56 } from "react";
|
|
12186
|
+
import { useForm as useForm10 } from "react-hook-form";
|
|
12187
|
+
import { z as z10 } from "zod";
|
|
12188
|
+
import { jsx as jsx157, jsxs as jsxs97 } from "react/jsx-runtime";
|
|
12189
|
+
var formSchema = z10.object({
|
|
12190
|
+
cancelImmediately: z10.boolean(),
|
|
12191
|
+
reason: z10.string().optional()
|
|
12192
|
+
});
|
|
12193
|
+
function CancelSubscriptionDialog({
|
|
12194
|
+
subscription,
|
|
12195
|
+
open,
|
|
12196
|
+
onOpenChange,
|
|
12197
|
+
onSuccess
|
|
12198
|
+
}) {
|
|
12199
|
+
const [isSubmitting, setIsSubmitting] = useState56(false);
|
|
12200
|
+
const form = useForm10({
|
|
12201
|
+
resolver: zodResolver10(formSchema),
|
|
12202
|
+
defaultValues: {
|
|
12203
|
+
cancelImmediately: false,
|
|
12204
|
+
reason: ""
|
|
12205
|
+
}
|
|
12206
|
+
});
|
|
12207
|
+
const cancelImmediately = form.watch("cancelImmediately");
|
|
12208
|
+
const onSubmit = /* @__PURE__ */ __name(async (values) => {
|
|
12209
|
+
setIsSubmitting(true);
|
|
12210
|
+
try {
|
|
12211
|
+
await StripeSubscriptionService.cancelSubscription({
|
|
12212
|
+
id: subscription.id,
|
|
12213
|
+
cancelImmediately: values.cancelImmediately
|
|
12214
|
+
});
|
|
12215
|
+
onSuccess();
|
|
12216
|
+
onOpenChange(false);
|
|
12217
|
+
} catch (error) {
|
|
12218
|
+
console.error("[CancelSubscriptionDialog] Failed to cancel subscription:", error);
|
|
12219
|
+
} finally {
|
|
12220
|
+
setIsSubmitting(false);
|
|
12221
|
+
}
|
|
12222
|
+
}, "onSubmit");
|
|
12223
|
+
const periodEndDate = formatDate3(subscription.currentPeriodEnd);
|
|
12224
|
+
return /* @__PURE__ */ jsx157(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs97(DialogContent, { className: "max-w-md", children: [
|
|
12225
|
+
/* @__PURE__ */ jsxs97(DialogHeader, { children: [
|
|
12226
|
+
/* @__PURE__ */ jsx157(DialogTitle, { children: "Cancel Subscription" }),
|
|
12227
|
+
/* @__PURE__ */ jsx157(DialogDescription, { children: "Are you sure you want to cancel this subscription? This action cannot be undone." })
|
|
12228
|
+
] }),
|
|
12229
|
+
/* @__PURE__ */ jsx157(Form, { ...form, children: /* @__PURE__ */ jsxs97("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-col gap-y-4", children: [
|
|
12230
|
+
/* @__PURE__ */ jsx157(FormCheckbox, { form, id: "cancelImmediately", name: "Cancel Immediately" }),
|
|
12231
|
+
cancelImmediately ? /* @__PURE__ */ jsx157("div", { className: "bg-red-50 border border-red-200 rounded-lg p-3 text-sm text-red-800", children: "Your subscription will be canceled immediately and you will lose access right away." }) : /* @__PURE__ */ jsxs97("div", { className: "bg-blue-50 border border-blue-200 rounded-lg p-3 text-sm text-blue-800", children: [
|
|
12232
|
+
"Your subscription will remain active until ",
|
|
12233
|
+
periodEndDate,
|
|
12234
|
+
". You can continue using the service until then."
|
|
12235
|
+
] }),
|
|
12236
|
+
/* @__PURE__ */ jsx157(
|
|
12237
|
+
FormTextarea,
|
|
12238
|
+
{
|
|
12239
|
+
form,
|
|
12240
|
+
id: "reason",
|
|
12241
|
+
name: "Reason (Optional)",
|
|
12242
|
+
placeholder: "Let us know why you're canceling...",
|
|
12243
|
+
className: "min-h-24"
|
|
12244
|
+
}
|
|
12245
|
+
),
|
|
12246
|
+
/* @__PURE__ */ jsxs97("div", { className: "flex gap-x-2 justify-end pt-2", children: [
|
|
12247
|
+
/* @__PURE__ */ jsx157(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), disabled: isSubmitting, children: "Keep Subscription" }),
|
|
12248
|
+
/* @__PURE__ */ jsx157(Button, { type: "submit", variant: "destructive", disabled: isSubmitting, children: isSubmitting ? "Canceling..." : "Confirm Cancellation" })
|
|
12249
|
+
] })
|
|
12250
|
+
] }) })
|
|
12251
|
+
] }) });
|
|
12252
|
+
}
|
|
12253
|
+
__name(CancelSubscriptionDialog, "CancelSubscriptionDialog");
|
|
12254
|
+
|
|
12255
|
+
// src/features/billing/stripe-subscription/components/forms/SubscriptionEditor.tsx
|
|
12256
|
+
import { CheckCircle, Loader2 as Loader22 } from "lucide-react";
|
|
12257
|
+
import { useEffect as useEffect44, useState as useState57 } from "react";
|
|
12258
|
+
import { v4 as v45 } from "uuid";
|
|
12259
|
+
|
|
12260
|
+
// src/features/billing/stripe-subscription/components/widgets/PricingCard.tsx
|
|
12261
|
+
import { Check } from "lucide-react";
|
|
12262
|
+
import { jsx as jsx158, jsxs as jsxs98 } from "react/jsx-runtime";
|
|
12263
|
+
function PricingCard({ price, isCurrentPlan = false, isSelected = false, isDisabled = false, isLoading = false, onSelect }) {
|
|
12264
|
+
const description = price.description || price.nickname || "Standard";
|
|
12265
|
+
const features = price.features || [];
|
|
12266
|
+
const formattedPrice = formatCurrency(price.unitAmount, price.currency);
|
|
12267
|
+
const interval = formatInterval(price);
|
|
12268
|
+
const handleKeyDown = /* @__PURE__ */ __name((e) => {
|
|
12269
|
+
if ((e.key === "Enter" || e.key === " ") && !isDisabled && !isCurrentPlan) {
|
|
12270
|
+
e.preventDefault();
|
|
12271
|
+
onSelect(price);
|
|
12272
|
+
}
|
|
12273
|
+
}, "handleKeyDown");
|
|
12274
|
+
const handleClick = /* @__PURE__ */ __name(() => {
|
|
12275
|
+
if (!isDisabled && !isCurrentPlan && !isLoading) {
|
|
12276
|
+
onSelect(price);
|
|
12277
|
+
}
|
|
12278
|
+
}, "handleClick");
|
|
12279
|
+
return /* @__PURE__ */ jsxs98(
|
|
12280
|
+
Card,
|
|
12281
|
+
{
|
|
12282
|
+
role: "radio",
|
|
12283
|
+
"aria-checked": isSelected,
|
|
12284
|
+
"aria-label": `${description} plan at ${formattedPrice} ${interval}`,
|
|
12285
|
+
tabIndex: isDisabled ? -1 : 0,
|
|
12286
|
+
onKeyDown: handleKeyDown,
|
|
12287
|
+
onClick: handleClick,
|
|
12288
|
+
className: cn(
|
|
12289
|
+
"relative cursor-pointer transition-all duration-200 flex flex-col h-full",
|
|
12290
|
+
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
12291
|
+
isCurrentPlan && "bg-muted/30",
|
|
12292
|
+
isSelected && !isCurrentPlan && "ring-2 ring-primary",
|
|
12293
|
+
!isDisabled && !isCurrentPlan && "hover:shadow-md hover:border-primary/50",
|
|
12294
|
+
isDisabled && "opacity-50 pointer-events-none",
|
|
12295
|
+
isLoading && "pointer-events-none"
|
|
12296
|
+
),
|
|
12297
|
+
children: [
|
|
12298
|
+
isCurrentPlan && /* @__PURE__ */ jsx158(Badge, { variant: "secondary", className: "absolute top-2 right-2", children: "Current" }),
|
|
12299
|
+
/* @__PURE__ */ jsx158(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx158("h3", { className: "font-semibold text-lg", children: description }) }),
|
|
12300
|
+
/* @__PURE__ */ jsxs98(CardContent, { className: "pb-4 grow", children: [
|
|
12301
|
+
/* @__PURE__ */ jsxs98("div", { className: "mb-4", children: [
|
|
12302
|
+
/* @__PURE__ */ jsx158("span", { className: "text-3xl font-bold", children: formattedPrice }),
|
|
12303
|
+
/* @__PURE__ */ jsx158("span", { className: "text-muted-foreground ml-1", children: interval })
|
|
12304
|
+
] }),
|
|
12305
|
+
features.length > 0 && /* @__PURE__ */ jsx158("ul", { className: "space-y-2", children: features.map((feature, index) => /* @__PURE__ */ jsxs98("li", { className: "flex items-start gap-2", children: [
|
|
12306
|
+
/* @__PURE__ */ jsx158(Check, { className: "h-4 w-4 text-green-500 mt-0.5 shrink-0" }),
|
|
12307
|
+
/* @__PURE__ */ jsx158("span", { className: "text-sm text-muted-foreground", children: feature })
|
|
12308
|
+
] }, index)) })
|
|
12309
|
+
] }),
|
|
12310
|
+
/* @__PURE__ */ jsx158(CardFooter, { children: /* @__PURE__ */ jsx158(
|
|
12311
|
+
Button,
|
|
12312
|
+
{
|
|
12313
|
+
variant: isCurrentPlan ? "secondary" : isSelected ? "default" : "outline",
|
|
12314
|
+
className: "w-full",
|
|
12315
|
+
disabled: isDisabled || isCurrentPlan || isLoading,
|
|
12316
|
+
children: isLoading ? "Processing..." : isCurrentPlan ? "Current Plan" : isSelected ? "Selected" : "Select Plan"
|
|
12317
|
+
}
|
|
12318
|
+
) })
|
|
12319
|
+
]
|
|
12320
|
+
}
|
|
12321
|
+
);
|
|
12322
|
+
}
|
|
12323
|
+
__name(PricingCard, "PricingCard");
|
|
12324
|
+
|
|
12325
|
+
// src/features/billing/stripe-subscription/components/widgets/PricingCardsGrid.tsx
|
|
12326
|
+
import { jsx as jsx159, jsxs as jsxs99 } from "react/jsx-runtime";
|
|
12327
|
+
function PricingCardsGrid({
|
|
12328
|
+
products,
|
|
12329
|
+
pricesByProduct,
|
|
12330
|
+
currentPriceId,
|
|
12331
|
+
selectedPriceId,
|
|
12332
|
+
loadingPriceId,
|
|
12333
|
+
loading = false,
|
|
12334
|
+
onSelectPrice
|
|
12335
|
+
}) {
|
|
12336
|
+
if (loading) {
|
|
12337
|
+
return /* @__PURE__ */ jsx159(PricingCardsGridSkeleton, {});
|
|
12338
|
+
}
|
|
12339
|
+
if (products.length === 0) {
|
|
12340
|
+
return /* @__PURE__ */ jsx159("div", { className: "text-center py-8 text-muted-foreground", children: "No plans available" });
|
|
12341
|
+
}
|
|
12342
|
+
return /* @__PURE__ */ jsx159("div", { className: "space-y-8", role: "radiogroup", "aria-label": "Available pricing plans", children: products.map((product) => {
|
|
12343
|
+
const prices = pricesByProduct.get(product.id) || [];
|
|
12344
|
+
if (prices.length === 0) return null;
|
|
12345
|
+
const sortedPrices = [...prices].sort((a, b) => (a.unitAmount ?? 0) - (b.unitAmount ?? 0));
|
|
12346
|
+
return /* @__PURE__ */ jsxs99("div", { className: "space-y-4", children: [
|
|
12347
|
+
/* @__PURE__ */ jsx159("h3", { className: "text-lg font-semibold", children: product.name }),
|
|
12348
|
+
/* @__PURE__ */ jsx159("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4", children: sortedPrices.map((price) => /* @__PURE__ */ jsx159(
|
|
12349
|
+
PricingCard,
|
|
12350
|
+
{
|
|
12351
|
+
price,
|
|
12352
|
+
isCurrentPlan: price.stripePriceId === currentPriceId,
|
|
12353
|
+
isSelected: price.stripePriceId === selectedPriceId,
|
|
12354
|
+
isLoading: price.stripePriceId === loadingPriceId,
|
|
12355
|
+
onSelect: onSelectPrice
|
|
12356
|
+
},
|
|
12357
|
+
price.stripePriceId
|
|
12358
|
+
)) })
|
|
12359
|
+
] }, product.id);
|
|
12360
|
+
}) });
|
|
12361
|
+
}
|
|
12362
|
+
__name(PricingCardsGrid, "PricingCardsGrid");
|
|
12363
|
+
function PricingCardsGridSkeleton() {
|
|
12364
|
+
return /* @__PURE__ */ jsx159("div", { className: "space-y-8", children: [1, 2].map((productIndex) => /* @__PURE__ */ jsxs99("div", { className: "space-y-4", children: [
|
|
12365
|
+
/* @__PURE__ */ jsx159(Skeleton, { className: "h-6 w-32" }),
|
|
12366
|
+
/* @__PURE__ */ jsx159("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4", children: [1, 2, 3].map((cardIndex) => /* @__PURE__ */ jsxs99(Card, { className: "animate-pulse", children: [
|
|
12367
|
+
/* @__PURE__ */ jsx159(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx159(Skeleton, { className: "h-5 w-24" }) }),
|
|
12368
|
+
/* @__PURE__ */ jsxs99(CardContent, { className: "pb-4", children: [
|
|
12369
|
+
/* @__PURE__ */ jsxs99("div", { className: "mb-4", children: [
|
|
12370
|
+
/* @__PURE__ */ jsx159(Skeleton, { className: "h-9 w-20 inline-block" }),
|
|
12371
|
+
/* @__PURE__ */ jsx159(Skeleton, { className: "h-4 w-12 inline-block ml-2" })
|
|
12372
|
+
] }),
|
|
12373
|
+
/* @__PURE__ */ jsxs99("div", { className: "space-y-2", children: [
|
|
12374
|
+
/* @__PURE__ */ jsxs99("div", { className: "flex items-center gap-2", children: [
|
|
12375
|
+
/* @__PURE__ */ jsx159(Skeleton, { className: "h-4 w-4 rounded-full" }),
|
|
12376
|
+
/* @__PURE__ */ jsx159(Skeleton, { className: "h-4 w-32" })
|
|
12377
|
+
] }),
|
|
12378
|
+
/* @__PURE__ */ jsxs99("div", { className: "flex items-center gap-2", children: [
|
|
12379
|
+
/* @__PURE__ */ jsx159(Skeleton, { className: "h-4 w-4 rounded-full" }),
|
|
12380
|
+
/* @__PURE__ */ jsx159(Skeleton, { className: "h-4 w-28" })
|
|
12381
|
+
] })
|
|
12382
|
+
] })
|
|
12383
|
+
] }),
|
|
12384
|
+
/* @__PURE__ */ jsx159(CardFooter, { children: /* @__PURE__ */ jsx159(Skeleton, { className: "h-9 w-full" }) })
|
|
12385
|
+
] }, cardIndex)) })
|
|
12386
|
+
] }, productIndex)) });
|
|
12387
|
+
}
|
|
12388
|
+
__name(PricingCardsGridSkeleton, "PricingCardsGridSkeleton");
|
|
12389
|
+
|
|
12390
|
+
// src/features/billing/stripe-subscription/components/widgets/ProrationPreview.tsx
|
|
12391
|
+
import { jsx as jsx160, jsxs as jsxs100 } from "react/jsx-runtime";
|
|
12392
|
+
function ProrationPreview({ preview }) {
|
|
12393
|
+
return /* @__PURE__ */ jsxs100("div", { className: "bg-blue-50 border border-blue-200 rounded-lg p-4", children: [
|
|
12394
|
+
/* @__PURE__ */ jsx160("h4", { className: "font-semibold text-blue-900 mb-3", children: "Proration Breakdown" }),
|
|
12395
|
+
/* @__PURE__ */ jsxs100("div", { className: "space-y-2", children: [
|
|
12396
|
+
preview.lineItems.map((item, index) => /* @__PURE__ */ jsxs100("div", { className: "flex justify-between text-sm", children: [
|
|
12397
|
+
/* @__PURE__ */ jsx160("span", { className: "text-blue-800", children: item.description }),
|
|
12398
|
+
/* @__PURE__ */ jsx160("span", { className: `font-medium ${item.amount < 0 ? "text-green-600" : "text-blue-900"}`, children: formatCurrency(item.amount, preview.currency) })
|
|
12399
|
+
] }, index)),
|
|
12400
|
+
/* @__PURE__ */ jsx160("div", { className: "border-t border-blue-200 pt-2 mt-2", children: /* @__PURE__ */ jsxs100("div", { className: "flex justify-between font-semibold", children: [
|
|
12401
|
+
/* @__PURE__ */ jsx160("span", { className: "text-blue-900", children: "Net Due Today" }),
|
|
12402
|
+
/* @__PURE__ */ jsx160("span", { className: "text-blue-900", children: formatCurrency(preview.immediateCharge, preview.currency) })
|
|
12403
|
+
] }) }),
|
|
12404
|
+
preview.lineItems.length > 0 && preview.lineItems[0].period && /* @__PURE__ */ jsxs100("div", { className: "text-xs text-blue-700 mt-2", children: [
|
|
12405
|
+
"Next invoice on ",
|
|
12406
|
+
formatDate3(preview.lineItems[0].period.end),
|
|
12407
|
+
" for",
|
|
12408
|
+
" ",
|
|
12409
|
+
formatCurrency(preview.amountDue, preview.currency)
|
|
12410
|
+
] })
|
|
12411
|
+
] })
|
|
12412
|
+
] });
|
|
12413
|
+
}
|
|
12414
|
+
__name(ProrationPreview, "ProrationPreview");
|
|
12415
|
+
|
|
12416
|
+
// src/features/billing/stripe-subscription/components/forms/SubscriptionEditor.tsx
|
|
12417
|
+
import { jsx as jsx161, jsxs as jsxs101 } from "react/jsx-runtime";
|
|
12418
|
+
function SubscriptionEditor({
|
|
12419
|
+
subscription,
|
|
12420
|
+
open,
|
|
12421
|
+
onOpenChange,
|
|
12422
|
+
onSuccess,
|
|
12423
|
+
onAddPaymentMethod
|
|
12424
|
+
}) {
|
|
12425
|
+
const { confirmPayment, isConfirming } = useConfirmSubscriptionPayment();
|
|
12426
|
+
const [products, setProducts] = useState57([]);
|
|
12427
|
+
const [pricesByProduct, setPricesByProduct] = useState57(/* @__PURE__ */ new Map());
|
|
12428
|
+
const [loading, setLoading] = useState57(true);
|
|
12429
|
+
const [selectedPriceId, setSelectedPriceId] = useState57(null);
|
|
12430
|
+
const [loadingPriceId, setLoadingPriceId] = useState57(null);
|
|
12431
|
+
const [prorationPreview, setProrationPreview] = useState57(null);
|
|
12432
|
+
const [loadingProration, setLoadingProration] = useState57(false);
|
|
12433
|
+
const [hasPaymentMethod, setHasPaymentMethod] = useState57(true);
|
|
12434
|
+
const [loadingPaymentMethods, setLoadingPaymentMethods] = useState57(true);
|
|
12435
|
+
const [paymentRequiredError, setPaymentRequiredError] = useState57(false);
|
|
12436
|
+
const [paymentConfirmationState, setPaymentConfirmationState] = useState57("idle");
|
|
12437
|
+
const [paymentError, setPaymentError] = useState57(null);
|
|
12438
|
+
const currentPriceId = subscription?.price?.id;
|
|
12439
|
+
const isEditMode = !!subscription;
|
|
12440
|
+
useEffect44(() => {
|
|
12441
|
+
const checkPaymentMethods = /* @__PURE__ */ __name(async () => {
|
|
12442
|
+
if (subscription) {
|
|
12443
|
+
setLoadingPaymentMethods(false);
|
|
12444
|
+
return;
|
|
12445
|
+
}
|
|
12446
|
+
setLoadingPaymentMethods(true);
|
|
12447
|
+
try {
|
|
12448
|
+
const paymentMethods = await StripeCustomerService.listPaymentMethods();
|
|
12449
|
+
const hasMethod = paymentMethods.length > 0;
|
|
12450
|
+
setHasPaymentMethod(hasMethod);
|
|
12451
|
+
} catch (error) {
|
|
12452
|
+
console.error("[SubscriptionEditor] Failed to check payment methods:", error);
|
|
12453
|
+
setHasPaymentMethod(false);
|
|
12454
|
+
} finally {
|
|
12455
|
+
setLoadingPaymentMethods(false);
|
|
12456
|
+
}
|
|
12457
|
+
}, "checkPaymentMethods");
|
|
12458
|
+
if (open) {
|
|
12459
|
+
checkPaymentMethods();
|
|
12460
|
+
}
|
|
12461
|
+
}, [open, subscription]);
|
|
12462
|
+
useEffect44(() => {
|
|
12463
|
+
const loadData = /* @__PURE__ */ __name(async () => {
|
|
12464
|
+
setLoading(true);
|
|
12465
|
+
try {
|
|
12466
|
+
const fetchedProducts = await StripeProductService.listProducts({ active: true });
|
|
12467
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
12468
|
+
for (const product of fetchedProducts) {
|
|
12469
|
+
if (product.stripePrices && product.stripePrices.length > 0) {
|
|
12470
|
+
grouped.set(product.id, product.stripePrices);
|
|
12471
|
+
}
|
|
12472
|
+
}
|
|
12473
|
+
setProducts(fetchedProducts);
|
|
12474
|
+
setPricesByProduct(grouped);
|
|
12475
|
+
} catch (error) {
|
|
12476
|
+
console.error("[SubscriptionEditor] Failed to load products/prices:", error);
|
|
12477
|
+
} finally {
|
|
12478
|
+
setLoading(false);
|
|
12479
|
+
}
|
|
12480
|
+
}, "loadData");
|
|
12481
|
+
if (open) {
|
|
12482
|
+
loadData();
|
|
12483
|
+
}
|
|
12484
|
+
}, [open]);
|
|
12485
|
+
useEffect44(() => {
|
|
12486
|
+
const loadProration = /* @__PURE__ */ __name(async () => {
|
|
12487
|
+
if (!subscription || !selectedPriceId || selectedPriceId === currentPriceId) {
|
|
12488
|
+
setProrationPreview(null);
|
|
12489
|
+
return;
|
|
12490
|
+
}
|
|
12491
|
+
setLoadingProration(true);
|
|
12492
|
+
try {
|
|
12493
|
+
const preview = await StripeSubscriptionService.getProrationPreview({
|
|
12494
|
+
subscriptionId: subscription.id,
|
|
12495
|
+
newPriceId: selectedPriceId
|
|
12496
|
+
});
|
|
12497
|
+
setProrationPreview(preview);
|
|
12498
|
+
} catch (error) {
|
|
12499
|
+
console.error("[SubscriptionEditor] Failed to load proration preview:", error);
|
|
12500
|
+
setProrationPreview(null);
|
|
12501
|
+
} finally {
|
|
12502
|
+
setLoadingProration(false);
|
|
12503
|
+
}
|
|
12504
|
+
}, "loadProration");
|
|
12505
|
+
loadProration();
|
|
12506
|
+
}, [selectedPriceId, subscription, currentPriceId]);
|
|
12507
|
+
const handleSelectPrice = /* @__PURE__ */ __name(async (price) => {
|
|
12508
|
+
const priceId = price.id;
|
|
12509
|
+
if (isEditMode) {
|
|
12510
|
+
setSelectedPriceId(priceId);
|
|
12511
|
+
} else {
|
|
12512
|
+
setLoadingPriceId(priceId);
|
|
12513
|
+
setSelectedPriceId(priceId);
|
|
12514
|
+
setPaymentError(null);
|
|
12515
|
+
setPaymentConfirmationState("idle");
|
|
12516
|
+
try {
|
|
12517
|
+
const result = await StripeSubscriptionService.createSubscription({
|
|
12518
|
+
id: v45(),
|
|
12519
|
+
priceId
|
|
12520
|
+
});
|
|
12521
|
+
if (result.meta.requiresAction && result.meta.clientSecret) {
|
|
12522
|
+
setPaymentConfirmationState("confirming");
|
|
12523
|
+
const confirmation = await confirmPayment(result.meta.clientSecret);
|
|
12524
|
+
if (!confirmation.success) {
|
|
12525
|
+
console.error("[SubscriptionEditor] Payment confirmation failed:", confirmation.error);
|
|
12526
|
+
setPaymentConfirmationState("error");
|
|
12527
|
+
setPaymentError(confirmation.error || "Payment confirmation failed");
|
|
12528
|
+
setLoadingPriceId(null);
|
|
12529
|
+
return;
|
|
12530
|
+
}
|
|
12531
|
+
await StripeSubscriptionService.syncSubscription({
|
|
12532
|
+
subscriptionId: result.subscription.id
|
|
12533
|
+
});
|
|
12534
|
+
}
|
|
12535
|
+
setPaymentConfirmationState("success");
|
|
12536
|
+
setTimeout(() => {
|
|
12537
|
+
onSuccess();
|
|
12538
|
+
onOpenChange(false);
|
|
12539
|
+
}, 1e3);
|
|
12540
|
+
} catch (error) {
|
|
12541
|
+
console.error("[SubscriptionEditor] Failed to create subscription:", error);
|
|
12542
|
+
if (error?.status === 402 || error?.response?.status === 402) {
|
|
12543
|
+
setPaymentRequiredError(true);
|
|
12544
|
+
setHasPaymentMethod(false);
|
|
12545
|
+
} else {
|
|
12546
|
+
setPaymentConfirmationState("error");
|
|
12547
|
+
setPaymentError(error?.message || "Failed to create subscription");
|
|
12548
|
+
}
|
|
12549
|
+
setLoadingPriceId(null);
|
|
12550
|
+
}
|
|
12551
|
+
}
|
|
12552
|
+
}, "handleSelectPrice");
|
|
12553
|
+
const handleConfirmPlanChange = /* @__PURE__ */ __name(async () => {
|
|
12554
|
+
if (!subscription || !selectedPriceId) return;
|
|
12555
|
+
setLoadingPriceId(selectedPriceId);
|
|
12556
|
+
try {
|
|
12557
|
+
await StripeSubscriptionService.changePlan({
|
|
12558
|
+
id: subscription.id,
|
|
12559
|
+
newPriceId: selectedPriceId
|
|
12560
|
+
});
|
|
12561
|
+
onSuccess();
|
|
12562
|
+
onOpenChange(false);
|
|
12563
|
+
} catch (error) {
|
|
12564
|
+
console.error("[SubscriptionEditor] Failed to change plan:", error);
|
|
12565
|
+
} finally {
|
|
12566
|
+
setLoadingPriceId(null);
|
|
12567
|
+
}
|
|
12568
|
+
}, "handleConfirmPlanChange");
|
|
12569
|
+
const handleCancel = /* @__PURE__ */ __name(() => {
|
|
12570
|
+
setSelectedPriceId(null);
|
|
12571
|
+
setProrationPreview(null);
|
|
12572
|
+
}, "handleCancel");
|
|
12573
|
+
return /* @__PURE__ */ jsx161(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs101(DialogContent, { className: "max-w-4xl max-h-[90vh] overflow-y-auto", children: [
|
|
12574
|
+
/* @__PURE__ */ jsxs101(DialogHeader, { children: [
|
|
12575
|
+
/* @__PURE__ */ jsx161(DialogTitle, { children: subscription ? "Change Plan" : "Subscribe to a Plan" }),
|
|
12576
|
+
/* @__PURE__ */ jsx161(DialogDescription, { children: subscription ? "Select a new plan to switch to. You'll see a proration preview before confirming." : "Choose a plan to start your subscription." })
|
|
12577
|
+
] }),
|
|
12578
|
+
loadingPaymentMethods && !subscription ? /* @__PURE__ */ jsx161("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx161("div", { className: "text-muted-foreground", children: "Checking payment methods..." }) }) : !hasPaymentMethod && !subscription ? /* @__PURE__ */ jsxs101(Alert, { variant: "destructive", children: [
|
|
12579
|
+
/* @__PURE__ */ jsx161(AlertTitle, { children: "Payment Method Required" }),
|
|
12580
|
+
/* @__PURE__ */ jsxs101(AlertDescription, { className: "mt-2", children: [
|
|
12581
|
+
/* @__PURE__ */ jsx161("p", { className: "mb-4", children: paymentRequiredError ? "Your subscription could not be created because no payment method is on file." : "You need to add a payment method before you can subscribe to a plan." }),
|
|
12582
|
+
onAddPaymentMethod && /* @__PURE__ */ jsx161(Button, { onClick: onAddPaymentMethod, variant: "outline", children: "Add Payment Method" })
|
|
12583
|
+
] })
|
|
12584
|
+
] }) : paymentConfirmationState === "confirming" || isConfirming ? /* @__PURE__ */ jsxs101("div", { className: "flex flex-col items-center justify-center py-12 space-y-4", children: [
|
|
12585
|
+
/* @__PURE__ */ jsx161(Loader22, { className: "h-8 w-8 animate-spin text-primary" }),
|
|
12586
|
+
/* @__PURE__ */ jsxs101("div", { className: "text-center", children: [
|
|
12587
|
+
/* @__PURE__ */ jsx161("p", { className: "font-medium", children: "Processing payment..." }),
|
|
12588
|
+
/* @__PURE__ */ jsx161("p", { className: "text-sm text-muted-foreground", children: "Please complete any verification if prompted." })
|
|
12589
|
+
] })
|
|
12590
|
+
] }) : paymentConfirmationState === "success" ? /* @__PURE__ */ jsxs101("div", { className: "flex flex-col items-center justify-center py-12 space-y-4", children: [
|
|
12591
|
+
/* @__PURE__ */ jsx161(CheckCircle, { className: "h-12 w-12 text-green-500" }),
|
|
12592
|
+
/* @__PURE__ */ jsxs101("div", { className: "text-center", children: [
|
|
12593
|
+
/* @__PURE__ */ jsx161("p", { className: "font-medium text-green-600", children: "Payment successful!" }),
|
|
12594
|
+
/* @__PURE__ */ jsx161("p", { className: "text-sm text-muted-foreground", children: "Your subscription is now active." })
|
|
12595
|
+
] })
|
|
12596
|
+
] }) : paymentConfirmationState === "error" ? /* @__PURE__ */ jsx161("div", { className: "space-y-4", children: /* @__PURE__ */ jsxs101(Alert, { variant: "destructive", children: [
|
|
12597
|
+
/* @__PURE__ */ jsx161(AlertTitle, { children: "Payment Failed" }),
|
|
12598
|
+
/* @__PURE__ */ jsxs101(AlertDescription, { className: "mt-2", children: [
|
|
12599
|
+
/* @__PURE__ */ jsx161("p", { className: "mb-4", children: paymentError || "We couldn't process your payment. Please try again." }),
|
|
12600
|
+
/* @__PURE__ */ jsx161(
|
|
12601
|
+
Button,
|
|
12602
|
+
{
|
|
12603
|
+
onClick: () => {
|
|
12604
|
+
setPaymentConfirmationState("idle");
|
|
12605
|
+
setPaymentError(null);
|
|
12606
|
+
setLoadingPriceId(null);
|
|
12607
|
+
},
|
|
12608
|
+
variant: "outline",
|
|
12609
|
+
children: "Try Again"
|
|
12610
|
+
}
|
|
12611
|
+
)
|
|
12612
|
+
] })
|
|
12613
|
+
] }) }) : /* @__PURE__ */ jsxs101("div", { className: "space-y-6", children: [
|
|
12614
|
+
/* @__PURE__ */ jsx161(
|
|
12615
|
+
PricingCardsGrid,
|
|
12616
|
+
{
|
|
12617
|
+
products,
|
|
12618
|
+
pricesByProduct,
|
|
12619
|
+
currentPriceId,
|
|
12620
|
+
selectedPriceId: selectedPriceId ?? void 0,
|
|
12621
|
+
loadingPriceId: loadingPriceId ?? void 0,
|
|
12622
|
+
loading,
|
|
12623
|
+
onSelectPrice: handleSelectPrice
|
|
12624
|
+
}
|
|
12625
|
+
),
|
|
12626
|
+
isEditMode && loadingProration && /* @__PURE__ */ jsx161("div", { className: "bg-muted/50 rounded-lg p-4 text-sm text-muted-foreground text-center", children: "Loading proration preview..." }),
|
|
12627
|
+
isEditMode && prorationPreview && !loadingProration && /* @__PURE__ */ jsxs101("div", { className: "space-y-4", children: [
|
|
12628
|
+
/* @__PURE__ */ jsx161(ProrationPreview, { preview: prorationPreview }),
|
|
12629
|
+
/* @__PURE__ */ jsxs101("div", { className: "flex justify-end gap-3", children: [
|
|
12630
|
+
/* @__PURE__ */ jsx161(Button, { variant: "outline", onClick: handleCancel, disabled: !!loadingPriceId, children: "Cancel" }),
|
|
12631
|
+
/* @__PURE__ */ jsx161(Button, { onClick: handleConfirmPlanChange, disabled: !!loadingPriceId, children: loadingPriceId ? "Processing..." : "Confirm Plan Change" })
|
|
12632
|
+
] })
|
|
12633
|
+
] })
|
|
12634
|
+
] })
|
|
12635
|
+
] }) });
|
|
12636
|
+
}
|
|
12637
|
+
__name(SubscriptionEditor, "SubscriptionEditor");
|
|
12638
|
+
|
|
12639
|
+
// src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx
|
|
12640
|
+
import { useState as useState59 } from "react";
|
|
12641
|
+
|
|
12642
|
+
// src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx
|
|
12643
|
+
import { useState as useState58 } from "react";
|
|
12644
|
+
|
|
12645
|
+
// src/features/billing/stripe-subscription/components/widgets/SubscriptionStatusBadge.tsx
|
|
12646
|
+
import { jsx as jsx162 } from "react/jsx-runtime";
|
|
12647
|
+
var statusConfig2 = {
|
|
12648
|
+
["active" /* ACTIVE */]: {
|
|
12649
|
+
label: "Active",
|
|
12650
|
+
color: "bg-green-100 text-green-800"
|
|
12651
|
+
},
|
|
12652
|
+
["trialing" /* TRIALING */]: {
|
|
12653
|
+
label: "Trial",
|
|
12654
|
+
color: "bg-blue-100 text-blue-800"
|
|
12655
|
+
},
|
|
12656
|
+
["past_due" /* PAST_DUE */]: {
|
|
12657
|
+
label: "Past Due",
|
|
12658
|
+
color: "bg-red-100 text-red-800"
|
|
12659
|
+
},
|
|
12660
|
+
["canceled" /* CANCELED */]: {
|
|
12661
|
+
label: "Canceled",
|
|
12662
|
+
color: "bg-gray-100 text-gray-800"
|
|
12663
|
+
},
|
|
12664
|
+
["paused" /* PAUSED */]: {
|
|
12665
|
+
label: "Paused",
|
|
12666
|
+
color: "bg-yellow-100 text-yellow-800"
|
|
12667
|
+
},
|
|
12668
|
+
["unpaid" /* UNPAID */]: {
|
|
12669
|
+
label: "Unpaid",
|
|
12670
|
+
color: "bg-orange-100 text-orange-800"
|
|
12671
|
+
},
|
|
12672
|
+
["incomplete" /* INCOMPLETE */]: {
|
|
12673
|
+
label: "Incomplete",
|
|
12674
|
+
color: "bg-gray-100 text-gray-800"
|
|
12675
|
+
},
|
|
12676
|
+
["incomplete_expired" /* INCOMPLETE_EXPIRED */]: {
|
|
12677
|
+
label: "Expired",
|
|
12678
|
+
color: "bg-gray-100 text-gray-800"
|
|
12679
|
+
}
|
|
12680
|
+
};
|
|
12681
|
+
var cancelingConfig = {
|
|
12682
|
+
label: "Canceling",
|
|
12683
|
+
color: "bg-amber-100 text-amber-800"
|
|
12684
|
+
};
|
|
12685
|
+
function SubscriptionStatusBadge({ status, cancelAtPeriodEnd }) {
|
|
12686
|
+
const config = cancelAtPeriodEnd ? cancelingConfig : statusConfig2[status] || statusConfig2["canceled" /* CANCELED */];
|
|
12687
|
+
return /* @__PURE__ */ jsx162("span", { className: `${config.color} text-xs px-2 py-1 rounded-full font-medium`, children: config.label });
|
|
12688
|
+
}
|
|
12689
|
+
__name(SubscriptionStatusBadge, "SubscriptionStatusBadge");
|
|
12690
|
+
|
|
12691
|
+
// src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx
|
|
12692
|
+
import { Fragment as Fragment30, jsx as jsx163, jsxs as jsxs102 } from "react/jsx-runtime";
|
|
12693
|
+
function formatPlanName2(price) {
|
|
12694
|
+
if (!price) return "N/A";
|
|
12695
|
+
const productName = price.product?.name || "";
|
|
12696
|
+
const nickname = price.nickname || "";
|
|
12697
|
+
let interval = "";
|
|
12698
|
+
if (price.recurring?.interval) {
|
|
12699
|
+
const intervalMap = {
|
|
12700
|
+
day: "Daily",
|
|
12701
|
+
week: "Weekly",
|
|
12702
|
+
month: "Monthly",
|
|
12703
|
+
year: "Yearly"
|
|
12704
|
+
};
|
|
12705
|
+
interval = intervalMap[price.recurring.interval] || price.recurring.interval;
|
|
12706
|
+
}
|
|
12707
|
+
const parts = [productName, nickname].filter(Boolean);
|
|
12708
|
+
const planLabel = parts.join(" - ");
|
|
12709
|
+
return interval ? `${planLabel} (${interval})` : planLabel || "N/A";
|
|
12710
|
+
}
|
|
12711
|
+
__name(formatPlanName2, "formatPlanName");
|
|
12712
|
+
function formatBillingAmount(price) {
|
|
12713
|
+
if (!price?.unitAmount) return "N/A";
|
|
12714
|
+
return formatCurrency(price.unitAmount, price.currency);
|
|
12715
|
+
}
|
|
12716
|
+
__name(formatBillingAmount, "formatBillingAmount");
|
|
12717
|
+
function SubscriptionDetails({
|
|
12718
|
+
subscription,
|
|
12719
|
+
open,
|
|
12720
|
+
onOpenChange,
|
|
12721
|
+
onSubscriptionChange
|
|
12722
|
+
}) {
|
|
12723
|
+
const [showEdit, setShowEdit] = useState58(false);
|
|
12724
|
+
const [showCancel, setShowCancel] = useState58(false);
|
|
12725
|
+
const [isProcessing, setIsProcessing] = useState58(false);
|
|
12726
|
+
const handlePause = /* @__PURE__ */ __name(async () => {
|
|
12727
|
+
setIsProcessing(true);
|
|
12728
|
+
try {
|
|
12729
|
+
await StripeSubscriptionService.pauseSubscription({ subscriptionId: subscription.id });
|
|
12730
|
+
onSubscriptionChange();
|
|
12731
|
+
} catch (error) {
|
|
12732
|
+
console.error("[SubscriptionDetails] Failed to pause subscription:", error);
|
|
12733
|
+
} finally {
|
|
12734
|
+
setIsProcessing(false);
|
|
12735
|
+
}
|
|
12736
|
+
}, "handlePause");
|
|
12737
|
+
const handleResume = /* @__PURE__ */ __name(async () => {
|
|
12738
|
+
setIsProcessing(true);
|
|
12739
|
+
try {
|
|
12740
|
+
await StripeSubscriptionService.resumeSubscription({ subscriptionId: subscription.id });
|
|
12741
|
+
onSubscriptionChange();
|
|
12742
|
+
} catch (error) {
|
|
12743
|
+
console.error("[SubscriptionDetails] Failed to resume subscription:", error);
|
|
12744
|
+
} finally {
|
|
12745
|
+
setIsProcessing(false);
|
|
12746
|
+
}
|
|
12747
|
+
}, "handleResume");
|
|
12748
|
+
const handleManageViaPortal = /* @__PURE__ */ __name(async () => {
|
|
12749
|
+
try {
|
|
12750
|
+
const { url } = await StripeCustomerService.createPortalSession();
|
|
12751
|
+
window.open(url, "_blank");
|
|
12752
|
+
} catch (error) {
|
|
12753
|
+
console.error("[SubscriptionDetails] Failed to create portal session:", error);
|
|
12754
|
+
}
|
|
12755
|
+
}, "handleManageViaPortal");
|
|
12756
|
+
const canPause = subscription.status === "active" /* ACTIVE */;
|
|
12757
|
+
const canResume = subscription.status === "paused" /* PAUSED */;
|
|
12758
|
+
const canCancel = subscription.status === "active" /* ACTIVE */ || subscription.status === "trialing" /* TRIALING */ || subscription.status === "paused" /* PAUSED */;
|
|
12759
|
+
return /* @__PURE__ */ jsxs102(Fragment30, { children: [
|
|
12760
|
+
/* @__PURE__ */ jsx163(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs102(DialogContent, { className: "max-w-2xl", children: [
|
|
12761
|
+
/* @__PURE__ */ jsxs102(DialogHeader, { children: [
|
|
12762
|
+
/* @__PURE__ */ jsx163(DialogTitle, { children: "Subscription Details" }),
|
|
12763
|
+
/* @__PURE__ */ jsx163(DialogDescription, { children: "View and manage your subscription" })
|
|
12764
|
+
] }),
|
|
12765
|
+
/* @__PURE__ */ jsxs102("div", { className: "space-y-6", children: [
|
|
12766
|
+
/* @__PURE__ */ jsxs102("div", { className: "flex items-center gap-x-3", children: [
|
|
12767
|
+
/* @__PURE__ */ jsx163("span", { className: "text-sm font-medium text-muted-foreground", children: "Status:" }),
|
|
12768
|
+
/* @__PURE__ */ jsx163(SubscriptionStatusBadge, { status: subscription.status, cancelAtPeriodEnd: subscription.cancelAtPeriodEnd })
|
|
12769
|
+
] }),
|
|
12770
|
+
/* @__PURE__ */ jsxs102("div", { className: "space-y-2", children: [
|
|
12771
|
+
/* @__PURE__ */ jsxs102("div", { className: "flex justify-between", children: [
|
|
12772
|
+
/* @__PURE__ */ jsx163("span", { className: "text-sm font-medium text-muted-foreground", children: "Plan:" }),
|
|
12773
|
+
/* @__PURE__ */ jsx163("span", { className: "font-medium", children: formatPlanName2(subscription.price) })
|
|
12774
|
+
] }),
|
|
12775
|
+
/* @__PURE__ */ jsxs102("div", { className: "flex justify-between", children: [
|
|
12776
|
+
/* @__PURE__ */ jsx163("span", { className: "text-sm font-medium text-muted-foreground", children: "Billing Amount:" }),
|
|
12777
|
+
/* @__PURE__ */ jsx163("span", { className: "font-medium", children: formatBillingAmount(subscription.price) })
|
|
12778
|
+
] })
|
|
12779
|
+
] }),
|
|
12780
|
+
/* @__PURE__ */ jsx163("div", { className: "space-y-2", children: /* @__PURE__ */ jsxs102("div", { className: "flex justify-between", children: [
|
|
12781
|
+
/* @__PURE__ */ jsx163("span", { className: "text-sm font-medium text-muted-foreground", children: "Current Period:" }),
|
|
12782
|
+
/* @__PURE__ */ jsxs102("span", { className: "font-medium", children: [
|
|
12783
|
+
formatDate3(subscription.currentPeriodStart),
|
|
12784
|
+
" - ",
|
|
12785
|
+
formatDate3(subscription.currentPeriodEnd)
|
|
12786
|
+
] })
|
|
12787
|
+
] }) }),
|
|
12788
|
+
subscription.trialEnd && /* @__PURE__ */ jsxs102("div", { className: "flex justify-between", children: [
|
|
12789
|
+
/* @__PURE__ */ jsx163("span", { className: "text-sm font-medium text-muted-foreground", children: "Trial Ends:" }),
|
|
12790
|
+
/* @__PURE__ */ jsx163("span", { className: "font-medium", children: formatDate3(subscription.trialEnd) })
|
|
12791
|
+
] }),
|
|
12792
|
+
subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsx163("div", { className: "bg-yellow-50 border border-yellow-200 rounded-lg p-3", children: /* @__PURE__ */ jsxs102("p", { className: "text-sm text-yellow-800", children: [
|
|
12793
|
+
"This subscription will be canceled at the end of the current period on",
|
|
12794
|
+
" ",
|
|
12795
|
+
formatDate3(subscription.currentPeriodEnd),
|
|
12796
|
+
"."
|
|
12797
|
+
] }) }),
|
|
12798
|
+
/* @__PURE__ */ jsxs102("div", { className: "flex flex-wrap gap-2 pt-4 border-t", children: [
|
|
12799
|
+
/* @__PURE__ */ jsx163(Button, { variant: "default", onClick: () => setShowEdit(true), children: "Change Plan" }),
|
|
12800
|
+
canPause && /* @__PURE__ */ jsx163(Button, { variant: "outline", onClick: handlePause, disabled: isProcessing, children: isProcessing ? "Pausing..." : "Pause" }),
|
|
12801
|
+
canResume && /* @__PURE__ */ jsx163(Button, { variant: "outline", onClick: handleResume, disabled: isProcessing, children: isProcessing ? "Resuming..." : "Resume" }),
|
|
12802
|
+
canCancel && /* @__PURE__ */ jsx163(Button, { variant: "destructive", onClick: () => setShowCancel(true), children: "Cancel" }),
|
|
12803
|
+
/* @__PURE__ */ jsx163(Button, { variant: "outline", onClick: handleManageViaPortal, children: "Manage via Portal" })
|
|
12804
|
+
] })
|
|
12805
|
+
] })
|
|
12806
|
+
] }) }),
|
|
12807
|
+
showEdit && /* @__PURE__ */ jsx163(
|
|
12808
|
+
SubscriptionEditor,
|
|
12809
|
+
{
|
|
12810
|
+
subscription,
|
|
12811
|
+
open: showEdit,
|
|
12812
|
+
onOpenChange: setShowEdit,
|
|
12813
|
+
onSuccess: () => {
|
|
12814
|
+
onSubscriptionChange();
|
|
12815
|
+
setShowEdit(false);
|
|
12816
|
+
}
|
|
12817
|
+
}
|
|
12818
|
+
),
|
|
12819
|
+
showCancel && /* @__PURE__ */ jsx163(
|
|
12820
|
+
CancelSubscriptionDialog,
|
|
12821
|
+
{
|
|
12822
|
+
subscription,
|
|
12823
|
+
open: showCancel,
|
|
12824
|
+
onOpenChange: setShowCancel,
|
|
12825
|
+
onSuccess: () => {
|
|
12826
|
+
onSubscriptionChange();
|
|
12827
|
+
setShowCancel(false);
|
|
12828
|
+
}
|
|
12829
|
+
}
|
|
12830
|
+
)
|
|
12831
|
+
] });
|
|
12832
|
+
}
|
|
12833
|
+
__name(SubscriptionDetails, "SubscriptionDetails");
|
|
12834
|
+
|
|
12835
|
+
// src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx
|
|
12836
|
+
import { Fragment as Fragment31, jsx as jsx164, jsxs as jsxs103 } from "react/jsx-runtime";
|
|
12837
|
+
function formatPlanName3(price) {
|
|
12838
|
+
if (!price) return "N/A";
|
|
12839
|
+
const productName = price.product?.name || "";
|
|
12840
|
+
const nickname = price.nickname || "";
|
|
12841
|
+
let interval = "";
|
|
12842
|
+
if (price.recurring?.interval) {
|
|
12843
|
+
const intervalMap = {
|
|
12844
|
+
day: "Daily",
|
|
12845
|
+
week: "Weekly",
|
|
12846
|
+
month: "Monthly",
|
|
12847
|
+
year: "Yearly"
|
|
12848
|
+
};
|
|
12849
|
+
interval = intervalMap[price.recurring.interval] || price.recurring.interval;
|
|
12850
|
+
}
|
|
12851
|
+
const parts = [productName, nickname].filter(Boolean);
|
|
12852
|
+
const planLabel = parts.join(" - ");
|
|
12853
|
+
return interval ? `${planLabel} (${interval})` : planLabel || "N/A";
|
|
12854
|
+
}
|
|
12855
|
+
__name(formatPlanName3, "formatPlanName");
|
|
12856
|
+
function SubscriptionsList({ subscriptions, onSubscriptionsChange }) {
|
|
12857
|
+
const [selectedSub, setSelectedSub] = useState59(null);
|
|
12858
|
+
const handleRowClick = /* @__PURE__ */ __name((subscription) => {
|
|
12859
|
+
setSelectedSub(subscription);
|
|
12860
|
+
}, "handleRowClick");
|
|
12861
|
+
return /* @__PURE__ */ jsxs103(Fragment31, { children: [
|
|
12862
|
+
/* @__PURE__ */ jsx164("div", { className: "border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxs103(Table, { children: [
|
|
12863
|
+
/* @__PURE__ */ jsx164(TableHeader, { className: "bg-muted", children: /* @__PURE__ */ jsxs103(TableRow, { children: [
|
|
12864
|
+
/* @__PURE__ */ jsx164(TableHead, { children: "Status" }),
|
|
12865
|
+
/* @__PURE__ */ jsx164(TableHead, { children: "Plan" }),
|
|
12866
|
+
/* @__PURE__ */ jsx164(TableHead, { children: "Period" }),
|
|
12867
|
+
/* @__PURE__ */ jsx164(TableHead, { className: "text-right", children: "Amount" })
|
|
12868
|
+
] }) }),
|
|
12869
|
+
/* @__PURE__ */ jsx164(TableBody, { children: subscriptions.map((subscription) => {
|
|
12870
|
+
const price = subscription.price;
|
|
12871
|
+
const amount = price?.unitAmount ? formatCurrency(price.unitAmount, price.currency) : "N/A";
|
|
12872
|
+
const period = `${formatDate3(subscription.currentPeriodStart)} - ${formatDate3(subscription.currentPeriodEnd)}`;
|
|
12873
|
+
return /* @__PURE__ */ jsxs103(
|
|
12874
|
+
TableRow,
|
|
12875
|
+
{
|
|
12876
|
+
onClick: () => handleRowClick(subscription),
|
|
12877
|
+
className: "cursor-pointer hover:bg-muted/50",
|
|
12878
|
+
children: [
|
|
12879
|
+
/* @__PURE__ */ jsx164(TableCell, { children: /* @__PURE__ */ jsx164(SubscriptionStatusBadge, { status: subscription.status, cancelAtPeriodEnd: subscription.cancelAtPeriodEnd }) }),
|
|
12880
|
+
/* @__PURE__ */ jsx164(TableCell, { className: "font-medium", children: formatPlanName3(price) }),
|
|
12881
|
+
/* @__PURE__ */ jsx164(TableCell, { className: "text-muted-foreground text-sm", children: period }),
|
|
12882
|
+
/* @__PURE__ */ jsx164(TableCell, { className: "text-right font-medium", children: amount })
|
|
12883
|
+
]
|
|
12884
|
+
},
|
|
12885
|
+
subscription.id
|
|
12886
|
+
);
|
|
12887
|
+
}) })
|
|
12888
|
+
] }) }),
|
|
12889
|
+
selectedSub && /* @__PURE__ */ jsx164(
|
|
12890
|
+
SubscriptionDetails,
|
|
12891
|
+
{
|
|
12892
|
+
subscription: selectedSub,
|
|
12893
|
+
open: !!selectedSub,
|
|
12894
|
+
onOpenChange: (open) => !open && setSelectedSub(null),
|
|
12895
|
+
onSubscriptionChange: () => {
|
|
12896
|
+
onSubscriptionsChange();
|
|
12897
|
+
setSelectedSub(null);
|
|
12898
|
+
}
|
|
12899
|
+
}
|
|
12900
|
+
)
|
|
12901
|
+
] });
|
|
12902
|
+
}
|
|
12903
|
+
__name(SubscriptionsList, "SubscriptionsList");
|
|
12904
|
+
|
|
12905
|
+
// src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx
|
|
12906
|
+
import { Fragment as Fragment32, jsx as jsx165, jsxs as jsxs104 } from "react/jsx-runtime";
|
|
12907
|
+
function SubscriptionsContainer() {
|
|
12908
|
+
const { confirmPayment, isConfirming } = useConfirmSubscriptionPayment();
|
|
12909
|
+
const [subscriptions, setSubscriptions] = useState60([]);
|
|
12910
|
+
const [loading, setLoading] = useState60(true);
|
|
12911
|
+
const [showCreateSubscription, setShowCreateSubscription] = useState60(false);
|
|
12912
|
+
const [showPaymentMethodEditor, setShowPaymentMethodEditor] = useState60(false);
|
|
12913
|
+
const [products, setProducts] = useState60([]);
|
|
12914
|
+
const [pricesByProduct, setPricesByProduct] = useState60(/* @__PURE__ */ new Map());
|
|
12915
|
+
const [loadingPricing, setLoadingPricing] = useState60(false);
|
|
12916
|
+
const [hasPaymentMethod, setHasPaymentMethod] = useState60(null);
|
|
12917
|
+
const [pendingPriceId, setPendingPriceId] = useState60(null);
|
|
12918
|
+
const [creatingSubscription, setCreatingSubscription] = useState60(false);
|
|
12919
|
+
const [paymentConfirmationState, setPaymentConfirmationState] = useState60("idle");
|
|
12920
|
+
const [paymentError, setPaymentError] = useState60(null);
|
|
12921
|
+
const loadSubscriptions = /* @__PURE__ */ __name(async () => {
|
|
12922
|
+
setLoading(true);
|
|
12923
|
+
try {
|
|
12924
|
+
const fetchedSubscriptions = await StripeSubscriptionService.listSubscriptions();
|
|
12925
|
+
setSubscriptions(fetchedSubscriptions);
|
|
12926
|
+
} catch (error) {
|
|
12927
|
+
console.error("[SubscriptionsContainer] Failed to load subscriptions:", error);
|
|
12928
|
+
} finally {
|
|
12929
|
+
setLoading(false);
|
|
12930
|
+
}
|
|
12931
|
+
}, "loadSubscriptions");
|
|
12932
|
+
const loadPricingData = /* @__PURE__ */ __name(async () => {
|
|
12933
|
+
setLoadingPricing(true);
|
|
12934
|
+
try {
|
|
12935
|
+
const fetchedProducts = await StripeProductService.listProducts({ active: true });
|
|
12936
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
12937
|
+
for (const product of fetchedProducts) {
|
|
12938
|
+
if (product.stripePrices && product.stripePrices.length > 0) {
|
|
12939
|
+
grouped.set(product.id, product.stripePrices);
|
|
12940
|
+
}
|
|
12941
|
+
}
|
|
12942
|
+
setProducts(fetchedProducts);
|
|
12943
|
+
setPricesByProduct(grouped);
|
|
12944
|
+
} catch (error) {
|
|
12945
|
+
console.error("[SubscriptionsContainer] Failed to load pricing data:", error);
|
|
12946
|
+
} finally {
|
|
12947
|
+
setLoadingPricing(false);
|
|
12948
|
+
}
|
|
12949
|
+
}, "loadPricingData");
|
|
12950
|
+
const checkPaymentMethod = /* @__PURE__ */ __name(async () => {
|
|
12951
|
+
try {
|
|
12952
|
+
const paymentMethods = await StripeCustomerService.listPaymentMethods();
|
|
12953
|
+
const hasMethod = paymentMethods.length > 0;
|
|
12954
|
+
setHasPaymentMethod(hasMethod);
|
|
12955
|
+
} catch (error) {
|
|
12956
|
+
console.error("[SubscriptionsContainer] Failed to check payment methods:", error);
|
|
12957
|
+
setHasPaymentMethod(false);
|
|
12958
|
+
}
|
|
12959
|
+
}, "checkPaymentMethod");
|
|
12960
|
+
const createSubscriptionWithPrice = /* @__PURE__ */ __name(async (priceId) => {
|
|
12961
|
+
setCreatingSubscription(true);
|
|
12962
|
+
setPaymentError(null);
|
|
12963
|
+
setPaymentConfirmationState("idle");
|
|
12964
|
+
try {
|
|
12965
|
+
const result = await StripeSubscriptionService.createSubscription({
|
|
12966
|
+
id: v46(),
|
|
12967
|
+
priceId
|
|
12968
|
+
});
|
|
12969
|
+
if (result.meta.requiresAction && result.meta.clientSecret) {
|
|
12970
|
+
setPaymentConfirmationState("confirming");
|
|
12971
|
+
const confirmation = await confirmPayment(result.meta.clientSecret);
|
|
12972
|
+
if (!confirmation.success) {
|
|
12973
|
+
console.error("[SubscriptionsContainer] Payment confirmation failed:", confirmation.error);
|
|
12974
|
+
setPaymentConfirmationState("error");
|
|
12975
|
+
setPaymentError(confirmation.error || "Payment confirmation failed");
|
|
12976
|
+
setCreatingSubscription(false);
|
|
12977
|
+
return;
|
|
12978
|
+
}
|
|
12979
|
+
await StripeSubscriptionService.syncSubscription({
|
|
12980
|
+
subscriptionId: result.subscription.id
|
|
12981
|
+
});
|
|
12982
|
+
}
|
|
12983
|
+
setPaymentConfirmationState("success");
|
|
12984
|
+
await loadSubscriptions();
|
|
12985
|
+
} catch (error) {
|
|
12986
|
+
console.error("[SubscriptionsContainer] Failed to create subscription:", error);
|
|
12987
|
+
if (error?.status === 402 || error?.response?.status === 402) {
|
|
12988
|
+
setPendingPriceId(priceId);
|
|
12989
|
+
setHasPaymentMethod(false);
|
|
12990
|
+
setShowPaymentMethodEditor(true);
|
|
12991
|
+
} else {
|
|
12992
|
+
setPaymentConfirmationState("error");
|
|
12993
|
+
setPaymentError(error?.message || "Failed to create subscription");
|
|
12994
|
+
}
|
|
12995
|
+
} finally {
|
|
12996
|
+
setCreatingSubscription(false);
|
|
12997
|
+
}
|
|
12998
|
+
}, "createSubscriptionWithPrice");
|
|
12999
|
+
const handleSelectPrice = /* @__PURE__ */ __name(async (price) => {
|
|
13000
|
+
const priceId = price.id;
|
|
13001
|
+
if (!hasPaymentMethod) {
|
|
13002
|
+
setPendingPriceId(priceId);
|
|
13003
|
+
setShowPaymentMethodEditor(true);
|
|
13004
|
+
return;
|
|
13005
|
+
}
|
|
13006
|
+
await createSubscriptionWithPrice(priceId);
|
|
13007
|
+
}, "handleSelectPrice");
|
|
13008
|
+
const handlePaymentMethodSuccess = /* @__PURE__ */ __name(async () => {
|
|
13009
|
+
setShowPaymentMethodEditor(false);
|
|
13010
|
+
setHasPaymentMethod(true);
|
|
13011
|
+
if (pendingPriceId) {
|
|
13012
|
+
await createSubscriptionWithPrice(pendingPriceId);
|
|
13013
|
+
setPendingPriceId(null);
|
|
13014
|
+
}
|
|
13015
|
+
}, "handlePaymentMethodSuccess");
|
|
13016
|
+
useEffect45(() => {
|
|
13017
|
+
loadSubscriptions();
|
|
13018
|
+
}, []);
|
|
13019
|
+
useEffect45(() => {
|
|
13020
|
+
if (!loading && subscriptions.length === 0) {
|
|
13021
|
+
loadPricingData();
|
|
13022
|
+
checkPaymentMethod();
|
|
13023
|
+
}
|
|
13024
|
+
}, [loading, subscriptions.length]);
|
|
13025
|
+
const criticalSubscriptions = subscriptions.filter(
|
|
13026
|
+
(sub) => sub.status === "past_due" /* PAST_DUE */ || sub.status === "trialing" /* TRIALING */ && sub.trialEnd && new Date(sub.trialEnd).getTime() - (/* @__PURE__ */ new Date()).getTime() <= 7 * 24 * 60 * 60 * 1e3
|
|
13027
|
+
);
|
|
13028
|
+
if (loading) {
|
|
13029
|
+
return /* @__PURE__ */ jsx165("div", { className: "flex h-64 items-center justify-center", children: /* @__PURE__ */ jsx165("p", { className: "text-muted-foreground", children: "Loading subscriptions..." }) });
|
|
13030
|
+
}
|
|
13031
|
+
return /* @__PURE__ */ jsxs104("div", { className: "flex w-full flex-col gap-y-6", children: [
|
|
13032
|
+
/* @__PURE__ */ jsxs104("div", { className: "flex items-center justify-between", children: [
|
|
13033
|
+
/* @__PURE__ */ jsxs104("div", { className: "flex items-center gap-x-3", children: [
|
|
13034
|
+
/* @__PURE__ */ jsx165(CreditCard3, { className: "h-8 w-8" }),
|
|
13035
|
+
/* @__PURE__ */ jsx165("h1", { className: "text-3xl font-bold", children: "Subscriptions" })
|
|
13036
|
+
] }),
|
|
13037
|
+
subscriptions.length > 0 && /* @__PURE__ */ jsx165(Button, { onClick: () => setShowCreateSubscription(true), children: "Subscribe to a Plan" })
|
|
13038
|
+
] }),
|
|
13039
|
+
criticalSubscriptions.map((subscription) => /* @__PURE__ */ jsx165(BillingAlertBanner, { subscription }, subscription.id)),
|
|
13040
|
+
subscriptions.length === 0 && /* @__PURE__ */ jsxs104("div", { className: "space-y-6", children: [
|
|
13041
|
+
(paymentConfirmationState === "confirming" || isConfirming) && /* @__PURE__ */ jsxs104("div", { className: "flex flex-col items-center justify-center py-12 space-y-4 bg-muted/50 rounded-lg", children: [
|
|
13042
|
+
/* @__PURE__ */ jsx165(Loader23, { className: "h-8 w-8 animate-spin text-primary" }),
|
|
13043
|
+
/* @__PURE__ */ jsxs104("div", { className: "text-center", children: [
|
|
13044
|
+
/* @__PURE__ */ jsx165("p", { className: "font-medium", children: "Processing payment..." }),
|
|
13045
|
+
/* @__PURE__ */ jsx165("p", { className: "text-sm text-muted-foreground", children: "Please complete any verification if prompted." })
|
|
13046
|
+
] })
|
|
13047
|
+
] }),
|
|
13048
|
+
paymentConfirmationState === "success" && /* @__PURE__ */ jsxs104("div", { className: "flex flex-col items-center justify-center py-8 space-y-4 bg-green-50 rounded-lg border border-green-200", children: [
|
|
13049
|
+
/* @__PURE__ */ jsx165(CheckCircle2, { className: "h-12 w-12 text-green-500" }),
|
|
13050
|
+
/* @__PURE__ */ jsxs104("div", { className: "text-center", children: [
|
|
13051
|
+
/* @__PURE__ */ jsx165("p", { className: "font-medium text-green-600", children: "Payment successful!" }),
|
|
13052
|
+
/* @__PURE__ */ jsx165("p", { className: "text-sm text-muted-foreground", children: "Your subscription is now active." })
|
|
13053
|
+
] })
|
|
13054
|
+
] }),
|
|
13055
|
+
paymentConfirmationState === "error" && /* @__PURE__ */ jsxs104(Alert, { variant: "destructive", children: [
|
|
13056
|
+
/* @__PURE__ */ jsx165(AlertTitle, { children: "Payment Failed" }),
|
|
13057
|
+
/* @__PURE__ */ jsxs104(AlertDescription, { className: "mt-2", children: [
|
|
13058
|
+
/* @__PURE__ */ jsx165("p", { className: "mb-4", children: paymentError || "We couldn't process your payment. Please try again." }),
|
|
13059
|
+
/* @__PURE__ */ jsx165(
|
|
13060
|
+
Button,
|
|
13061
|
+
{
|
|
13062
|
+
onClick: () => {
|
|
13063
|
+
setPaymentConfirmationState("idle");
|
|
13064
|
+
setPaymentError(null);
|
|
13065
|
+
},
|
|
13066
|
+
variant: "outline",
|
|
13067
|
+
children: "Try Again"
|
|
13068
|
+
}
|
|
13069
|
+
)
|
|
13070
|
+
] })
|
|
13071
|
+
] }),
|
|
13072
|
+
paymentConfirmationState === "idle" && !isConfirming && /* @__PURE__ */ jsxs104(Fragment32, { children: [
|
|
13073
|
+
/* @__PURE__ */ jsxs104("div", { className: "text-center", children: [
|
|
13074
|
+
/* @__PURE__ */ jsx165(CreditCard3, { className: "text-muted-foreground mx-auto h-16 w-16 mb-4" }),
|
|
13075
|
+
/* @__PURE__ */ jsx165("h3", { className: "mb-2 text-xl font-semibold", children: "Choose Your Plan" }),
|
|
13076
|
+
/* @__PURE__ */ jsx165("p", { className: "text-muted-foreground mb-6", children: "Select a subscription plan to get started with our services." })
|
|
13077
|
+
] }),
|
|
13078
|
+
/* @__PURE__ */ jsx165(
|
|
13079
|
+
PricingCardsGrid,
|
|
13080
|
+
{
|
|
13081
|
+
products,
|
|
13082
|
+
pricesByProduct,
|
|
13083
|
+
loading: loadingPricing,
|
|
13084
|
+
loadingPriceId: creatingSubscription ? pendingPriceId ?? void 0 : void 0,
|
|
13085
|
+
onSelectPrice: handleSelectPrice
|
|
13086
|
+
}
|
|
13087
|
+
)
|
|
13088
|
+
] })
|
|
13089
|
+
] }),
|
|
13090
|
+
subscriptions.length > 0 && /* @__PURE__ */ jsx165(SubscriptionsList, { subscriptions, onSubscriptionsChange: loadSubscriptions }),
|
|
13091
|
+
showCreateSubscription && /* @__PURE__ */ jsx165(
|
|
13092
|
+
SubscriptionEditor,
|
|
13093
|
+
{
|
|
13094
|
+
open: showCreateSubscription,
|
|
13095
|
+
onOpenChange: setShowCreateSubscription,
|
|
13096
|
+
onSuccess: loadSubscriptions,
|
|
13097
|
+
onAddPaymentMethod: () => {
|
|
13098
|
+
setShowCreateSubscription(false);
|
|
13099
|
+
setShowPaymentMethodEditor(true);
|
|
13100
|
+
}
|
|
13101
|
+
}
|
|
13102
|
+
),
|
|
13103
|
+
showPaymentMethodEditor && /* @__PURE__ */ jsx165(
|
|
13104
|
+
PaymentMethodEditor,
|
|
13105
|
+
{
|
|
13106
|
+
open: showPaymentMethodEditor,
|
|
13107
|
+
onOpenChange: (open) => {
|
|
13108
|
+
setShowPaymentMethodEditor(open);
|
|
13109
|
+
if (!open) {
|
|
13110
|
+
setPendingPriceId(null);
|
|
13111
|
+
}
|
|
13112
|
+
},
|
|
13113
|
+
onSuccess: handlePaymentMethodSuccess
|
|
13114
|
+
}
|
|
13115
|
+
)
|
|
13116
|
+
] });
|
|
13117
|
+
}
|
|
13118
|
+
__name(SubscriptionsContainer, "SubscriptionsContainer");
|
|
13119
|
+
|
|
13120
|
+
// src/features/billing/stripe-usage/components/containers/UsageContainer.tsx
|
|
13121
|
+
import { Activity as Activity3 } from "lucide-react";
|
|
13122
|
+
import { useEffect as useEffect46, useState as useState61 } from "react";
|
|
13123
|
+
|
|
13124
|
+
// src/features/billing/stripe-usage/components/details/UsageSummaryCard.tsx
|
|
13125
|
+
import { Activity as Activity2 } from "lucide-react";
|
|
13126
|
+
import { jsx as jsx166, jsxs as jsxs105 } from "react/jsx-runtime";
|
|
13127
|
+
function getProgressColor(percentage) {
|
|
13128
|
+
if (percentage === null) return "bg-blue-500";
|
|
13129
|
+
if (percentage >= 90) return "bg-red-500";
|
|
13130
|
+
if (percentage >= 75) return "bg-orange-500";
|
|
13131
|
+
return "bg-green-500";
|
|
13132
|
+
}
|
|
13133
|
+
__name(getProgressColor, "getProgressColor");
|
|
13134
|
+
function formatDate4(date) {
|
|
13135
|
+
if (!date) return "N/A";
|
|
13136
|
+
try {
|
|
13137
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
13138
|
+
month: "short",
|
|
13139
|
+
day: "numeric"
|
|
13140
|
+
}).format(new Date(date));
|
|
13141
|
+
} catch (error) {
|
|
13142
|
+
return "Invalid Date";
|
|
13143
|
+
}
|
|
13144
|
+
}
|
|
13145
|
+
__name(formatDate4, "formatDate");
|
|
13146
|
+
function UsageSummaryCard({ meter, summary }) {
|
|
13147
|
+
const currentUsage = summary?.aggregatedValue ?? 0;
|
|
13148
|
+
const limit = meter.limit;
|
|
13149
|
+
const percentage = limit && limit > 0 ? currentUsage / limit * 100 : null;
|
|
13150
|
+
const progressColor = getProgressColor(percentage);
|
|
13151
|
+
const progressWidth = percentage !== null ? Math.min(percentage, 100) : 0;
|
|
13152
|
+
const displayName = meter.displayName || meter.eventName;
|
|
13153
|
+
const hasLimit = limit !== null && limit !== void 0;
|
|
13154
|
+
return /* @__PURE__ */ jsxs105(Card, { children: [
|
|
13155
|
+
/* @__PURE__ */ jsxs105(CardHeader, { className: "flex flex-row items-center gap-x-3 pb-3", children: [
|
|
13156
|
+
/* @__PURE__ */ jsx166("div", { className: "flex h-10 w-10 items-center justify-center rounded-lg bg-blue-100 text-blue-600", children: /* @__PURE__ */ jsx166(Activity2, { className: "h-5 w-5" }) }),
|
|
13157
|
+
/* @__PURE__ */ jsxs105("div", { className: "flex flex-col", children: [
|
|
13158
|
+
/* @__PURE__ */ jsx166("h3", { className: "font-semibold", children: displayName }),
|
|
13159
|
+
/* @__PURE__ */ jsx166("p", { className: "text-xs text-gray-500", children: meter.id })
|
|
13160
|
+
] })
|
|
13161
|
+
] }),
|
|
13162
|
+
/* @__PURE__ */ jsxs105(CardContent, { className: "flex flex-col gap-y-4", children: [
|
|
13163
|
+
/* @__PURE__ */ jsxs105("div", { children: [
|
|
13164
|
+
/* @__PURE__ */ jsx166("p", { className: "text-3xl font-bold", children: currentUsage.toLocaleString() }),
|
|
13165
|
+
hasLimit && /* @__PURE__ */ jsxs105("p", { className: "text-sm text-gray-500", children: [
|
|
13166
|
+
"of ",
|
|
13167
|
+
limit.toLocaleString(),
|
|
13168
|
+
" used"
|
|
13169
|
+
] })
|
|
13170
|
+
] }),
|
|
13171
|
+
hasLimit ? /* @__PURE__ */ jsxs105("div", { className: "flex flex-col gap-y-2", children: [
|
|
13172
|
+
/* @__PURE__ */ jsx166("div", { className: "h-2 w-full overflow-hidden rounded-full bg-gray-200", children: /* @__PURE__ */ jsx166("div", { className: `h-full transition-all ${progressColor}`, style: { width: `${progressWidth}%` } }) }),
|
|
13173
|
+
/* @__PURE__ */ jsxs105("p", { className: "text-sm text-gray-500", children: [
|
|
13174
|
+
percentage?.toFixed(1),
|
|
13175
|
+
"% used"
|
|
13176
|
+
] })
|
|
13177
|
+
] }) : /* @__PURE__ */ jsx166("p", { className: "text-sm text-gray-500", children: "No limit set" }),
|
|
13178
|
+
summary && summary.start && summary.end && /* @__PURE__ */ jsx166("div", { className: "border-t pt-3", children: /* @__PURE__ */ jsxs105("p", { className: "text-xs text-gray-500", children: [
|
|
13179
|
+
"Period: ",
|
|
13180
|
+
formatDate4(summary.start),
|
|
13181
|
+
" - ",
|
|
13182
|
+
formatDate4(summary.end)
|
|
13183
|
+
] }) })
|
|
13184
|
+
] })
|
|
13185
|
+
] });
|
|
13186
|
+
}
|
|
13187
|
+
__name(UsageSummaryCard, "UsageSummaryCard");
|
|
13188
|
+
|
|
13189
|
+
// src/features/billing/stripe-usage/components/widgets/UsageSummaryCards.tsx
|
|
13190
|
+
import { jsx as jsx167 } from "react/jsx-runtime";
|
|
13191
|
+
function UsageSummaryCards({ meters, summaries }) {
|
|
13192
|
+
return /* @__PURE__ */ jsx167("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3", children: meters.map((meter) => /* @__PURE__ */ jsx167(UsageSummaryCard, { meter, summary: summaries[meter.id] || null }, meter.id)) });
|
|
13193
|
+
}
|
|
13194
|
+
__name(UsageSummaryCards, "UsageSummaryCards");
|
|
13195
|
+
|
|
13196
|
+
// src/features/billing/stripe-usage/components/containers/UsageContainer.tsx
|
|
13197
|
+
import { jsx as jsx168, jsxs as jsxs106 } from "react/jsx-runtime";
|
|
13198
|
+
function UsageContainer() {
|
|
13199
|
+
const [meters, setMeters] = useState61([]);
|
|
13200
|
+
const [summaries, setSummaries] = useState61({});
|
|
13201
|
+
const [loading, setLoading] = useState61(true);
|
|
13202
|
+
const [subscriptions, setSubscriptions] = useState61([]);
|
|
13203
|
+
useEffect46(() => {
|
|
13204
|
+
loadUsageData();
|
|
13205
|
+
}, []);
|
|
13206
|
+
const loadUsageData = /* @__PURE__ */ __name(async () => {
|
|
13207
|
+
setLoading(true);
|
|
13208
|
+
try {
|
|
13209
|
+
const fetchedSubscriptions = await StripeSubscriptionService.listSubscriptions();
|
|
13210
|
+
setSubscriptions(fetchedSubscriptions);
|
|
13211
|
+
const hasMeteredSubscriptions2 = fetchedSubscriptions.some((sub) => sub.price?.recurring?.usageType === "metered");
|
|
13212
|
+
if (!hasMeteredSubscriptions2) {
|
|
13213
|
+
setLoading(false);
|
|
13214
|
+
return;
|
|
13215
|
+
}
|
|
13216
|
+
const fetchedMeters = await StripeUsageService.listMeters();
|
|
13217
|
+
setMeters(fetchedMeters);
|
|
13218
|
+
const summariesMap = {};
|
|
13219
|
+
const now = /* @__PURE__ */ new Date();
|
|
13220
|
+
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
13221
|
+
const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);
|
|
13222
|
+
for (const meter of fetchedMeters) {
|
|
13223
|
+
try {
|
|
13224
|
+
const meterSummaries = await StripeUsageService.getMeterSummaries({
|
|
13225
|
+
meterId: meter.id,
|
|
13226
|
+
startTime: startOfMonth,
|
|
13227
|
+
endTime: endOfMonth
|
|
13228
|
+
});
|
|
13229
|
+
summariesMap[meter.id] = meterSummaries.length > 0 ? meterSummaries[0] : null;
|
|
13230
|
+
} catch (error) {
|
|
13231
|
+
console.error(`[UsageContainer] Failed to load summaries for meter ${meter.id}:`, error);
|
|
13232
|
+
summariesMap[meter.id] = null;
|
|
13233
|
+
}
|
|
13234
|
+
}
|
|
13235
|
+
setSummaries(summariesMap);
|
|
13236
|
+
} catch (error) {
|
|
13237
|
+
console.error("[UsageContainer] Failed to load usage data:", error);
|
|
13238
|
+
} finally {
|
|
13239
|
+
setLoading(false);
|
|
13240
|
+
}
|
|
13241
|
+
}, "loadUsageData");
|
|
13242
|
+
const hasMeteredSubscriptions = subscriptions.some((sub) => sub.price?.recurring?.usageType === "metered");
|
|
13243
|
+
if (!loading && !hasMeteredSubscriptions) {
|
|
13244
|
+
return null;
|
|
13245
|
+
}
|
|
13246
|
+
if (loading) {
|
|
13247
|
+
return /* @__PURE__ */ jsx168("div", { className: "flex h-64 items-center justify-center", children: /* @__PURE__ */ jsx168("p", { className: "text-muted-foreground", children: "Loading usage data..." }) });
|
|
13248
|
+
}
|
|
13249
|
+
return /* @__PURE__ */ jsxs106("div", { className: "flex w-full flex-col gap-y-6", children: [
|
|
13250
|
+
/* @__PURE__ */ jsxs106("div", { className: "flex items-center gap-x-3", children: [
|
|
13251
|
+
/* @__PURE__ */ jsx168(Activity3, { className: "h-8 w-8" }),
|
|
13252
|
+
/* @__PURE__ */ jsx168("h1", { className: "text-3xl font-bold", children: "Usage Tracking" })
|
|
13253
|
+
] }),
|
|
13254
|
+
meters.length === 0 && /* @__PURE__ */ jsxs106("div", { className: "bg-muted/50 flex flex-col items-center justify-center gap-y-4 rounded-lg border-2 border-dashed border-gray-300 p-12", children: [
|
|
13255
|
+
/* @__PURE__ */ jsx168(Activity3, { className: "text-muted-foreground h-16 w-16" }),
|
|
13256
|
+
/* @__PURE__ */ jsxs106("div", { className: "text-center", children: [
|
|
13257
|
+
/* @__PURE__ */ jsx168("h3", { className: "mb-2 text-xl font-semibold", children: "No usage meters configured" }),
|
|
13258
|
+
/* @__PURE__ */ jsx168("p", { className: "text-muted-foreground", children: "Usage tracking will appear here when you have metered subscriptions with configured meters." })
|
|
13259
|
+
] })
|
|
13260
|
+
] }),
|
|
13261
|
+
meters.length > 0 && /* @__PURE__ */ jsx168(UsageSummaryCards, { meters, summaries })
|
|
13262
|
+
] });
|
|
13263
|
+
}
|
|
13264
|
+
__name(UsageContainer, "UsageContainer");
|
|
13265
|
+
|
|
13266
|
+
// src/features/billing/stripe-usage/components/lists/UsageHistoryTable.tsx
|
|
13267
|
+
import { jsx as jsx169, jsxs as jsxs107 } from "react/jsx-runtime";
|
|
13268
|
+
function formatDateTime(date) {
|
|
13269
|
+
if (!date) return "N/A";
|
|
13270
|
+
const dateObj = typeof date === "string" ? new Date(date) : date;
|
|
13271
|
+
try {
|
|
13272
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
13273
|
+
month: "short",
|
|
13274
|
+
day: "numeric",
|
|
13275
|
+
year: "numeric",
|
|
13276
|
+
hour: "numeric",
|
|
13277
|
+
minute: "2-digit"
|
|
13278
|
+
}).format(dateObj);
|
|
13279
|
+
} catch (error) {
|
|
13280
|
+
return "Invalid Date";
|
|
13281
|
+
}
|
|
13282
|
+
}
|
|
13283
|
+
__name(formatDateTime, "formatDateTime");
|
|
13284
|
+
function UsageHistoryTable({ usageRecords }) {
|
|
13285
|
+
if (usageRecords.length === 0) {
|
|
13286
|
+
return /* @__PURE__ */ jsx169("div", { className: "rounded-lg border p-8 text-center", children: /* @__PURE__ */ jsx169("p", { className: "text-muted-foreground", children: "No usage history available." }) });
|
|
13287
|
+
}
|
|
13288
|
+
return /* @__PURE__ */ jsxs107("div", { className: "flex w-full flex-col gap-y-4", children: [
|
|
13289
|
+
/* @__PURE__ */ jsx169("h2", { className: "text-xl font-semibold", children: "Usage History" }),
|
|
13290
|
+
/* @__PURE__ */ jsx169("div", { className: "overflow-hidden rounded-lg border", children: /* @__PURE__ */ jsxs107(Table, { children: [
|
|
13291
|
+
/* @__PURE__ */ jsx169(TableHeader, { className: "bg-muted", children: /* @__PURE__ */ jsxs107(TableRow, { children: [
|
|
13292
|
+
/* @__PURE__ */ jsx169(TableHead, { children: "Date & Time" }),
|
|
13293
|
+
/* @__PURE__ */ jsx169(TableHead, { children: "Meter Event" }),
|
|
13294
|
+
/* @__PURE__ */ jsx169(TableHead, { className: "text-right", children: "Quantity" }),
|
|
13295
|
+
/* @__PURE__ */ jsx169(TableHead, { children: "Event ID" })
|
|
13296
|
+
] }) }),
|
|
13297
|
+
/* @__PURE__ */ jsx169(TableBody, { children: usageRecords.map((record) => {
|
|
13298
|
+
const dateTime = formatDateTime(record.timestamp);
|
|
13299
|
+
const quantity = record.quantity.toLocaleString();
|
|
13300
|
+
return /* @__PURE__ */ jsxs107(TableRow, { children: [
|
|
13301
|
+
/* @__PURE__ */ jsx169(TableCell, { className: "font-medium", children: dateTime }),
|
|
13302
|
+
/* @__PURE__ */ jsx169(TableCell, { className: "text-muted-foreground", children: record.meterEventName }),
|
|
13303
|
+
/* @__PURE__ */ jsx169(TableCell, { className: "text-right font-medium", children: quantity }),
|
|
13304
|
+
/* @__PURE__ */ jsx169(TableCell, { className: "text-muted-foreground text-sm font-mono", children: record.stripeEventId })
|
|
13305
|
+
] }, record.id);
|
|
13306
|
+
}) })
|
|
13307
|
+
] }) })
|
|
13308
|
+
] });
|
|
13309
|
+
}
|
|
13310
|
+
__name(UsageHistoryTable, "UsageHistoryTable");
|
|
13311
|
+
|
|
13312
|
+
// src/features/billing/components/modals/BillingDetailModal.tsx
|
|
13313
|
+
import { jsx as jsx170, jsxs as jsxs108 } from "react/jsx-runtime";
|
|
13314
|
+
function BillingDetailModal({
|
|
13315
|
+
open,
|
|
13316
|
+
onOpenChange,
|
|
13317
|
+
title,
|
|
13318
|
+
children,
|
|
13319
|
+
className
|
|
13320
|
+
}) {
|
|
13321
|
+
return /* @__PURE__ */ jsx170(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs108(DialogContent, { className: className ?? "max-w-4xl max-h-[90vh] overflow-y-auto", children: [
|
|
13322
|
+
/* @__PURE__ */ jsx170(DialogHeader, { children: /* @__PURE__ */ jsx170(DialogTitle, { children: title }) }),
|
|
13323
|
+
children
|
|
13324
|
+
] }) });
|
|
13325
|
+
}
|
|
13326
|
+
__name(BillingDetailModal, "BillingDetailModal");
|
|
13327
|
+
|
|
13328
|
+
// src/features/billing/components/widgets/BillingAlertBanner.tsx
|
|
13329
|
+
import { AlertCircle } from "lucide-react";
|
|
13330
|
+
import { jsx as jsx171, jsxs as jsxs109 } from "react/jsx-runtime";
|
|
13331
|
+
function BillingAlertBanner({ subscription, onUpdatePayment, onAddPayment }) {
|
|
13332
|
+
if (subscription.status === "past_due" /* PAST_DUE */) {
|
|
13333
|
+
return /* @__PURE__ */ jsxs109("div", { className: "bg-red-50 border border-red-200 rounded-lg p-4 flex items-start gap-x-3", children: [
|
|
13334
|
+
/* @__PURE__ */ jsx171(AlertCircle, { className: "h-5 w-5 text-red-600 mt-0.5" }),
|
|
13335
|
+
/* @__PURE__ */ jsxs109("div", { className: "flex-1", children: [
|
|
13336
|
+
/* @__PURE__ */ jsx171("h3", { className: "font-semibold text-red-900", children: "Payment Failed" }),
|
|
13337
|
+
/* @__PURE__ */ jsx171("p", { className: "text-sm text-red-700 mt-1", children: "Your last payment failed. Please update your payment method to avoid service interruption." })
|
|
13338
|
+
] }),
|
|
13339
|
+
onUpdatePayment && /* @__PURE__ */ jsx171(Button, { variant: "outline", size: "sm", onClick: onUpdatePayment, className: "border-red-300 text-red-700", children: "Update Payment Method" })
|
|
13340
|
+
] });
|
|
13341
|
+
}
|
|
13342
|
+
if (subscription.status === "trialing" /* TRIALING */ && subscription.trialEnd) {
|
|
13343
|
+
const trialEnd = new Date(subscription.trialEnd);
|
|
13344
|
+
const now = /* @__PURE__ */ new Date();
|
|
13345
|
+
const daysRemaining = Math.ceil((trialEnd.getTime() - now.getTime()) / (1e3 * 60 * 60 * 24));
|
|
13346
|
+
if (daysRemaining <= 7) {
|
|
13347
|
+
return /* @__PURE__ */ jsxs109("div", { className: "bg-yellow-50 border border-yellow-200 rounded-lg p-4 flex items-start gap-x-3", children: [
|
|
13348
|
+
/* @__PURE__ */ jsx171(AlertCircle, { className: "h-5 w-5 text-yellow-600 mt-0.5" }),
|
|
13349
|
+
/* @__PURE__ */ jsxs109("div", { className: "flex-1", children: [
|
|
13350
|
+
/* @__PURE__ */ jsx171("h3", { className: "font-semibold text-yellow-900", children: "Trial Ending Soon" }),
|
|
13351
|
+
/* @__PURE__ */ jsxs109("p", { className: "text-sm text-yellow-700 mt-1", children: [
|
|
13352
|
+
"Your trial ends in ",
|
|
13353
|
+
daysRemaining,
|
|
13354
|
+
" ",
|
|
13355
|
+
daysRemaining === 1 ? "day" : "days",
|
|
13356
|
+
". Add a payment method to continue your subscription."
|
|
13357
|
+
] })
|
|
13358
|
+
] }),
|
|
13359
|
+
onAddPayment && /* @__PURE__ */ jsx171(Button, { variant: "outline", size: "sm", onClick: onAddPayment, className: "border-yellow-300 text-yellow-700", children: "Add Payment Method" })
|
|
13360
|
+
] });
|
|
13361
|
+
}
|
|
13362
|
+
}
|
|
13363
|
+
return null;
|
|
13364
|
+
}
|
|
13365
|
+
__name(BillingAlertBanner, "BillingAlertBanner");
|
|
13366
|
+
|
|
13367
|
+
// src/features/billing/components/containers/BillingDashboardContainer.tsx
|
|
13368
|
+
import { Fragment as Fragment33, jsx as jsx172, jsxs as jsxs110 } from "react/jsx-runtime";
|
|
13369
|
+
function BillingDashboardContainer() {
|
|
13370
|
+
const [data, setData] = useState62({
|
|
13371
|
+
customer: null,
|
|
13372
|
+
subscriptions: [],
|
|
13373
|
+
paymentMethods: [],
|
|
13374
|
+
invoices: [],
|
|
13375
|
+
meters: [],
|
|
13376
|
+
meterSummaries: {}
|
|
13377
|
+
});
|
|
13378
|
+
const [loading, setLoading] = useState62({
|
|
13379
|
+
customer: true,
|
|
13380
|
+
subscriptions: true,
|
|
13381
|
+
paymentMethods: true,
|
|
13382
|
+
invoices: true,
|
|
13383
|
+
usage: true
|
|
13384
|
+
});
|
|
13385
|
+
const [errors, setErrors] = useState62({
|
|
13386
|
+
customer: null,
|
|
13387
|
+
subscriptions: null,
|
|
13388
|
+
paymentMethods: null,
|
|
13389
|
+
invoices: null,
|
|
13390
|
+
usage: null
|
|
13391
|
+
});
|
|
13392
|
+
const [activeModal, setActiveModal] = useState62(null);
|
|
13393
|
+
const [noCustomerExists, setNoCustomerExists] = useState62(false);
|
|
13394
|
+
const [creatingCustomer, setCreatingCustomer] = useState62(false);
|
|
13395
|
+
const hasMeteredSubscriptions = useCallback18(() => {
|
|
13396
|
+
return data.subscriptions.some((sub) => sub.price?.recurring?.usageType === "metered");
|
|
13397
|
+
}, [data.subscriptions]);
|
|
13398
|
+
const fetchAllData = useCallback18(async () => {
|
|
13399
|
+
setNoCustomerExists(false);
|
|
13400
|
+
let customer = null;
|
|
13401
|
+
try {
|
|
13402
|
+
customer = await StripeCustomerService.getCustomer();
|
|
13403
|
+
setData((prev) => ({ ...prev, customer }));
|
|
13404
|
+
setErrors((prev) => ({ ...prev, customer: null }));
|
|
13405
|
+
setNoCustomerExists(false);
|
|
13406
|
+
} catch (error) {
|
|
13407
|
+
console.error("[BillingDashboard] Failed to load customer:", error);
|
|
13408
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
13409
|
+
if (errorMessage.includes("Not Found") || errorMessage.includes("not found")) {
|
|
13410
|
+
setNoCustomerExists(true);
|
|
13411
|
+
setLoading({
|
|
13412
|
+
customer: false,
|
|
13413
|
+
subscriptions: false,
|
|
13414
|
+
paymentMethods: false,
|
|
13415
|
+
invoices: false,
|
|
13416
|
+
usage: false
|
|
13417
|
+
});
|
|
13418
|
+
return;
|
|
13419
|
+
}
|
|
13420
|
+
setErrors((prev) => ({ ...prev, customer: "Failed to load billing account" }));
|
|
13421
|
+
} finally {
|
|
13422
|
+
setLoading((prev) => ({ ...prev, customer: false }));
|
|
13423
|
+
}
|
|
13424
|
+
if (customer) {
|
|
13425
|
+
const fetchSubscriptions = /* @__PURE__ */ __name(async () => {
|
|
13426
|
+
try {
|
|
13427
|
+
const subscriptions2 = await StripeSubscriptionService.listSubscriptions();
|
|
13428
|
+
setData((prev) => ({ ...prev, subscriptions: subscriptions2 }));
|
|
13429
|
+
setErrors((prev) => ({ ...prev, subscriptions: null }));
|
|
13430
|
+
return subscriptions2;
|
|
13431
|
+
} catch (error) {
|
|
13432
|
+
console.error("[BillingDashboard] Failed to load subscriptions:", error);
|
|
13433
|
+
setErrors((prev) => ({ ...prev, subscriptions: "Failed to load subscriptions" }));
|
|
13434
|
+
return [];
|
|
13435
|
+
} finally {
|
|
13436
|
+
setLoading((prev) => ({ ...prev, subscriptions: false }));
|
|
13437
|
+
}
|
|
13438
|
+
}, "fetchSubscriptions");
|
|
13439
|
+
const fetchPaymentMethods = /* @__PURE__ */ __name(async () => {
|
|
13440
|
+
try {
|
|
13441
|
+
const paymentMethods = await StripeCustomerService.listPaymentMethods();
|
|
13442
|
+
setData((prev) => ({ ...prev, paymentMethods }));
|
|
13443
|
+
setErrors((prev) => ({ ...prev, paymentMethods: null }));
|
|
13444
|
+
} catch (error) {
|
|
13445
|
+
console.error("[BillingDashboard] Failed to load payment methods:", error);
|
|
13446
|
+
setErrors((prev) => ({ ...prev, paymentMethods: "Failed to load payment methods" }));
|
|
13447
|
+
} finally {
|
|
13448
|
+
setLoading((prev) => ({ ...prev, paymentMethods: false }));
|
|
13449
|
+
}
|
|
13450
|
+
}, "fetchPaymentMethods");
|
|
13451
|
+
const fetchInvoices = /* @__PURE__ */ __name(async () => {
|
|
13452
|
+
try {
|
|
13453
|
+
const invoices = await StripeInvoiceService.listInvoices();
|
|
13454
|
+
setData((prev) => ({ ...prev, invoices }));
|
|
13455
|
+
setErrors((prev) => ({ ...prev, invoices: null }));
|
|
13456
|
+
} catch (error) {
|
|
13457
|
+
console.error("[BillingDashboard] Failed to load invoices:", error);
|
|
13458
|
+
setErrors((prev) => ({ ...prev, invoices: "Failed to load invoices" }));
|
|
13459
|
+
} finally {
|
|
13460
|
+
setLoading((prev) => ({ ...prev, invoices: false }));
|
|
13461
|
+
}
|
|
13462
|
+
}, "fetchInvoices");
|
|
13463
|
+
const [subscriptions] = await Promise.all([fetchSubscriptions(), fetchPaymentMethods(), fetchInvoices()]);
|
|
13464
|
+
const hasMetered = subscriptions.some(
|
|
13465
|
+
(sub) => sub.price?.recurring?.usageType === "metered"
|
|
13466
|
+
);
|
|
13467
|
+
if (hasMetered) {
|
|
13468
|
+
await fetchUsageData();
|
|
13469
|
+
} else {
|
|
13470
|
+
setLoading((prev) => ({ ...prev, usage: false }));
|
|
13471
|
+
}
|
|
13472
|
+
}
|
|
13473
|
+
}, []);
|
|
13474
|
+
const handleCreateCustomer = /* @__PURE__ */ __name(async () => {
|
|
13475
|
+
setCreatingCustomer(true);
|
|
13476
|
+
try {
|
|
13477
|
+
await StripeCustomerService.createCustomer();
|
|
13478
|
+
setNoCustomerExists(false);
|
|
13479
|
+
await fetchAllData();
|
|
13480
|
+
} catch (error) {
|
|
13481
|
+
console.error("[BillingDashboard] Failed to create customer:", error);
|
|
13482
|
+
setErrors((prev) => ({ ...prev, customer: "Failed to set up billing" }));
|
|
13483
|
+
} finally {
|
|
13484
|
+
setCreatingCustomer(false);
|
|
13485
|
+
}
|
|
13486
|
+
}, "handleCreateCustomer");
|
|
13487
|
+
const fetchUsageData = /* @__PURE__ */ __name(async () => {
|
|
13488
|
+
try {
|
|
13489
|
+
const meters = await StripeUsageService.listMeters();
|
|
13490
|
+
setData((prev) => ({ ...prev, meters }));
|
|
13491
|
+
const summariesMap = {};
|
|
13492
|
+
const now = /* @__PURE__ */ new Date();
|
|
13493
|
+
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
13494
|
+
const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);
|
|
13495
|
+
for (const meter of meters) {
|
|
13496
|
+
try {
|
|
13497
|
+
const meterSummaries = await StripeUsageService.getMeterSummaries({
|
|
13498
|
+
meterId: meter.id,
|
|
13499
|
+
startTime: startOfMonth,
|
|
13500
|
+
endTime: endOfMonth
|
|
13501
|
+
});
|
|
13502
|
+
summariesMap[meter.id] = meterSummaries.length > 0 ? meterSummaries[0] : null;
|
|
13503
|
+
} catch (error) {
|
|
13504
|
+
console.error(`[BillingDashboard] Failed to load summaries for meter ${meter.id}:`, error);
|
|
13505
|
+
summariesMap[meter.id] = null;
|
|
13506
|
+
}
|
|
13507
|
+
}
|
|
13508
|
+
setData((prev) => ({ ...prev, meterSummaries: summariesMap }));
|
|
13509
|
+
setErrors((prev) => ({ ...prev, usage: null }));
|
|
13510
|
+
} catch (error) {
|
|
13511
|
+
console.error("[BillingDashboard] Failed to load usage data:", error);
|
|
13512
|
+
setErrors((prev) => ({ ...prev, usage: "Failed to load usage data" }));
|
|
13513
|
+
} finally {
|
|
13514
|
+
setLoading((prev) => ({ ...prev, usage: false }));
|
|
13515
|
+
}
|
|
13516
|
+
}, "fetchUsageData");
|
|
13517
|
+
const refreshData = useCallback18(async () => {
|
|
13518
|
+
setLoading({
|
|
13519
|
+
customer: true,
|
|
13520
|
+
subscriptions: true,
|
|
13521
|
+
paymentMethods: true,
|
|
13522
|
+
invoices: true,
|
|
13523
|
+
usage: true
|
|
13524
|
+
});
|
|
13525
|
+
await fetchAllData();
|
|
13526
|
+
}, [fetchAllData]);
|
|
13527
|
+
useEffect47(() => {
|
|
13528
|
+
fetchAllData();
|
|
13529
|
+
}, [fetchAllData]);
|
|
13530
|
+
const criticalSubscriptions = data.subscriptions.filter(
|
|
13531
|
+
(sub) => sub.status === "past_due" /* PAST_DUE */ || sub.status === "trialing" /* TRIALING */ && sub.trialEnd && new Date(sub.trialEnd).getTime() - (/* @__PURE__ */ new Date()).getTime() <= 7 * 24 * 60 * 60 * 1e3
|
|
13532
|
+
);
|
|
13533
|
+
const handleModalClose = /* @__PURE__ */ __name((open) => {
|
|
13534
|
+
if (!open) {
|
|
13535
|
+
setActiveModal(null);
|
|
13536
|
+
refreshData();
|
|
13537
|
+
}
|
|
13538
|
+
}, "handleModalClose");
|
|
13539
|
+
const getModalTitle = /* @__PURE__ */ __name((type) => {
|
|
13540
|
+
switch (type) {
|
|
13541
|
+
case "subscriptions":
|
|
13542
|
+
return "Manage Subscriptions";
|
|
13543
|
+
case "payment-methods":
|
|
13544
|
+
return "Payment Methods";
|
|
13545
|
+
case "invoices":
|
|
13546
|
+
return "Invoice History";
|
|
13547
|
+
case "usage":
|
|
13548
|
+
return "Usage Tracking";
|
|
13549
|
+
default:
|
|
13550
|
+
return "";
|
|
13551
|
+
}
|
|
13552
|
+
}, "getModalTitle");
|
|
13553
|
+
const isInitialLoading = loading.customer && !noCustomerExists && !data.customer;
|
|
13554
|
+
return /* @__PURE__ */ jsxs110("div", { className: "flex w-full flex-col gap-y-6", children: [
|
|
13555
|
+
/* @__PURE__ */ jsxs110("div", { className: "flex items-center gap-x-3", children: [
|
|
13556
|
+
/* @__PURE__ */ jsx172(Wallet2, { className: "h-8 w-8" }),
|
|
13557
|
+
/* @__PURE__ */ jsx172("h1", { className: "text-3xl font-bold", children: "Billing" })
|
|
13558
|
+
] }),
|
|
13559
|
+
isInitialLoading && /* @__PURE__ */ jsx172(Card, { children: /* @__PURE__ */ jsx172(CardContent, { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx172(Loader24, { className: "h-8 w-8 animate-spin text-muted-foreground" }) }) }),
|
|
13560
|
+
noCustomerExists && !isInitialLoading && /* @__PURE__ */ jsxs110(Card, { children: [
|
|
13561
|
+
/* @__PURE__ */ jsxs110(CardHeader, { className: "text-center", children: [
|
|
13562
|
+
/* @__PURE__ */ jsx172("div", { className: "mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-primary/10", children: /* @__PURE__ */ jsx172(CreditCard4, { className: "h-8 w-8 text-primary" }) }),
|
|
13563
|
+
/* @__PURE__ */ jsx172(CardTitle, { children: "Set Up Billing" }),
|
|
13564
|
+
/* @__PURE__ */ jsx172(CardDescription, { children: "Your company doesn't have a billing account yet. Set one up to manage subscriptions, payment methods, and view invoices." })
|
|
13565
|
+
] }),
|
|
13566
|
+
/* @__PURE__ */ jsx172(CardContent, { className: "flex justify-center pb-8", children: /* @__PURE__ */ jsx172(Button, { onClick: handleCreateCustomer, disabled: creatingCustomer, size: "lg", children: creatingCustomer ? /* @__PURE__ */ jsxs110(Fragment33, { children: [
|
|
13567
|
+
/* @__PURE__ */ jsx172(Loader24, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
13568
|
+
"Setting up..."
|
|
13569
|
+
] }) : "Set Up Billing Account" }) }),
|
|
13570
|
+
errors.customer && /* @__PURE__ */ jsx172(CardContent, { className: "pt-0", children: /* @__PURE__ */ jsx172("p", { className: "text-center text-sm text-destructive", children: errors.customer }) })
|
|
13571
|
+
] }),
|
|
13572
|
+
!noCustomerExists && !isInitialLoading && /* @__PURE__ */ jsxs110(Fragment33, { children: [
|
|
13573
|
+
criticalSubscriptions.map((subscription) => /* @__PURE__ */ jsx172(
|
|
13574
|
+
BillingAlertBanner,
|
|
13575
|
+
{
|
|
13576
|
+
subscription,
|
|
13577
|
+
onUpdatePayment: () => setActiveModal("payment-methods"),
|
|
13578
|
+
onAddPayment: () => setActiveModal("payment-methods")
|
|
13579
|
+
},
|
|
13580
|
+
subscription.id
|
|
13581
|
+
)),
|
|
13582
|
+
/* @__PURE__ */ jsxs110("div", { className: "grid gap-4 md:grid-cols-2", children: [
|
|
13583
|
+
/* @__PURE__ */ jsx172(
|
|
13584
|
+
SubscriptionSummaryCard,
|
|
13585
|
+
{
|
|
13586
|
+
subscriptions: data.subscriptions,
|
|
13587
|
+
loading: loading.subscriptions,
|
|
13588
|
+
error: errors.subscriptions || void 0,
|
|
13589
|
+
onManageClick: () => setActiveModal("subscriptions")
|
|
13590
|
+
}
|
|
13591
|
+
),
|
|
13592
|
+
/* @__PURE__ */ jsx172(
|
|
13593
|
+
PaymentMethodSummaryCard,
|
|
13594
|
+
{
|
|
13595
|
+
paymentMethods: data.paymentMethods,
|
|
13596
|
+
defaultPaymentMethodId: data.customer?.defaultPaymentMethodId,
|
|
13597
|
+
loading: loading.paymentMethods,
|
|
13598
|
+
error: errors.paymentMethods || void 0,
|
|
13599
|
+
onManageClick: () => setActiveModal("payment-methods")
|
|
13600
|
+
}
|
|
13601
|
+
),
|
|
13602
|
+
/* @__PURE__ */ jsx172(
|
|
13603
|
+
CustomerInfoCard,
|
|
13604
|
+
{
|
|
13605
|
+
customer: data.customer,
|
|
13606
|
+
loading: loading.customer,
|
|
13607
|
+
error: errors.customer || void 0
|
|
13608
|
+
}
|
|
13609
|
+
),
|
|
13610
|
+
/* @__PURE__ */ jsx172(
|
|
13611
|
+
InvoicesSummaryCard,
|
|
13612
|
+
{
|
|
13613
|
+
invoices: data.invoices,
|
|
13614
|
+
loading: loading.invoices,
|
|
13615
|
+
error: errors.invoices || void 0,
|
|
13616
|
+
onViewAllClick: () => setActiveModal("invoices")
|
|
13617
|
+
}
|
|
13618
|
+
),
|
|
13619
|
+
hasMeteredSubscriptions() && /* @__PURE__ */ jsx172(
|
|
13620
|
+
BillingUsageSummaryCard,
|
|
13621
|
+
{
|
|
13622
|
+
meters: data.meters,
|
|
13623
|
+
summaries: data.meterSummaries,
|
|
13624
|
+
loading: loading.usage,
|
|
13625
|
+
error: errors.usage || void 0,
|
|
13626
|
+
onViewDetailsClick: () => setActiveModal("usage")
|
|
13627
|
+
}
|
|
13628
|
+
)
|
|
13629
|
+
] }),
|
|
13630
|
+
/* @__PURE__ */ jsx172(
|
|
13631
|
+
BillingDetailModal,
|
|
13632
|
+
{
|
|
13633
|
+
open: activeModal === "subscriptions",
|
|
13634
|
+
onOpenChange: handleModalClose,
|
|
13635
|
+
title: getModalTitle("subscriptions"),
|
|
13636
|
+
children: /* @__PURE__ */ jsx172(SubscriptionsContainer, {})
|
|
13637
|
+
}
|
|
13638
|
+
),
|
|
13639
|
+
/* @__PURE__ */ jsx172(
|
|
13640
|
+
BillingDetailModal,
|
|
13641
|
+
{
|
|
13642
|
+
open: activeModal === "payment-methods",
|
|
13643
|
+
onOpenChange: handleModalClose,
|
|
13644
|
+
title: getModalTitle("payment-methods"),
|
|
13645
|
+
children: /* @__PURE__ */ jsx172(PaymentMethodsContainer, {})
|
|
13646
|
+
}
|
|
13647
|
+
),
|
|
13648
|
+
/* @__PURE__ */ jsx172(
|
|
13649
|
+
BillingDetailModal,
|
|
13650
|
+
{
|
|
13651
|
+
open: activeModal === "invoices",
|
|
13652
|
+
onOpenChange: handleModalClose,
|
|
13653
|
+
title: getModalTitle("invoices"),
|
|
13654
|
+
children: /* @__PURE__ */ jsx172(InvoicesContainer, {})
|
|
13655
|
+
}
|
|
13656
|
+
),
|
|
13657
|
+
/* @__PURE__ */ jsx172(
|
|
13658
|
+
BillingDetailModal,
|
|
13659
|
+
{
|
|
13660
|
+
open: activeModal === "usage",
|
|
13661
|
+
onOpenChange: handleModalClose,
|
|
13662
|
+
title: getModalTitle("usage"),
|
|
13663
|
+
children: /* @__PURE__ */ jsx172(UsageContainer, {})
|
|
13664
|
+
}
|
|
13665
|
+
)
|
|
13666
|
+
] })
|
|
13667
|
+
] });
|
|
13668
|
+
}
|
|
13669
|
+
__name(BillingDashboardContainer, "BillingDashboardContainer");
|
|
13670
|
+
|
|
13671
|
+
// src/features/billing/components/providers/StripeProvider.tsx
|
|
13672
|
+
import { Elements } from "@stripe/react-stripe-js";
|
|
13673
|
+
import { loadStripe } from "@stripe/stripe-js";
|
|
13674
|
+
import { useMemo as useMemo18 } from "react";
|
|
13675
|
+
import { Fragment as Fragment34, jsx as jsx173 } from "react/jsx-runtime";
|
|
13676
|
+
var stripePromiseCache = null;
|
|
13677
|
+
function getStripePromise(publishableKey) {
|
|
13678
|
+
if (!publishableKey) {
|
|
13679
|
+
return Promise.resolve(null);
|
|
13680
|
+
}
|
|
13681
|
+
if (stripePromiseCache?.key === publishableKey) {
|
|
13682
|
+
return stripePromiseCache.promise;
|
|
13683
|
+
}
|
|
13684
|
+
const promise = loadStripe(publishableKey);
|
|
13685
|
+
stripePromiseCache = { key: publishableKey, promise };
|
|
13686
|
+
return promise;
|
|
13687
|
+
}
|
|
13688
|
+
__name(getStripePromise, "getStripePromise");
|
|
13689
|
+
function StripeProvider({ children }) {
|
|
13690
|
+
const publishableKey = getStripePublishableKey();
|
|
13691
|
+
const stripePromise = useMemo18(() => getStripePromise(publishableKey), [publishableKey]);
|
|
13692
|
+
const options = useMemo18(() => ({}), []);
|
|
13693
|
+
if (!publishableKey) {
|
|
13694
|
+
return /* @__PURE__ */ jsx173(Fragment34, { children });
|
|
13695
|
+
}
|
|
13696
|
+
return /* @__PURE__ */ jsx173(Elements, { stripe: stripePromise, options, children });
|
|
13697
|
+
}
|
|
13698
|
+
__name(StripeProvider, "StripeProvider");
|
|
13699
|
+
function isStripeConfigured() {
|
|
13700
|
+
return !!getStripePublishableKey();
|
|
13701
|
+
}
|
|
13702
|
+
__name(isStripeConfigured, "isStripeConfigured");
|
|
13703
|
+
|
|
13704
|
+
// src/features/billing/stripe-price/components/forms/PriceEditor.tsx
|
|
13705
|
+
import { zodResolver as zodResolver11 } from "@hookform/resolvers/zod";
|
|
13706
|
+
import { AlertCircle as AlertCircle2, PlusIcon, XIcon as XIcon5 } from "lucide-react";
|
|
13707
|
+
import { useState as useState63 } from "react";
|
|
13708
|
+
import { useForm as useForm11 } from "react-hook-form";
|
|
13709
|
+
import { v4 as v47 } from "uuid";
|
|
13710
|
+
import { z as z11 } from "zod";
|
|
13711
|
+
import { jsx as jsx174, jsxs as jsxs111 } from "react/jsx-runtime";
|
|
13712
|
+
function PriceEditor({ productId, price, open, onOpenChange, onSuccess }) {
|
|
13713
|
+
const [isSubmitting, setIsSubmitting] = useState63(false);
|
|
13714
|
+
const formSchema2 = z11.object({
|
|
13715
|
+
unitAmount: z11.preprocess(
|
|
13716
|
+
(val) => typeof val === "string" ? parseFloat(val) : val,
|
|
13717
|
+
z11.number().min(0, { message: "Amount must be 0 or greater" })
|
|
13718
|
+
),
|
|
13719
|
+
currency: z11.string().min(1, { message: "Currency is required" }),
|
|
13720
|
+
interval: z11.enum(["one_time", "day", "week", "month", "year"]),
|
|
13721
|
+
intervalCount: z11.preprocess(
|
|
13722
|
+
(val) => val === "" || val === void 0 ? void 0 : typeof val === "string" ? parseInt(val, 10) : val,
|
|
13723
|
+
z11.number().min(1).optional()
|
|
13724
|
+
),
|
|
13725
|
+
usageType: z11.enum(["licensed", "metered"]).optional(),
|
|
13726
|
+
nickname: z11.string().optional(),
|
|
13727
|
+
active: z11.boolean(),
|
|
13728
|
+
description: z11.string().optional(),
|
|
13729
|
+
features: z11.array(z11.string())
|
|
13730
|
+
});
|
|
13731
|
+
const isEditMode = !!price;
|
|
13732
|
+
const defaultUnitAmount = price?.unitAmount ? price.unitAmount / 100 : 0;
|
|
13733
|
+
const form = useForm11({
|
|
13734
|
+
resolver: zodResolver11(formSchema2),
|
|
13735
|
+
defaultValues: {
|
|
13736
|
+
unitAmount: defaultUnitAmount,
|
|
13737
|
+
currency: price?.currency || "usd",
|
|
13738
|
+
interval: price?.priceType === "one_time" ? "one_time" : price?.recurring?.interval || "month",
|
|
13739
|
+
intervalCount: price?.recurring?.intervalCount || 1,
|
|
13740
|
+
usageType: price?.recurring?.usageType || "licensed",
|
|
13741
|
+
nickname: price?.nickname || "",
|
|
13742
|
+
active: price?.active ?? true,
|
|
13743
|
+
description: price?.description || "",
|
|
13744
|
+
features: price?.features || []
|
|
13745
|
+
}
|
|
13746
|
+
});
|
|
13747
|
+
const watchInterval = form.watch("interval");
|
|
13748
|
+
const isRecurring = watchInterval !== "one_time";
|
|
13749
|
+
const onSubmit = /* @__PURE__ */ __name(async (values) => {
|
|
13750
|
+
setIsSubmitting(true);
|
|
13751
|
+
try {
|
|
13752
|
+
const unitAmountInCents = Math.round(values.unitAmount * 100);
|
|
13753
|
+
if (isEditMode) {
|
|
13754
|
+
await StripePriceService.updatePrice({
|
|
13755
|
+
id: price.id,
|
|
13756
|
+
nickname: values.nickname || void 0,
|
|
13757
|
+
description: values.description || void 0,
|
|
13758
|
+
features: values.features.filter((f) => f.trim()) || void 0
|
|
13759
|
+
});
|
|
13760
|
+
} else {
|
|
13761
|
+
const createInput = {
|
|
13762
|
+
id: v47(),
|
|
13763
|
+
productId,
|
|
13764
|
+
currency: values.currency,
|
|
13765
|
+
unitAmount: unitAmountInCents
|
|
13766
|
+
};
|
|
13767
|
+
if (isRecurring) {
|
|
13768
|
+
createInput.recurring = {
|
|
13769
|
+
interval: values.interval,
|
|
13770
|
+
intervalCount: values.intervalCount || 1,
|
|
13771
|
+
usageType: values.usageType || "licensed"
|
|
13772
|
+
};
|
|
13773
|
+
}
|
|
13774
|
+
if (values.nickname) {
|
|
13775
|
+
createInput.nickname = values.nickname;
|
|
13776
|
+
}
|
|
13777
|
+
if (values.description) {
|
|
13778
|
+
createInput.description = values.description;
|
|
13779
|
+
}
|
|
13780
|
+
const filteredFeatures = values.features.filter((f) => f.trim());
|
|
13781
|
+
if (filteredFeatures.length > 0) {
|
|
13782
|
+
createInput.features = filteredFeatures;
|
|
13783
|
+
}
|
|
13784
|
+
await StripePriceService.createPrice(createInput);
|
|
13785
|
+
}
|
|
13786
|
+
onSuccess();
|
|
13787
|
+
onOpenChange(false);
|
|
13788
|
+
} catch (error) {
|
|
13789
|
+
console.error("[PriceEditor] Failed to save price:", error);
|
|
13790
|
+
} finally {
|
|
13791
|
+
setIsSubmitting(false);
|
|
13792
|
+
}
|
|
13793
|
+
}, "onSubmit");
|
|
13794
|
+
const currencyOptions = [
|
|
13795
|
+
{ id: "usd", text: "USD ($)" },
|
|
13796
|
+
{ id: "eur", text: "EUR (\u20AC)" },
|
|
13797
|
+
{ id: "gbp", text: "GBP (\xA3)" }
|
|
13798
|
+
];
|
|
13799
|
+
const intervalOptions = [
|
|
13800
|
+
{ id: "one_time", text: "One-time" },
|
|
13801
|
+
{ id: "day", text: "Daily" },
|
|
13802
|
+
{ id: "week", text: "Weekly" },
|
|
13803
|
+
{ id: "month", text: "Monthly" },
|
|
13804
|
+
{ id: "year", text: "Yearly" }
|
|
13805
|
+
];
|
|
13806
|
+
const usageTypeOptions = [
|
|
13807
|
+
{ id: "licensed", text: "Licensed (per unit)" },
|
|
13808
|
+
{ id: "metered", text: "Metered (usage-based)" }
|
|
13809
|
+
];
|
|
13810
|
+
return /* @__PURE__ */ jsx174(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs111(DialogContent, { className: "max-w-2xl", children: [
|
|
13811
|
+
/* @__PURE__ */ jsxs111(DialogHeader, { children: [
|
|
13812
|
+
/* @__PURE__ */ jsx174(DialogTitle, { children: isEditMode ? "Edit Price" : "Create Price" }),
|
|
13813
|
+
/* @__PURE__ */ jsx174(DialogDescription, { children: isEditMode ? "Update the price details. Note: Only nickname and active status can be changed." : "Create a new price for this product" })
|
|
13814
|
+
] }),
|
|
13815
|
+
isEditMode && /* @__PURE__ */ jsxs111("div", { className: "bg-blue-50 border border-blue-200 rounded-lg p-4 flex gap-x-3", children: [
|
|
13816
|
+
/* @__PURE__ */ jsx174(AlertCircle2, { className: "h-5 w-5 text-blue-600 flex-shrink-0 mt-0.5" }),
|
|
13817
|
+
/* @__PURE__ */ jsxs111("div", { className: "text-sm text-blue-800", children: [
|
|
13818
|
+
/* @__PURE__ */ jsx174("p", { className: "font-semibold mb-1", children: "Stripe Price Immutability" }),
|
|
13819
|
+
/* @__PURE__ */ jsx174("p", { children: "Due to Stripe's architecture, only the nickname and active status can be modified after creation. To change amount, currency, or billing interval, create a new price." })
|
|
13820
|
+
] })
|
|
13821
|
+
] }),
|
|
13822
|
+
/* @__PURE__ */ jsx174(Form, { ...form, children: /* @__PURE__ */ jsxs111("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-col gap-y-4", children: [
|
|
13823
|
+
/* @__PURE__ */ jsxs111("div", { className: "grid grid-cols-2 gap-x-4", children: [
|
|
13824
|
+
/* @__PURE__ */ jsx174(
|
|
13825
|
+
FormInput,
|
|
13826
|
+
{
|
|
13827
|
+
form,
|
|
13828
|
+
id: "unitAmount",
|
|
13829
|
+
name: "Amount (in dollars)",
|
|
13830
|
+
placeholder: "9.99",
|
|
13831
|
+
disabled: isEditMode,
|
|
13832
|
+
isRequired: true
|
|
13833
|
+
}
|
|
13834
|
+
),
|
|
13835
|
+
/* @__PURE__ */ jsx174(FormSelect, { form, id: "currency", name: "Currency", values: currencyOptions, disabled: isEditMode })
|
|
13836
|
+
] }),
|
|
13837
|
+
/* @__PURE__ */ jsx174(
|
|
13838
|
+
FormSelect,
|
|
13839
|
+
{
|
|
13840
|
+
form,
|
|
13841
|
+
id: "interval",
|
|
13842
|
+
name: "Billing Interval",
|
|
13843
|
+
values: intervalOptions,
|
|
13844
|
+
disabled: isEditMode
|
|
13845
|
+
}
|
|
13846
|
+
),
|
|
13847
|
+
isRecurring && /* @__PURE__ */ jsxs111("div", { className: "grid grid-cols-2 gap-x-4", children: [
|
|
13848
|
+
/* @__PURE__ */ jsx174(
|
|
13849
|
+
FormInput,
|
|
13850
|
+
{
|
|
13851
|
+
form,
|
|
13852
|
+
id: "intervalCount",
|
|
13853
|
+
name: "Interval Count",
|
|
13854
|
+
placeholder: "1",
|
|
13855
|
+
type: "number",
|
|
13856
|
+
disabled: isEditMode
|
|
13857
|
+
}
|
|
13858
|
+
),
|
|
13859
|
+
/* @__PURE__ */ jsx174(
|
|
13860
|
+
FormSelect,
|
|
13861
|
+
{
|
|
13862
|
+
form,
|
|
13863
|
+
id: "usageType",
|
|
13864
|
+
name: "Usage Type",
|
|
13865
|
+
values: usageTypeOptions,
|
|
13866
|
+
disabled: isEditMode
|
|
13867
|
+
}
|
|
13868
|
+
)
|
|
13869
|
+
] }),
|
|
13870
|
+
/* @__PURE__ */ jsx174(
|
|
13871
|
+
FormInput,
|
|
13872
|
+
{
|
|
13873
|
+
form,
|
|
13874
|
+
id: "nickname",
|
|
13875
|
+
name: "Nickname (optional)",
|
|
13876
|
+
placeholder: "e.g., Standard Plan, Pro Tier"
|
|
13877
|
+
}
|
|
13878
|
+
),
|
|
13879
|
+
/* @__PURE__ */ jsx174(
|
|
13880
|
+
FormTextarea,
|
|
13881
|
+
{
|
|
13882
|
+
form,
|
|
13883
|
+
id: "description",
|
|
13884
|
+
name: "Description (optional)",
|
|
13885
|
+
placeholder: "Describe what this price tier includes...",
|
|
13886
|
+
className: "min-h-24"
|
|
13887
|
+
}
|
|
13888
|
+
),
|
|
13889
|
+
/* @__PURE__ */ jsxs111(FormItem, { children: [
|
|
13890
|
+
/* @__PURE__ */ jsx174(FormLabel, { children: "Features (optional)" }),
|
|
13891
|
+
/* @__PURE__ */ jsxs111("div", { className: "space-y-2", children: [
|
|
13892
|
+
form.watch("features").map((_, index) => /* @__PURE__ */ jsxs111("div", { className: "flex gap-2", children: [
|
|
13893
|
+
/* @__PURE__ */ jsx174(FormControl, { children: /* @__PURE__ */ jsx174(
|
|
13894
|
+
Input,
|
|
13895
|
+
{
|
|
13896
|
+
...form.register(`features.${index}`),
|
|
13897
|
+
placeholder: `Feature ${index + 1}`,
|
|
13898
|
+
className: "flex-1"
|
|
13899
|
+
}
|
|
13900
|
+
) }),
|
|
13901
|
+
/* @__PURE__ */ jsx174(
|
|
13902
|
+
Button,
|
|
13903
|
+
{
|
|
13904
|
+
type: "button",
|
|
13905
|
+
variant: "outline",
|
|
13906
|
+
size: "icon",
|
|
13907
|
+
onClick: () => {
|
|
13908
|
+
const currentFeatures = form.getValues("features");
|
|
13909
|
+
form.setValue(
|
|
13910
|
+
"features",
|
|
13911
|
+
currentFeatures.filter((_2, i) => i !== index)
|
|
13912
|
+
);
|
|
13913
|
+
},
|
|
13914
|
+
children: /* @__PURE__ */ jsx174(XIcon5, { className: "h-4 w-4" })
|
|
13915
|
+
}
|
|
13916
|
+
)
|
|
13917
|
+
] }, index)),
|
|
13918
|
+
/* @__PURE__ */ jsxs111(
|
|
13919
|
+
Button,
|
|
13920
|
+
{
|
|
13921
|
+
type: "button",
|
|
13922
|
+
variant: "outline",
|
|
13923
|
+
size: "sm",
|
|
13924
|
+
onClick: () => {
|
|
13925
|
+
const currentFeatures = form.getValues("features");
|
|
13926
|
+
form.setValue("features", [...currentFeatures, ""]);
|
|
13927
|
+
},
|
|
13928
|
+
className: "mt-2",
|
|
13929
|
+
children: [
|
|
13930
|
+
/* @__PURE__ */ jsx174(PlusIcon, { className: "h-4 w-4 mr-2" }),
|
|
13931
|
+
"Add Feature"
|
|
13932
|
+
]
|
|
13933
|
+
}
|
|
13934
|
+
)
|
|
13935
|
+
] })
|
|
13936
|
+
] }),
|
|
13937
|
+
/* @__PURE__ */ jsx174(FormCheckbox, { form, id: "active", name: "Active" }),
|
|
13938
|
+
/* @__PURE__ */ jsx174(CommonEditorButtons, { isEdit: isEditMode, form, disabled: isSubmitting, setOpen: onOpenChange })
|
|
13939
|
+
] }) })
|
|
13940
|
+
] }) });
|
|
13941
|
+
}
|
|
13942
|
+
__name(PriceEditor, "PriceEditor");
|
|
13943
|
+
|
|
13944
|
+
// src/features/billing/stripe-price/components/lists/PricesList.tsx
|
|
13945
|
+
import { Archive, DollarSign, Edit, RotateCcw } from "lucide-react";
|
|
13946
|
+
import { useEffect as useEffect48, useState as useState64 } from "react";
|
|
13947
|
+
import { jsx as jsx175, jsxs as jsxs112 } from "react/jsx-runtime";
|
|
13948
|
+
function PricesList({ productId, onPricesChange }) {
|
|
13949
|
+
const [prices, setPrices] = useState64([]);
|
|
13950
|
+
const [loading, setLoading] = useState64(true);
|
|
13951
|
+
const [showCreatePrice, setShowCreatePrice] = useState64(false);
|
|
13952
|
+
const [editingPrice, setEditingPrice] = useState64(null);
|
|
13953
|
+
const [priceToArchive, setPriceToArchive] = useState64(null);
|
|
13954
|
+
const [priceToReactivate, setPriceToReactivate] = useState64(null);
|
|
13955
|
+
const [archivingPriceId, setArchivingPriceId] = useState64(null);
|
|
13956
|
+
const [reactivatingPriceId, setReactivatingPriceId] = useState64(null);
|
|
13957
|
+
const loadPrices = /* @__PURE__ */ __name(async () => {
|
|
13958
|
+
setLoading(true);
|
|
13959
|
+
try {
|
|
13960
|
+
const fetchedPrices = await StripePriceService.listPrices({ productId });
|
|
13961
|
+
setPrices(fetchedPrices);
|
|
13962
|
+
} catch (error) {
|
|
13963
|
+
console.error("[PricesList] Failed to load prices:", error);
|
|
13964
|
+
} finally {
|
|
13965
|
+
setLoading(false);
|
|
13966
|
+
}
|
|
13967
|
+
}, "loadPrices");
|
|
13968
|
+
useEffect48(() => {
|
|
13969
|
+
loadPrices();
|
|
13970
|
+
}, [productId]);
|
|
13971
|
+
const handleArchive = /* @__PURE__ */ __name(async () => {
|
|
13972
|
+
if (!priceToArchive) {
|
|
13973
|
+
return;
|
|
13974
|
+
}
|
|
13975
|
+
setArchivingPriceId(priceToArchive.id);
|
|
13976
|
+
try {
|
|
13977
|
+
await StripePriceService.archivePrice({ id: priceToArchive.id });
|
|
13978
|
+
setPriceToArchive(null);
|
|
13979
|
+
await loadPrices();
|
|
13980
|
+
onPricesChange();
|
|
13981
|
+
} catch (error) {
|
|
13982
|
+
console.error("[PricesList] Failed to archive price:", error);
|
|
13983
|
+
} finally {
|
|
13984
|
+
setArchivingPriceId(null);
|
|
13985
|
+
}
|
|
13986
|
+
}, "handleArchive");
|
|
13987
|
+
const handleReactivate = /* @__PURE__ */ __name(async () => {
|
|
13988
|
+
if (!priceToReactivate) {
|
|
13989
|
+
return;
|
|
13990
|
+
}
|
|
13991
|
+
setReactivatingPriceId(priceToReactivate.id);
|
|
13992
|
+
try {
|
|
13993
|
+
await StripePriceService.reactivatePrice({ id: priceToReactivate.id });
|
|
13994
|
+
setPriceToReactivate(null);
|
|
13995
|
+
await loadPrices();
|
|
13996
|
+
onPricesChange();
|
|
13997
|
+
} catch (error) {
|
|
13998
|
+
console.error("[PricesList] Failed to reactivate price:", error);
|
|
13999
|
+
} finally {
|
|
14000
|
+
setReactivatingPriceId(null);
|
|
14001
|
+
}
|
|
14002
|
+
}, "handleReactivate");
|
|
14003
|
+
const formatInterval2 = /* @__PURE__ */ __name((price) => {
|
|
14004
|
+
if (price.priceType === "one_time") {
|
|
14005
|
+
return "one-time";
|
|
14006
|
+
}
|
|
14007
|
+
if (price.recurring) {
|
|
14008
|
+
const count = price.recurring.intervalCount;
|
|
14009
|
+
const interval = price.recurring.interval;
|
|
14010
|
+
if (count === 1) {
|
|
14011
|
+
return `/ ${interval}`;
|
|
14012
|
+
} else {
|
|
14013
|
+
return `/ ${count} ${interval}s`;
|
|
14014
|
+
}
|
|
14015
|
+
}
|
|
14016
|
+
return "";
|
|
14017
|
+
}, "formatInterval");
|
|
14018
|
+
if (loading) {
|
|
14019
|
+
return /* @__PURE__ */ jsx175("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx175("p", { className: "text-muted-foreground", children: "Loading prices..." }) });
|
|
14020
|
+
}
|
|
14021
|
+
return /* @__PURE__ */ jsxs112("div", { className: "flex flex-col gap-y-4", children: [
|
|
14022
|
+
/* @__PURE__ */ jsxs112("div", { className: "flex items-center justify-between mb-4", children: [
|
|
14023
|
+
/* @__PURE__ */ jsx175("h4", { className: "text-lg font-semibold", children: "Prices" }),
|
|
14024
|
+
/* @__PURE__ */ jsx175(Button, { size: "sm", onClick: () => setShowCreatePrice(true), children: "Add Price" })
|
|
14025
|
+
] }),
|
|
14026
|
+
prices.length === 0 && /* @__PURE__ */ jsxs112("div", { className: "bg-background flex flex-col items-center justify-center gap-y-3 rounded-lg border border-dashed p-8", children: [
|
|
14027
|
+
/* @__PURE__ */ jsx175(DollarSign, { className: "text-muted-foreground h-12 w-12" }),
|
|
14028
|
+
/* @__PURE__ */ jsx175("p", { className: "text-muted-foreground text-sm", children: "No prices yet. Add a price to enable subscriptions." }),
|
|
14029
|
+
/* @__PURE__ */ jsx175(Button, { size: "sm", onClick: () => setShowCreatePrice(true), children: "Add Price" })
|
|
14030
|
+
] }),
|
|
14031
|
+
prices.length > 0 && /* @__PURE__ */ jsx175("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: prices.map((price) => {
|
|
14032
|
+
const isArchiving = archivingPriceId === price.id;
|
|
14033
|
+
const isReactivating = reactivatingPriceId === price.id;
|
|
14034
|
+
return /* @__PURE__ */ jsxs112("div", { className: "border rounded-lg bg-white p-4 hover:shadow-sm transition-shadow", children: [
|
|
14035
|
+
/* @__PURE__ */ jsxs112("div", { className: "flex items-start justify-between mb-3", children: [
|
|
14036
|
+
/* @__PURE__ */ jsx175(DollarSign, { className: "h-5 w-5 text-primary" }),
|
|
14037
|
+
/* @__PURE__ */ jsxs112("div", { className: "flex gap-1", children: [
|
|
14038
|
+
/* @__PURE__ */ jsx175(Button, { variant: "ghost", size: "sm", onClick: () => setEditingPrice(price), className: "h-8 w-8 p-0", children: /* @__PURE__ */ jsx175(Edit, { className: "h-4 w-4" }) }),
|
|
14039
|
+
price.active ? /* @__PURE__ */ jsx175(
|
|
14040
|
+
Button,
|
|
14041
|
+
{
|
|
14042
|
+
variant: "ghost",
|
|
14043
|
+
size: "sm",
|
|
14044
|
+
onClick: () => setPriceToArchive(price),
|
|
14045
|
+
className: "h-8 w-8 p-0",
|
|
14046
|
+
disabled: isArchiving,
|
|
14047
|
+
children: /* @__PURE__ */ jsx175(Archive, { className: "h-4 w-4" })
|
|
14048
|
+
}
|
|
14049
|
+
) : /* @__PURE__ */ jsx175(
|
|
14050
|
+
Button,
|
|
14051
|
+
{
|
|
14052
|
+
variant: "ghost",
|
|
14053
|
+
size: "sm",
|
|
14054
|
+
onClick: () => setPriceToReactivate(price),
|
|
14055
|
+
className: "h-8 w-8 p-0",
|
|
14056
|
+
disabled: isReactivating,
|
|
14057
|
+
children: /* @__PURE__ */ jsx175(RotateCcw, { className: "h-4 w-4" })
|
|
14058
|
+
}
|
|
14059
|
+
)
|
|
14060
|
+
] })
|
|
14061
|
+
] }),
|
|
14062
|
+
/* @__PURE__ */ jsx175("div", { className: "mb-2", children: /* @__PURE__ */ jsxs112("div", { className: "text-2xl font-bold", children: [
|
|
14063
|
+
formatCurrency(price.unitAmount, price.currency),
|
|
14064
|
+
" ",
|
|
14065
|
+
/* @__PURE__ */ jsx175("span", { className: "text-muted-foreground text-sm font-normal", children: formatInterval2(price) })
|
|
14066
|
+
] }) }),
|
|
14067
|
+
price.metadata?.nickname && /* @__PURE__ */ jsx175("p", { className: "text-sm font-medium mb-2", children: price.metadata.nickname }),
|
|
14068
|
+
/* @__PURE__ */ jsxs112("div", { className: "flex flex-wrap gap-2", children: [
|
|
14069
|
+
price.active ? /* @__PURE__ */ jsx175("span", { className: "bg-green-100 text-green-800 text-xs px-2 py-1 rounded-full font-medium", children: "Active" }) : /* @__PURE__ */ jsx175("span", { className: "bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded-full font-medium", children: "Inactive" }),
|
|
14070
|
+
price.recurring?.usageType === "metered" && /* @__PURE__ */ jsx175("span", { className: "bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full font-medium", children: "Metered" }),
|
|
14071
|
+
/* @__PURE__ */ jsx175("span", { className: "bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded-full font-medium uppercase", children: price.currency })
|
|
14072
|
+
] })
|
|
14073
|
+
] }, price.id);
|
|
14074
|
+
}) }),
|
|
14075
|
+
showCreatePrice && /* @__PURE__ */ jsx175(
|
|
14076
|
+
PriceEditor,
|
|
14077
|
+
{
|
|
14078
|
+
productId,
|
|
14079
|
+
open: showCreatePrice,
|
|
14080
|
+
onOpenChange: setShowCreatePrice,
|
|
14081
|
+
onSuccess: () => {
|
|
14082
|
+
loadPrices();
|
|
14083
|
+
onPricesChange();
|
|
14084
|
+
}
|
|
14085
|
+
}
|
|
14086
|
+
),
|
|
14087
|
+
editingPrice && /* @__PURE__ */ jsx175(
|
|
14088
|
+
PriceEditor,
|
|
14089
|
+
{
|
|
14090
|
+
productId,
|
|
14091
|
+
price: editingPrice,
|
|
14092
|
+
open: !!editingPrice,
|
|
14093
|
+
onOpenChange: (open) => !open && setEditingPrice(null),
|
|
14094
|
+
onSuccess: () => {
|
|
14095
|
+
loadPrices();
|
|
14096
|
+
onPricesChange();
|
|
14097
|
+
setEditingPrice(null);
|
|
14098
|
+
}
|
|
14099
|
+
}
|
|
14100
|
+
),
|
|
14101
|
+
/* @__PURE__ */ jsx175(AlertDialog, { open: !!priceToArchive, onOpenChange: (open) => !open && setPriceToArchive(null), children: /* @__PURE__ */ jsxs112(AlertDialogContent, { children: [
|
|
14102
|
+
/* @__PURE__ */ jsxs112(AlertDialogHeader, { children: [
|
|
14103
|
+
/* @__PURE__ */ jsx175(AlertDialogTitle, { children: "Archive Price" }),
|
|
14104
|
+
/* @__PURE__ */ jsxs112(AlertDialogDescription, { children: [
|
|
14105
|
+
"Are you sure you want to archive the price for",
|
|
14106
|
+
" ",
|
|
14107
|
+
priceToArchive && `${formatCurrency(priceToArchive.unitAmount, priceToArchive.currency)} ${formatInterval2(priceToArchive)}`,
|
|
14108
|
+
"? This will prevent new subscriptions but existing ones will continue."
|
|
14109
|
+
] })
|
|
14110
|
+
] }),
|
|
14111
|
+
/* @__PURE__ */ jsxs112(AlertDialogFooter, { children: [
|
|
14112
|
+
/* @__PURE__ */ jsx175(AlertDialogCancel, { disabled: !!archivingPriceId, children: "Cancel" }),
|
|
14113
|
+
/* @__PURE__ */ jsx175(
|
|
14114
|
+
AlertDialogAction,
|
|
14115
|
+
{
|
|
14116
|
+
onClick: handleArchive,
|
|
14117
|
+
disabled: !!archivingPriceId,
|
|
14118
|
+
className: "bg-red-600 hover:bg-red-700",
|
|
14119
|
+
children: archivingPriceId ? "Archiving..." : "Archive"
|
|
14120
|
+
}
|
|
14121
|
+
)
|
|
14122
|
+
] })
|
|
14123
|
+
] }) }),
|
|
14124
|
+
/* @__PURE__ */ jsx175(AlertDialog, { open: !!priceToReactivate, onOpenChange: (open) => !open && setPriceToReactivate(null), children: /* @__PURE__ */ jsxs112(AlertDialogContent, { children: [
|
|
14125
|
+
/* @__PURE__ */ jsxs112(AlertDialogHeader, { children: [
|
|
14126
|
+
/* @__PURE__ */ jsx175(AlertDialogTitle, { children: "Reactivate Price" }),
|
|
14127
|
+
/* @__PURE__ */ jsxs112(AlertDialogDescription, { children: [
|
|
14128
|
+
"Are you sure you want to reactivate the price for",
|
|
14129
|
+
" ",
|
|
14130
|
+
priceToReactivate && `${formatCurrency(priceToReactivate.unitAmount, priceToReactivate.currency)} ${formatInterval2(priceToReactivate)}`,
|
|
14131
|
+
"? This will allow new subscriptions again."
|
|
14132
|
+
] })
|
|
14133
|
+
] }),
|
|
14134
|
+
/* @__PURE__ */ jsxs112(AlertDialogFooter, { children: [
|
|
14135
|
+
/* @__PURE__ */ jsx175(AlertDialogCancel, { disabled: !!reactivatingPriceId, children: "Cancel" }),
|
|
14136
|
+
/* @__PURE__ */ jsx175(
|
|
14137
|
+
AlertDialogAction,
|
|
14138
|
+
{
|
|
14139
|
+
onClick: handleReactivate,
|
|
14140
|
+
disabled: !!reactivatingPriceId,
|
|
14141
|
+
className: "bg-green-600 hover:bg-green-700",
|
|
14142
|
+
children: reactivatingPriceId ? "Reactivating..." : "Reactivate"
|
|
14143
|
+
}
|
|
14144
|
+
)
|
|
14145
|
+
] })
|
|
14146
|
+
] }) })
|
|
14147
|
+
] });
|
|
14148
|
+
}
|
|
14149
|
+
__name(PricesList, "PricesList");
|
|
14150
|
+
|
|
14151
|
+
// src/features/billing/stripe-product/components/containers/ProductsAdminContainer.tsx
|
|
14152
|
+
import { Package as Package2 } from "lucide-react";
|
|
14153
|
+
import { useEffect as useEffect49, useState as useState67 } from "react";
|
|
14154
|
+
|
|
14155
|
+
// src/features/billing/stripe-product/components/forms/ProductEditor.tsx
|
|
14156
|
+
import { zodResolver as zodResolver12 } from "@hookform/resolvers/zod";
|
|
14157
|
+
import { useState as useState65 } from "react";
|
|
14158
|
+
import { useForm as useForm12 } from "react-hook-form";
|
|
14159
|
+
import { v4 as v48 } from "uuid";
|
|
14160
|
+
import { z as z12 } from "zod";
|
|
14161
|
+
import { jsx as jsx176, jsxs as jsxs113 } from "react/jsx-runtime";
|
|
14162
|
+
function ProductEditor({ product, open, onOpenChange, onSuccess }) {
|
|
14163
|
+
const [isSubmitting, setIsSubmitting] = useState65(false);
|
|
14164
|
+
const formSchema2 = z12.object({
|
|
14165
|
+
name: z12.string().min(1, { message: "Product name is required" }),
|
|
14166
|
+
description: z12.string().optional(),
|
|
14167
|
+
active: z12.boolean()
|
|
14168
|
+
});
|
|
14169
|
+
const form = useForm12({
|
|
14170
|
+
resolver: zodResolver12(formSchema2),
|
|
14171
|
+
defaultValues: {
|
|
14172
|
+
name: product?.name || "",
|
|
14173
|
+
description: product?.description || "",
|
|
14174
|
+
active: product?.active ?? true
|
|
14175
|
+
}
|
|
14176
|
+
});
|
|
14177
|
+
const onSubmit = /* @__PURE__ */ __name(async (values) => {
|
|
14178
|
+
setIsSubmitting(true);
|
|
14179
|
+
try {
|
|
14180
|
+
if (product) {
|
|
14181
|
+
await StripeProductService.updateProduct({
|
|
14182
|
+
id: product.id,
|
|
14183
|
+
name: values.name,
|
|
14184
|
+
description: values.description,
|
|
14185
|
+
active: values.active
|
|
14186
|
+
});
|
|
14187
|
+
} else {
|
|
14188
|
+
await StripeProductService.createProduct({
|
|
14189
|
+
id: v48(),
|
|
14190
|
+
name: values.name,
|
|
14191
|
+
description: values.description,
|
|
14192
|
+
active: values.active
|
|
14193
|
+
});
|
|
14194
|
+
}
|
|
14195
|
+
onSuccess();
|
|
14196
|
+
onOpenChange(false);
|
|
14197
|
+
} catch (error) {
|
|
14198
|
+
console.error("[ProductEditor] Failed to save product:", error);
|
|
14199
|
+
} finally {
|
|
14200
|
+
setIsSubmitting(false);
|
|
14201
|
+
}
|
|
14202
|
+
}, "onSubmit");
|
|
14203
|
+
return /* @__PURE__ */ jsx176(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs113(DialogContent, { className: "max-w-2xl", children: [
|
|
14204
|
+
/* @__PURE__ */ jsxs113(DialogHeader, { children: [
|
|
14205
|
+
/* @__PURE__ */ jsx176(DialogTitle, { children: product ? "Edit Product" : "Create Product" }),
|
|
14206
|
+
/* @__PURE__ */ jsx176(DialogDescription, { children: product ? `Update the details for ${product.name}` : "Create a new product to offer to your customers" })
|
|
14207
|
+
] }),
|
|
14208
|
+
/* @__PURE__ */ jsx176(Form, { ...form, children: /* @__PURE__ */ jsxs113("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-col gap-y-4", children: [
|
|
14209
|
+
/* @__PURE__ */ jsx176(FormInput, { form, id: "name", name: "Product Name", placeholder: "Enter product name", isRequired: true }),
|
|
14210
|
+
/* @__PURE__ */ jsx176(
|
|
14211
|
+
FormTextarea,
|
|
14212
|
+
{
|
|
14213
|
+
form,
|
|
14214
|
+
id: "description",
|
|
14215
|
+
name: "Description",
|
|
14216
|
+
placeholder: "Enter product description (optional)",
|
|
14217
|
+
className: "min-h-32"
|
|
14218
|
+
}
|
|
14219
|
+
),
|
|
14220
|
+
/* @__PURE__ */ jsx176(FormCheckbox, { form, id: "active", name: "Active" }),
|
|
14221
|
+
/* @__PURE__ */ jsx176(CommonEditorButtons, { isEdit: !!product, form, disabled: isSubmitting, setOpen: onOpenChange })
|
|
14222
|
+
] }) })
|
|
14223
|
+
] }) });
|
|
14224
|
+
}
|
|
14225
|
+
__name(ProductEditor, "ProductEditor");
|
|
14226
|
+
|
|
14227
|
+
// src/features/billing/stripe-product/components/lists/ProductsList.tsx
|
|
14228
|
+
import { Archive as Archive2, ChevronDown as ChevronDown3, ChevronUp as ChevronUp2, Edit as Edit2, Package, RefreshCw as RefreshCw3 } from "lucide-react";
|
|
14229
|
+
import { useState as useState66 } from "react";
|
|
14230
|
+
import { jsx as jsx177, jsxs as jsxs114 } from "react/jsx-runtime";
|
|
14231
|
+
function ProductsList({ products, onProductsChange }) {
|
|
14232
|
+
const [expandedProductId, setExpandedProductId] = useState66(null);
|
|
14233
|
+
const [editingProduct, setEditingProduct] = useState66(null);
|
|
14234
|
+
const [archivingProductId, setArchivingProductId] = useState66(null);
|
|
14235
|
+
const [reactivatingProductId, setReactivatingProductId] = useState66(null);
|
|
14236
|
+
const [productToArchive, setProductToArchive] = useState66(null);
|
|
14237
|
+
const [productToReactivate, setProductToReactivate] = useState66(null);
|
|
14238
|
+
const handleArchive = /* @__PURE__ */ __name(async () => {
|
|
14239
|
+
if (!productToArchive) {
|
|
14240
|
+
return;
|
|
14241
|
+
}
|
|
14242
|
+
setArchivingProductId(productToArchive.id);
|
|
14243
|
+
try {
|
|
14244
|
+
const archivedProduct = await StripeProductService.archiveProduct({ id: productToArchive.id });
|
|
14245
|
+
setProductToArchive(null);
|
|
14246
|
+
onProductsChange();
|
|
14247
|
+
} catch (error) {
|
|
14248
|
+
console.error("[ProductsList] Failed to archive product:", error);
|
|
14249
|
+
} finally {
|
|
14250
|
+
setArchivingProductId(null);
|
|
14251
|
+
}
|
|
14252
|
+
}, "handleArchive");
|
|
14253
|
+
const handleReactivate = /* @__PURE__ */ __name(async () => {
|
|
14254
|
+
if (!productToReactivate) {
|
|
14255
|
+
return;
|
|
14256
|
+
}
|
|
14257
|
+
setReactivatingProductId(productToReactivate.id);
|
|
14258
|
+
try {
|
|
14259
|
+
const reactivatedProduct = await StripeProductService.reactivateProduct({ id: productToReactivate.id });
|
|
14260
|
+
setProductToReactivate(null);
|
|
14261
|
+
onProductsChange();
|
|
14262
|
+
} catch (error) {
|
|
14263
|
+
} finally {
|
|
14264
|
+
setReactivatingProductId(null);
|
|
14265
|
+
}
|
|
14266
|
+
}, "handleReactivate");
|
|
14267
|
+
const toggleExpand = /* @__PURE__ */ __name((productId) => {
|
|
14268
|
+
setExpandedProductId(expandedProductId === productId ? null : productId);
|
|
14269
|
+
}, "toggleExpand");
|
|
14270
|
+
return /* @__PURE__ */ jsxs114("div", { className: "flex flex-col gap-y-4", children: [
|
|
14271
|
+
products.map((product) => {
|
|
14272
|
+
const isExpanded = expandedProductId === product.id;
|
|
14273
|
+
const isArchiving = archivingProductId === product.id;
|
|
14274
|
+
const isReactivating = reactivatingProductId === product.id;
|
|
14275
|
+
return /* @__PURE__ */ jsxs114("div", { className: "border rounded-lg bg-white shadow-sm hover:shadow-md transition-shadow", children: [
|
|
14276
|
+
/* @__PURE__ */ jsxs114("div", { className: "flex items-center justify-between p-6", children: [
|
|
14277
|
+
/* @__PURE__ */ jsxs114("div", { className: "flex items-center gap-x-4 flex-1", children: [
|
|
14278
|
+
/* @__PURE__ */ jsx177(Package, { className: "h-6 w-6 text-primary" }),
|
|
14279
|
+
/* @__PURE__ */ jsxs114("div", { className: "flex-1", children: [
|
|
14280
|
+
/* @__PURE__ */ jsxs114("div", { className: "flex items-center gap-x-3", children: [
|
|
14281
|
+
/* @__PURE__ */ jsx177("h3", { className: "text-lg font-semibold", children: product.name }),
|
|
14282
|
+
product.active ? /* @__PURE__ */ jsx177("span", { className: "bg-green-100 text-green-800 text-xs px-2 py-1 rounded-full font-medium", children: "Active" }) : /* @__PURE__ */ jsx177("span", { className: "bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded-full font-medium", children: "Inactive" })
|
|
14283
|
+
] }),
|
|
14284
|
+
product.description && /* @__PURE__ */ jsx177("p", { className: "text-muted-foreground text-sm mt-1", children: product.description })
|
|
14285
|
+
] })
|
|
14286
|
+
] }),
|
|
14287
|
+
/* @__PURE__ */ jsxs114("div", { className: "flex items-center gap-x-2", children: [
|
|
14288
|
+
/* @__PURE__ */ jsxs114(Button, { variant: "outline", size: "sm", onClick: () => setEditingProduct(product), children: [
|
|
14289
|
+
/* @__PURE__ */ jsx177(Edit2, { className: "h-4 w-4 mr-1" }),
|
|
14290
|
+
"Edit"
|
|
14291
|
+
] }),
|
|
14292
|
+
product.active ? /* @__PURE__ */ jsxs114(
|
|
14293
|
+
Button,
|
|
14294
|
+
{
|
|
14295
|
+
variant: "outline",
|
|
14296
|
+
size: "sm",
|
|
14297
|
+
onClick: () => setProductToArchive(product),
|
|
14298
|
+
disabled: isArchiving,
|
|
14299
|
+
children: [
|
|
14300
|
+
/* @__PURE__ */ jsx177(Archive2, { className: "h-4 w-4 mr-1" }),
|
|
14301
|
+
isArchiving ? "Archiving..." : "Archive"
|
|
14302
|
+
]
|
|
14303
|
+
}
|
|
14304
|
+
) : /* @__PURE__ */ jsxs114(
|
|
14305
|
+
Button,
|
|
14306
|
+
{
|
|
14307
|
+
variant: "outline",
|
|
14308
|
+
size: "sm",
|
|
14309
|
+
onClick: () => setProductToReactivate(product),
|
|
14310
|
+
disabled: isReactivating,
|
|
14311
|
+
children: [
|
|
14312
|
+
/* @__PURE__ */ jsx177(RefreshCw3, { className: "h-4 w-4 mr-1" }),
|
|
14313
|
+
isReactivating ? "Reactivating..." : "Reactivate"
|
|
14314
|
+
]
|
|
14315
|
+
}
|
|
14316
|
+
),
|
|
14317
|
+
/* @__PURE__ */ jsx177(Button, { variant: "ghost", size: "sm", onClick: () => toggleExpand(product.id), children: isExpanded ? /* @__PURE__ */ jsx177(ChevronUp2, { className: "h-5 w-5" }) : /* @__PURE__ */ jsx177(ChevronDown3, { className: "h-5 w-5" }) })
|
|
14318
|
+
] })
|
|
14319
|
+
] }),
|
|
14320
|
+
isExpanded && /* @__PURE__ */ jsx177("div", { className: "border-t bg-muted/30 p-6", children: /* @__PURE__ */ jsx177(PricesList, { productId: product.id, onPricesChange: onProductsChange }) })
|
|
14321
|
+
] }, product.id);
|
|
14322
|
+
}),
|
|
14323
|
+
editingProduct && /* @__PURE__ */ jsx177(
|
|
14324
|
+
ProductEditor,
|
|
14325
|
+
{
|
|
14326
|
+
product: editingProduct,
|
|
14327
|
+
open: !!editingProduct,
|
|
14328
|
+
onOpenChange: (open) => !open && setEditingProduct(null),
|
|
14329
|
+
onSuccess: () => {
|
|
14330
|
+
onProductsChange();
|
|
14331
|
+
setEditingProduct(null);
|
|
14332
|
+
}
|
|
14333
|
+
}
|
|
14334
|
+
),
|
|
14335
|
+
/* @__PURE__ */ jsx177(AlertDialog, { open: !!productToArchive, onOpenChange: (open) => !open && setProductToArchive(null), children: /* @__PURE__ */ jsxs114(AlertDialogContent, { children: [
|
|
14336
|
+
/* @__PURE__ */ jsxs114(AlertDialogHeader, { children: [
|
|
14337
|
+
/* @__PURE__ */ jsx177(AlertDialogTitle, { children: "Archive Product" }),
|
|
14338
|
+
/* @__PURE__ */ jsxs114(AlertDialogDescription, { children: [
|
|
14339
|
+
'Are you sure you want to archive "',
|
|
14340
|
+
productToArchive?.name,
|
|
14341
|
+
'"? This will deactivate it and it will no longer be available for new subscriptions.'
|
|
14342
|
+
] })
|
|
14343
|
+
] }),
|
|
14344
|
+
/* @__PURE__ */ jsxs114(AlertDialogFooter, { children: [
|
|
14345
|
+
/* @__PURE__ */ jsx177(AlertDialogCancel, { disabled: !!archivingProductId, children: "Cancel" }),
|
|
14346
|
+
/* @__PURE__ */ jsx177(
|
|
14347
|
+
AlertDialogAction,
|
|
14348
|
+
{
|
|
14349
|
+
onClick: handleArchive,
|
|
14350
|
+
disabled: !!archivingProductId,
|
|
14351
|
+
className: "bg-red-600 hover:bg-red-700",
|
|
14352
|
+
children: archivingProductId ? "Archiving..." : "Archive"
|
|
14353
|
+
}
|
|
14354
|
+
)
|
|
14355
|
+
] })
|
|
14356
|
+
] }) }),
|
|
14357
|
+
/* @__PURE__ */ jsx177(AlertDialog, { open: !!productToReactivate, onOpenChange: (open) => !open && setProductToReactivate(null), children: /* @__PURE__ */ jsxs114(AlertDialogContent, { children: [
|
|
14358
|
+
/* @__PURE__ */ jsxs114(AlertDialogHeader, { children: [
|
|
14359
|
+
/* @__PURE__ */ jsx177(AlertDialogTitle, { children: "Reactivate Product" }),
|
|
14360
|
+
/* @__PURE__ */ jsxs114(AlertDialogDescription, { children: [
|
|
14361
|
+
'Are you sure you want to reactivate "',
|
|
14362
|
+
productToReactivate?.name,
|
|
14363
|
+
'"? This will make it available for new subscriptions again.'
|
|
14364
|
+
] })
|
|
14365
|
+
] }),
|
|
14366
|
+
/* @__PURE__ */ jsxs114(AlertDialogFooter, { children: [
|
|
14367
|
+
/* @__PURE__ */ jsx177(AlertDialogCancel, { disabled: !!reactivatingProductId, children: "Cancel" }),
|
|
14368
|
+
/* @__PURE__ */ jsx177(
|
|
14369
|
+
AlertDialogAction,
|
|
14370
|
+
{
|
|
14371
|
+
onClick: handleReactivate,
|
|
14372
|
+
disabled: !!reactivatingProductId,
|
|
14373
|
+
className: "bg-green-600 hover:bg-green-700",
|
|
14374
|
+
children: reactivatingProductId ? "Reactivating..." : "Reactivate"
|
|
14375
|
+
}
|
|
14376
|
+
)
|
|
14377
|
+
] })
|
|
14378
|
+
] }) })
|
|
14379
|
+
] });
|
|
14380
|
+
}
|
|
14381
|
+
__name(ProductsList, "ProductsList");
|
|
14382
|
+
|
|
14383
|
+
// src/features/billing/stripe-product/components/containers/ProductsAdminContainer.tsx
|
|
14384
|
+
import { jsx as jsx178, jsxs as jsxs115 } from "react/jsx-runtime";
|
|
14385
|
+
function ProductsAdminContainer() {
|
|
14386
|
+
const { hasRole } = useCurrentUserContext();
|
|
14387
|
+
const [products, setProducts] = useState67([]);
|
|
14388
|
+
const [loading, setLoading] = useState67(true);
|
|
14389
|
+
const [showCreateProduct, setShowCreateProduct] = useState67(false);
|
|
14390
|
+
if (!hasRole(getRoleId().Administrator)) {
|
|
14391
|
+
return /* @__PURE__ */ jsx178("div", { className: "flex h-64 items-center justify-center", children: /* @__PURE__ */ jsx178("p", { className: "text-red-600 font-semibold", children: "Permission denied. Administrator access required." }) });
|
|
14392
|
+
}
|
|
14393
|
+
const loadProducts = /* @__PURE__ */ __name(async () => {
|
|
14394
|
+
setLoading(true);
|
|
14395
|
+
try {
|
|
14396
|
+
const fetchedProducts = await StripeProductService.listProducts();
|
|
14397
|
+
setProducts(fetchedProducts);
|
|
14398
|
+
} catch (error) {
|
|
14399
|
+
console.error("[ProductsAdminContainer] Failed to load products:", error);
|
|
14400
|
+
} finally {
|
|
14401
|
+
setLoading(false);
|
|
14402
|
+
}
|
|
14403
|
+
}, "loadProducts");
|
|
14404
|
+
useEffect49(() => {
|
|
14405
|
+
loadProducts();
|
|
14406
|
+
}, []);
|
|
14407
|
+
if (loading) {
|
|
14408
|
+
return /* @__PURE__ */ jsx178("div", { className: "flex h-64 items-center justify-center", children: /* @__PURE__ */ jsx178("p", { className: "text-muted-foreground", children: "Loading products..." }) });
|
|
14409
|
+
}
|
|
14410
|
+
return /* @__PURE__ */ jsxs115("div", { className: "flex w-full flex-col gap-y-6", children: [
|
|
14411
|
+
/* @__PURE__ */ jsxs115("div", { className: "flex items-center justify-between", children: [
|
|
14412
|
+
/* @__PURE__ */ jsxs115("div", { className: "flex items-center gap-x-3", children: [
|
|
14413
|
+
/* @__PURE__ */ jsx178(Package2, { className: "h-8 w-8" }),
|
|
14414
|
+
/* @__PURE__ */ jsx178("h1", { className: "text-3xl font-bold", children: "Product & Price Management" })
|
|
14415
|
+
] }),
|
|
14416
|
+
/* @__PURE__ */ jsx178(Button, { onClick: () => setShowCreateProduct(true), children: "Create Product" })
|
|
14417
|
+
] }),
|
|
14418
|
+
products.length === 0 && /* @__PURE__ */ jsxs115("div", { className: "bg-muted/50 flex flex-col items-center justify-center gap-y-4 rounded-lg border-2 border-dashed p-12", children: [
|
|
14419
|
+
/* @__PURE__ */ jsx178(Package2, { className: "text-muted-foreground h-16 w-16" }),
|
|
14420
|
+
/* @__PURE__ */ jsxs115("div", { className: "text-center", children: [
|
|
14421
|
+
/* @__PURE__ */ jsx178("h3", { className: "mb-2 text-xl font-semibold", children: "No products yet" }),
|
|
14422
|
+
/* @__PURE__ */ jsx178("p", { className: "text-muted-foreground mb-4", children: "Create your first product to start offering subscriptions to your customers." }),
|
|
14423
|
+
/* @__PURE__ */ jsx178(Button, { onClick: () => setShowCreateProduct(true), children: "Create Your First Product" })
|
|
14424
|
+
] })
|
|
14425
|
+
] }),
|
|
14426
|
+
products.length > 0 && /* @__PURE__ */ jsx178(ProductsList, { products, onProductsChange: loadProducts }),
|
|
14427
|
+
showCreateProduct && /* @__PURE__ */ jsx178(ProductEditor, { open: showCreateProduct, onOpenChange: setShowCreateProduct, onSuccess: loadProducts })
|
|
14428
|
+
] });
|
|
14429
|
+
}
|
|
14430
|
+
__name(ProductsAdminContainer, "ProductsAdminContainer");
|
|
14431
|
+
|
|
14432
|
+
// src/features/content/components/lists/ContentsList.tsx
|
|
14433
|
+
import { useTranslations as useTranslations53 } from "next-intl";
|
|
14434
|
+
import { jsx as jsx179, jsxs as jsxs116 } from "react/jsx-runtime";
|
|
14435
|
+
function ContentsList({ contentList }) {
|
|
14436
|
+
const t = useTranslations53();
|
|
14437
|
+
return /* @__PURE__ */ jsxs116("div", { className: "flex min-h-0 w-full flex-col overflow-y-auto", children: [
|
|
14438
|
+
/* @__PURE__ */ jsx179("h2", { className: "text-xl font-semibold", children: t(`foundations.content.news`) }),
|
|
14439
|
+
/* @__PURE__ */ jsx179("div", { className: "flex flex-col", children: contentList.map((content) => /* @__PURE__ */ jsx179(ContentsListElement, { content }, content.id)) })
|
|
14440
|
+
] });
|
|
14441
|
+
}
|
|
14442
|
+
__name(ContentsList, "ContentsList");
|
|
14443
|
+
function ContentsListElement({ content }) {
|
|
14444
|
+
const generateUrl = usePageUrlGenerator();
|
|
14445
|
+
const contentModule = content.contentType ? Modules.findByModelName(content.contentType) : void 0;
|
|
14446
|
+
const link = contentModule ? generateUrl({ page: contentModule, id: content.id }) : "#";
|
|
14447
|
+
return /* @__PURE__ */ jsx179("div", { className: "hover:bg-muted flex w-full flex-col gap-y-2 border-b p-2 py-4", children: /* @__PURE__ */ jsxs116("div", { className: "flex w-full justify-between gap-x-2", children: [
|
|
14448
|
+
/* @__PURE__ */ jsxs116(HoverCard, { children: [
|
|
14449
|
+
/* @__PURE__ */ jsx179(HoverCardTrigger, { asChild: true, children: /* @__PURE__ */ jsxs116(Link2, { href: link, className: "flex w-full items-center justify-start gap-2 font-semibold", children: [
|
|
14450
|
+
contentModule && getIconByModule({ module: contentModule, className: "h-4 w-4" }),
|
|
14451
|
+
content.name
|
|
14452
|
+
] }) }),
|
|
14453
|
+
/* @__PURE__ */ jsxs116(HoverCardContent, { className: "flex max-h-96 w-96 flex-col gap-y-4 overflow-y-auto", children: [
|
|
14454
|
+
/* @__PURE__ */ jsx179(Link2, { href: link, className: "font-semibold", children: content.name }),
|
|
14455
|
+
/* @__PURE__ */ jsx179("div", { className: "text-xs", children: content.abstract })
|
|
14456
|
+
] })
|
|
14457
|
+
] }),
|
|
14458
|
+
/* @__PURE__ */ jsx179(ContributorsList, { content })
|
|
14459
|
+
] }) });
|
|
14460
|
+
}
|
|
14461
|
+
__name(ContentsListElement, "ContentsListElement");
|
|
14462
|
+
|
|
14463
|
+
// src/features/content/components/lists/ContentsListById.tsx
|
|
14464
|
+
import { useTranslations as useTranslations54 } from "next-intl";
|
|
14465
|
+
import { jsx as jsx180 } from "react/jsx-runtime";
|
|
14466
|
+
function ContentsListById({ contentIds }) {
|
|
14467
|
+
const t = useTranslations54();
|
|
14468
|
+
const data = useDataListRetriever({
|
|
14469
|
+
module: Modules.Content,
|
|
14470
|
+
retriever: /* @__PURE__ */ __name((params) => ContentService.findMany(params), "retriever"),
|
|
14471
|
+
retrieverParams: { contentIds }
|
|
14472
|
+
});
|
|
14473
|
+
return /* @__PURE__ */ jsx180(
|
|
14474
|
+
ContentListTable,
|
|
14475
|
+
{
|
|
14476
|
+
data,
|
|
14477
|
+
fields: ["name" /* name */, "authors" /* authors */, "updatedAt" /* updatedAt */],
|
|
14478
|
+
tableGeneratorType: Modules.Content,
|
|
14479
|
+
title: t(`generic.relevant`)
|
|
14480
|
+
}
|
|
14481
|
+
);
|
|
14482
|
+
}
|
|
14483
|
+
__name(ContentsListById, "ContentsListById");
|
|
14484
|
+
|
|
14485
|
+
// src/features/content/components/lists/RelevantContentsList.tsx
|
|
14486
|
+
import { useTranslations as useTranslations55 } from "next-intl";
|
|
14487
|
+
import { jsx as jsx181 } from "react/jsx-runtime";
|
|
14488
|
+
function RelevantContentsList({ id }) {
|
|
14489
|
+
const t = useTranslations55();
|
|
14490
|
+
const data = useDataListRetriever({
|
|
14491
|
+
module: Modules.Content,
|
|
14492
|
+
retriever: /* @__PURE__ */ __name((params) => ContentService.findRelevant(params), "retriever"),
|
|
14493
|
+
retrieverParams: { id }
|
|
14494
|
+
});
|
|
14495
|
+
return /* @__PURE__ */ jsx181(
|
|
14496
|
+
ContentListTable,
|
|
14497
|
+
{
|
|
14498
|
+
data,
|
|
14499
|
+
fields: ["name" /* name */, "authors" /* authors */, "relevance" /* relevance */, "updatedAt" /* updatedAt */],
|
|
14500
|
+
tableGeneratorType: Modules.Content,
|
|
14501
|
+
title: t(`generic.relevant`)
|
|
14502
|
+
}
|
|
14503
|
+
);
|
|
14504
|
+
}
|
|
14505
|
+
__name(RelevantContentsList, "RelevantContentsList");
|
|
14506
|
+
|
|
14507
|
+
// src/features/notification/components/common/NotificationErrorBoundary.tsx
|
|
14508
|
+
import { Component } from "react";
|
|
14509
|
+
import { jsx as jsx182, jsxs as jsxs117 } from "react/jsx-runtime";
|
|
14510
|
+
var NotificationErrorBoundary = class extends Component {
|
|
14511
|
+
static {
|
|
14512
|
+
__name(this, "NotificationErrorBoundary");
|
|
14513
|
+
}
|
|
14514
|
+
constructor(props) {
|
|
14515
|
+
super(props);
|
|
14516
|
+
this.state = { hasError: false };
|
|
14517
|
+
}
|
|
14518
|
+
static getDerivedStateFromError(error) {
|
|
14519
|
+
return { hasError: true, error };
|
|
14520
|
+
}
|
|
14521
|
+
componentDidCatch(error, errorInfo) {
|
|
14522
|
+
console.error("\u{1F6A8} [NotificationErrorBoundary] Caught error:", error, errorInfo);
|
|
14523
|
+
}
|
|
14524
|
+
render() {
|
|
14525
|
+
if (this.state.hasError) {
|
|
14526
|
+
return this.props.fallback || /* @__PURE__ */ jsx182("div", { className: "flex items-center justify-center p-4 text-center", children: /* @__PURE__ */ jsxs117("div", { className: "text-muted-foreground text-sm", children: [
|
|
14527
|
+
/* @__PURE__ */ jsx182("p", { children: "Something went wrong with notifications." }),
|
|
14528
|
+
/* @__PURE__ */ jsx182(
|
|
14529
|
+
"button",
|
|
14530
|
+
{
|
|
14531
|
+
onClick: () => this.setState({ hasError: false }),
|
|
14532
|
+
className: "text-primary mt-2 underline hover:no-underline",
|
|
14533
|
+
children: "Try again"
|
|
14534
|
+
}
|
|
14535
|
+
)
|
|
14536
|
+
] }) });
|
|
14537
|
+
}
|
|
14538
|
+
return this.props.children;
|
|
14539
|
+
}
|
|
14540
|
+
};
|
|
14541
|
+
|
|
14542
|
+
// src/features/notification/components/containers/NotificationsListContainer.tsx
|
|
14543
|
+
import { useTranslations as useTranslations57 } from "next-intl";
|
|
14544
|
+
|
|
14545
|
+
// src/features/notification/components/lists/NotificationsList.tsx
|
|
14546
|
+
import { ArchiveIcon } from "lucide-react";
|
|
14547
|
+
import { useTranslations as useTranslations56 } from "next-intl";
|
|
14548
|
+
import { Fragment as Fragment35, jsx as jsx183, jsxs as jsxs118 } from "react/jsx-runtime";
|
|
14549
|
+
function NotificationsList({ archived }) {
|
|
14550
|
+
const t = useTranslations56();
|
|
14551
|
+
const generateUrl = usePageUrlGenerator();
|
|
14552
|
+
const data = useDataListRetriever({
|
|
14553
|
+
retriever: /* @__PURE__ */ __name((params) => NotificationService.findMany(params), "retriever"),
|
|
14554
|
+
retrieverParams: { isArchived: archived },
|
|
14555
|
+
module: Modules.Notification
|
|
14556
|
+
});
|
|
14557
|
+
const archiveNotification = /* @__PURE__ */ __name(async (notification) => {
|
|
14558
|
+
await NotificationService.archive({ id: notification.id });
|
|
14559
|
+
data.removeElement(notification);
|
|
14560
|
+
}, "archiveNotification");
|
|
14561
|
+
const LoadingSkeleton = /* @__PURE__ */ __name(() => /* @__PURE__ */ jsx183("div", { className: "space-y-4", children: Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ jsx183(Card, { children: /* @__PURE__ */ jsx183(CardContent, { className: "p-2", children: /* @__PURE__ */ jsxs118("div", { className: "flex w-full flex-row items-center", children: [
|
|
14562
|
+
/* @__PURE__ */ jsx183(Skeleton, { className: "mr-4 h-8 w-8 rounded-full" }),
|
|
14563
|
+
/* @__PURE__ */ jsxs118("div", { className: "flex-1 space-y-2", children: [
|
|
14564
|
+
/* @__PURE__ */ jsx183(Skeleton, { className: "h-4 w-3/4" }),
|
|
14565
|
+
/* @__PURE__ */ jsx183(Skeleton, { className: "h-3 w-1/2" })
|
|
14566
|
+
] }),
|
|
14567
|
+
/* @__PURE__ */ jsx183(Skeleton, { className: "h-8 w-20" })
|
|
14568
|
+
] }) }) }, i)) }), "LoadingSkeleton");
|
|
14569
|
+
return /* @__PURE__ */ jsx183("div", { className: "space-y-4", children: data.isLoaded ? data.data?.map((notification) => {
|
|
14570
|
+
const notificationData = generateNotificationData({ notification, generateUrl });
|
|
14571
|
+
return /* @__PURE__ */ jsx183(Card, { children: /* @__PURE__ */ jsx183(CardContent, { className: "p-0", children: /* @__PURE__ */ jsxs118("div", { className: `flex w-full flex-row items-center p-2`, children: [
|
|
14572
|
+
notificationData.actor ? /* @__PURE__ */ jsx183("div", { className: "flex w-12 max-w-12 px-2", children: /* @__PURE__ */ jsx183(Link2, { href: generateUrl({ page: Modules.User, id: notificationData.actor.id }), children: /* @__PURE__ */ jsx183(UserAvatar, { user: notificationData.actor, className: "h-8 w-8" }) }) }) : /* @__PURE__ */ jsx183("div", { className: "flex w-14 max-w-14 px-2" }),
|
|
14573
|
+
/* @__PURE__ */ jsxs118("div", { className: "flex w-full flex-col", children: [
|
|
14574
|
+
/* @__PURE__ */ jsx183("p", { className: "text-sm", children: t.rich(`foundations.notification.${notification.notificationType}.description`, {
|
|
14575
|
+
strong: /* @__PURE__ */ __name((chunks) => /* @__PURE__ */ jsx183("strong", { children: chunks }), "strong"),
|
|
14576
|
+
actor: notificationData.actor?.name ?? "",
|
|
14577
|
+
title: notificationData.title
|
|
14578
|
+
}) }),
|
|
14579
|
+
/* @__PURE__ */ jsx183("div", { className: "text-muted-foreground mt-1 w-full text-xs", children: new Date(notification.createdAt).toLocaleString() })
|
|
14580
|
+
] }),
|
|
14581
|
+
/* @__PURE__ */ jsxs118("div", { className: "flex flex-row items-center", children: [
|
|
14582
|
+
notificationData.url ? /* @__PURE__ */ jsx183(Link2, { href: notificationData.url, children: /* @__PURE__ */ jsx183(Button, { variant: `outline`, size: `sm`, onClick: (e) => e.stopPropagation(), children: t(`foundations.notification.${notification.notificationType}.buttons.action`) }) }) : /* @__PURE__ */ jsx183(Fragment35, {}),
|
|
14583
|
+
!archived && /* @__PURE__ */ jsxs118(Tooltip2, { children: [
|
|
14584
|
+
/* @__PURE__ */ jsx183(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx183(
|
|
14585
|
+
Button,
|
|
14586
|
+
{
|
|
14587
|
+
variant: `link`,
|
|
14588
|
+
onClick: (e) => {
|
|
14589
|
+
e.preventDefault();
|
|
14590
|
+
e.stopPropagation();
|
|
14591
|
+
archiveNotification(notification);
|
|
14592
|
+
},
|
|
14593
|
+
className: "text-muted-foreground hover:text-destructive ml-2",
|
|
14594
|
+
children: /* @__PURE__ */ jsx183(ArchiveIcon, { className: "h-4 w-4 cursor-pointer" })
|
|
14595
|
+
}
|
|
14596
|
+
) }),
|
|
14597
|
+
/* @__PURE__ */ jsx183(TooltipContent, { children: t(`foundations.notification.buttons.archive`) })
|
|
14598
|
+
] })
|
|
14599
|
+
] })
|
|
14600
|
+
] }) }) }, notification.id);
|
|
14601
|
+
}) : /* @__PURE__ */ jsx183(LoadingSkeleton, {}) });
|
|
14602
|
+
}
|
|
14603
|
+
__name(NotificationsList, "NotificationsList");
|
|
14604
|
+
|
|
14605
|
+
// src/features/notification/components/containers/NotificationsListContainer.tsx
|
|
14606
|
+
import { jsx as jsx184, jsxs as jsxs119 } from "react/jsx-runtime";
|
|
14607
|
+
function NotificationsListContainerContent() {
|
|
14608
|
+
const t = useTranslations57();
|
|
14609
|
+
const { notifications, isLoading, error } = useNotificationContext();
|
|
14610
|
+
if (error) {
|
|
14611
|
+
return /* @__PURE__ */ jsx184("div", { className: "flex items-center justify-center p-8 text-center", children: /* @__PURE__ */ jsxs119("div", { className: "text-destructive text-sm", children: [
|
|
14612
|
+
/* @__PURE__ */ jsxs119("p", { children: [
|
|
14613
|
+
"Error loading notifications: ",
|
|
14614
|
+
error
|
|
14615
|
+
] }),
|
|
14616
|
+
/* @__PURE__ */ jsx184("p", { className: "text-muted-foreground mt-2", children: "Please try refreshing the page." })
|
|
14617
|
+
] }) });
|
|
14618
|
+
}
|
|
14619
|
+
const tabs = [
|
|
14620
|
+
{
|
|
14621
|
+
label: t(`foundations.notification.inbox`),
|
|
14622
|
+
content: /* @__PURE__ */ jsx184(NotificationsList, { archived: false })
|
|
14623
|
+
},
|
|
14624
|
+
{
|
|
14625
|
+
label: t(`foundations.notification.archived`),
|
|
14626
|
+
content: /* @__PURE__ */ jsx184(NotificationsList, { archived: true })
|
|
11352
14627
|
}
|
|
11353
14628
|
];
|
|
11354
|
-
return /* @__PURE__ */
|
|
14629
|
+
return /* @__PURE__ */ jsx184(TabsContainer, { tabs });
|
|
11355
14630
|
}
|
|
11356
14631
|
__name(NotificationsListContainerContent, "NotificationsListContainerContent");
|
|
11357
14632
|
function NotificationsListContainer() {
|
|
11358
|
-
return /* @__PURE__ */
|
|
14633
|
+
return /* @__PURE__ */ jsx184(NotificationErrorBoundary, { children: /* @__PURE__ */ jsx184(NotificationsListContainerContent, {}) });
|
|
11359
14634
|
}
|
|
11360
14635
|
__name(NotificationsListContainer, "NotificationsListContainer");
|
|
11361
14636
|
|
|
11362
14637
|
// src/features/notification/components/modals/NotificationModal.tsx
|
|
11363
14638
|
import { BellIcon } from "lucide-react";
|
|
11364
14639
|
import { useTranslations as useTranslations58 } from "next-intl";
|
|
11365
|
-
import { Fragment as
|
|
14640
|
+
import { Fragment as Fragment36, useCallback as useCallback19, useEffect as useEffect50, useMemo as useMemo19, useRef as useRef16, useState as useState68 } from "react";
|
|
11366
14641
|
import { toast as toast10 } from "sonner";
|
|
11367
|
-
import { jsx as
|
|
14642
|
+
import { jsx as jsx185, jsxs as jsxs120 } from "react/jsx-runtime";
|
|
11368
14643
|
function NotificationModalContent({ isOpen, setIsOpen }) {
|
|
11369
14644
|
const instanceId = useRef16(Math.random().toString(36).substr(2, 9));
|
|
11370
14645
|
const {
|
|
@@ -11382,14 +14657,14 @@ function NotificationModalContent({ isOpen, setIsOpen }) {
|
|
|
11382
14657
|
const { socketNotifications, removeSocketNotification, clearSocketNotifications } = useSocketContext();
|
|
11383
14658
|
const t = useTranslations58();
|
|
11384
14659
|
const generateUrl = usePageUrlGenerator();
|
|
11385
|
-
const [newNotifications, setNewNotifications] =
|
|
14660
|
+
const [newNotifications, setNewNotifications] = useState68(false);
|
|
11386
14661
|
const preventAutoClose = useRef16(false);
|
|
11387
14662
|
const circuitBreakerRef = useRef16({
|
|
11388
14663
|
count: 0,
|
|
11389
14664
|
resetTime: 0,
|
|
11390
14665
|
isOpen: false
|
|
11391
14666
|
});
|
|
11392
|
-
const checkCircuitBreaker =
|
|
14667
|
+
const checkCircuitBreaker = useCallback19(() => {
|
|
11393
14668
|
const now = Date.now();
|
|
11394
14669
|
const breaker = circuitBreakerRef.current;
|
|
11395
14670
|
if (now > breaker.resetTime) {
|
|
@@ -11404,21 +14679,21 @@ function NotificationModalContent({ isOpen, setIsOpen }) {
|
|
|
11404
14679
|
}
|
|
11405
14680
|
return !breaker.isOpen;
|
|
11406
14681
|
}, []);
|
|
11407
|
-
const { unreadCount, unreadIds } =
|
|
14682
|
+
const { unreadCount, unreadIds } = useMemo19(() => {
|
|
11408
14683
|
const unreadNotifications2 = notifications.filter((notif) => !notif.isRead);
|
|
11409
14684
|
return {
|
|
11410
14685
|
unreadCount: unreadNotifications2.length,
|
|
11411
14686
|
unreadIds: unreadNotifications2.map((notif) => notif.id)
|
|
11412
14687
|
};
|
|
11413
14688
|
}, [notifications]);
|
|
11414
|
-
|
|
14689
|
+
useEffect50(() => {
|
|
11415
14690
|
setNewNotifications(unreadCount > 0);
|
|
11416
14691
|
}, [unreadCount]);
|
|
11417
|
-
|
|
14692
|
+
useEffect50(() => {
|
|
11418
14693
|
if (lastLoaded === 0) loadNotifications();
|
|
11419
14694
|
}, [lastLoaded, loadNotifications]);
|
|
11420
14695
|
const processSocketNotificationsRef = useRef16(null);
|
|
11421
|
-
const processSocketNotifications =
|
|
14696
|
+
const processSocketNotifications = useCallback19(() => {
|
|
11422
14697
|
if (socketNotifications.length === 0) {
|
|
11423
14698
|
return;
|
|
11424
14699
|
}
|
|
@@ -11457,7 +14732,7 @@ function NotificationModalContent({ isOpen, setIsOpen }) {
|
|
|
11457
14732
|
generateUrl,
|
|
11458
14733
|
checkCircuitBreaker
|
|
11459
14734
|
]);
|
|
11460
|
-
|
|
14735
|
+
useEffect50(() => {
|
|
11461
14736
|
if (processSocketNotificationsRef.current) {
|
|
11462
14737
|
clearTimeout(processSocketNotificationsRef.current);
|
|
11463
14738
|
}
|
|
@@ -11494,9 +14769,9 @@ function NotificationModalContent({ isOpen, setIsOpen }) {
|
|
|
11494
14769
|
}
|
|
11495
14770
|
}, "handleOpenChange");
|
|
11496
14771
|
const unreadNotifications = newNotifications && unreadCount > 0;
|
|
11497
|
-
return /* @__PURE__ */
|
|
11498
|
-
/* @__PURE__ */
|
|
11499
|
-
/* @__PURE__ */
|
|
14772
|
+
return /* @__PURE__ */ jsxs120(Popover, { open: isOpen, onOpenChange: handleOpenChange, "data-testid": `sidebar-notification button`, children: [
|
|
14773
|
+
/* @__PURE__ */ jsx185(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs120(SidebarMenuButton, { className: "text-muted-foreground h-6", disabled: isLoading, children: [
|
|
14774
|
+
/* @__PURE__ */ jsx185(
|
|
11500
14775
|
BellIcon,
|
|
11501
14776
|
{
|
|
11502
14777
|
className: `h-5 w-5 cursor-pointer ${unreadNotifications ? "text-destructive" : ""} ${isLoading ? "animate-pulse" : ""}`
|
|
@@ -11504,75 +14779,75 @@ function NotificationModalContent({ isOpen, setIsOpen }) {
|
|
|
11504
14779
|
),
|
|
11505
14780
|
t(`types.notifications`, { count: 2 })
|
|
11506
14781
|
] }) }),
|
|
11507
|
-
/* @__PURE__ */
|
|
11508
|
-
/* @__PURE__ */
|
|
11509
|
-
/* @__PURE__ */
|
|
11510
|
-
isLoading && /* @__PURE__ */
|
|
11511
|
-
error && /* @__PURE__ */
|
|
14782
|
+
/* @__PURE__ */ jsx185(PopoverContent, { className: "relative left-10 w-80 border-0 p-0 shadow-none", children: /* @__PURE__ */ jsxs120(Card, { children: [
|
|
14783
|
+
/* @__PURE__ */ jsxs120(CardHeader, { className: "p-4", children: [
|
|
14784
|
+
/* @__PURE__ */ jsx185(CardTitle, { children: t(`types.notifications`, { count: 2 }) }),
|
|
14785
|
+
isLoading && /* @__PURE__ */ jsx185("div", { className: "text-muted-foreground text-xs", children: "Loading..." }),
|
|
14786
|
+
error && /* @__PURE__ */ jsxs120("div", { className: "text-destructive text-xs", children: [
|
|
11512
14787
|
"Error: ",
|
|
11513
14788
|
error
|
|
11514
14789
|
] })
|
|
11515
14790
|
] }),
|
|
11516
|
-
/* @__PURE__ */
|
|
11517
|
-
/* @__PURE__ */
|
|
14791
|
+
/* @__PURE__ */ jsx185(Separator4, {}),
|
|
14792
|
+
/* @__PURE__ */ jsx185(ScrollArea, { className: "h-96", children: notifications.length > 0 ? notifications.map((notification) => /* @__PURE__ */ jsx185(Fragment36, { children: generateNotification(notification, () => setIsOpen(false)) }, notification.id)) : /* @__PURE__ */ jsx185("div", { className: "p-4 text-center text-sm text-gray-500", children: t(`foundations.notification.empty`, { count: 2 }) }) })
|
|
11518
14793
|
] }) })
|
|
11519
14794
|
] });
|
|
11520
14795
|
}
|
|
11521
14796
|
__name(NotificationModalContent, "NotificationModalContent");
|
|
11522
14797
|
function NotificationModal(props) {
|
|
11523
|
-
return /* @__PURE__ */
|
|
14798
|
+
return /* @__PURE__ */ jsx185(NotificationErrorBoundary, { children: /* @__PURE__ */ jsx185(NotificationModalContent, { ...props }) });
|
|
11524
14799
|
}
|
|
11525
14800
|
__name(NotificationModal, "NotificationModal");
|
|
11526
14801
|
|
|
11527
14802
|
// src/features/notification/components/notifications/PushNotificationProvider.tsx
|
|
11528
|
-
import { Fragment as
|
|
14803
|
+
import { Fragment as Fragment37, jsx as jsx186 } from "react/jsx-runtime";
|
|
11529
14804
|
function PushNotificationProvider({ children }) {
|
|
11530
14805
|
usePushNotifications();
|
|
11531
|
-
return /* @__PURE__ */
|
|
14806
|
+
return /* @__PURE__ */ jsx186(Fragment37, { children });
|
|
11532
14807
|
}
|
|
11533
14808
|
__name(PushNotificationProvider, "PushNotificationProvider");
|
|
11534
14809
|
|
|
11535
14810
|
// src/features/role/components/details/RoleDetails.tsx
|
|
11536
14811
|
import { useTranslations as useTranslations59 } from "next-intl";
|
|
11537
|
-
import { jsx as
|
|
14812
|
+
import { jsx as jsx187 } from "react/jsx-runtime";
|
|
11538
14813
|
function RoleDetails() {
|
|
11539
14814
|
const { role } = useRoleContext();
|
|
11540
14815
|
const t = useTranslations59();
|
|
11541
14816
|
if (!role) return null;
|
|
11542
|
-
return /* @__PURE__ */
|
|
14817
|
+
return /* @__PURE__ */ jsx187(Card, { className: "w-full", children: /* @__PURE__ */ jsx187(CardContent, { className: "p-4", children: /* @__PURE__ */ jsx187(AttributeElement, { title: t(`foundations.role.fields.description.label`), value: role.description }) }) });
|
|
11543
14818
|
}
|
|
11544
14819
|
__name(RoleDetails, "RoleDetails");
|
|
11545
14820
|
|
|
11546
14821
|
// src/features/role/components/containers/RoleContainer.tsx
|
|
11547
|
-
import { Fragment as
|
|
14822
|
+
import { Fragment as Fragment38, jsx as jsx188, jsxs as jsxs121 } from "react/jsx-runtime";
|
|
11548
14823
|
function RoleContainer() {
|
|
11549
14824
|
const { role } = useRoleContext();
|
|
11550
14825
|
if (!role) return null;
|
|
11551
|
-
return /* @__PURE__ */
|
|
11552
|
-
/* @__PURE__ */
|
|
11553
|
-
/* @__PURE__ */
|
|
14826
|
+
return /* @__PURE__ */ jsxs121(Fragment38, { children: [
|
|
14827
|
+
/* @__PURE__ */ jsx188(RoleDetails, {}),
|
|
14828
|
+
/* @__PURE__ */ jsx188(RoleUsersList, { role })
|
|
11554
14829
|
] });
|
|
11555
14830
|
}
|
|
11556
14831
|
__name(RoleContainer, "RoleContainer");
|
|
11557
14832
|
|
|
11558
14833
|
// src/features/role/components/forms/FormRoles.tsx
|
|
11559
14834
|
import { useTranslations as useTranslations60 } from "next-intl";
|
|
11560
|
-
import { jsx as
|
|
14835
|
+
import { jsx as jsx189, jsxs as jsxs122 } from "react/jsx-runtime";
|
|
11561
14836
|
function FormRoles({ form, id, name, roles }) {
|
|
11562
14837
|
const t = useTranslations60();
|
|
11563
14838
|
const { hasAccesToFeature } = useCurrentUserContext();
|
|
11564
|
-
return /* @__PURE__ */
|
|
14839
|
+
return /* @__PURE__ */ jsx189("div", { className: "flex w-full flex-col", children: /* @__PURE__ */ jsx189(
|
|
11565
14840
|
FormField,
|
|
11566
14841
|
{
|
|
11567
14842
|
control: form.control,
|
|
11568
14843
|
name: id,
|
|
11569
|
-
render: ({ field }) => /* @__PURE__ */
|
|
11570
|
-
/* @__PURE__ */
|
|
11571
|
-
/* @__PURE__ */
|
|
14844
|
+
render: ({ field }) => /* @__PURE__ */ jsxs122(FormItem, { className: `${name ? "mb-5" : "mb-1"}`, children: [
|
|
14845
|
+
/* @__PURE__ */ jsx189(FormControl, { children: /* @__PURE__ */ jsxs122("div", { children: [
|
|
14846
|
+
/* @__PURE__ */ jsx189("div", { className: "text-sm font-semibold", children: name }),
|
|
11572
14847
|
roles.filter((role) => role.isSelectable).sort((a, b) => a.name.localeCompare(b.name)).map((role) => {
|
|
11573
14848
|
if (role.requiredFeature && !hasAccesToFeature(role.requiredFeature.id)) return null;
|
|
11574
|
-
return /* @__PURE__ */
|
|
11575
|
-
/* @__PURE__ */
|
|
14849
|
+
return /* @__PURE__ */ jsxs122("div", { children: [
|
|
14850
|
+
/* @__PURE__ */ jsx189(
|
|
11576
14851
|
Checkbox,
|
|
11577
14852
|
{
|
|
11578
14853
|
defaultChecked: field.value.some((roleId) => roleId === role.id),
|
|
@@ -11588,14 +14863,14 @@ function FormRoles({ form, id, name, roles }) {
|
|
|
11588
14863
|
}
|
|
11589
14864
|
}
|
|
11590
14865
|
),
|
|
11591
|
-
/* @__PURE__ */
|
|
11592
|
-
/* @__PURE__ */
|
|
11593
|
-
/* @__PURE__ */
|
|
14866
|
+
/* @__PURE__ */ jsxs122(Tooltip2, { children: [
|
|
14867
|
+
/* @__PURE__ */ jsx189(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx189(FormLabel, { className: "ml-3 font-normal", children: t(`foundations.role.roles`, { role: role.id.replaceAll(`-`, ``) }) }) }),
|
|
14868
|
+
/* @__PURE__ */ jsx189(TooltipContent, { children: t(`foundations.role.roles_descriptions`, { role: role.id.replaceAll(`-`, ``) }) })
|
|
11594
14869
|
] })
|
|
11595
14870
|
] }, role.id);
|
|
11596
14871
|
})
|
|
11597
14872
|
] }) }),
|
|
11598
|
-
/* @__PURE__ */
|
|
14873
|
+
/* @__PURE__ */ jsx189(FormMessage, {})
|
|
11599
14874
|
] })
|
|
11600
14875
|
}
|
|
11601
14876
|
) });
|
|
@@ -11604,13 +14879,13 @@ __name(FormRoles, "FormRoles");
|
|
|
11604
14879
|
|
|
11605
14880
|
// src/features/role/components/forms/RemoveUserFromRole.tsx
|
|
11606
14881
|
import { useTranslations as useTranslations61 } from "next-intl";
|
|
11607
|
-
import { useEffect as
|
|
11608
|
-
import { Fragment as
|
|
14882
|
+
import { useEffect as useEffect51, useState as useState69 } from "react";
|
|
14883
|
+
import { Fragment as Fragment39, jsx as jsx190, jsxs as jsxs123 } from "react/jsx-runtime";
|
|
11609
14884
|
function RemoveUserFromRole({ role, user, refresh }) {
|
|
11610
|
-
const [open, setOpen] =
|
|
11611
|
-
const [canRemove, setCanRemove] =
|
|
14885
|
+
const [open, setOpen] = useState69(false);
|
|
14886
|
+
const [canRemove, setCanRemove] = useState69(false);
|
|
11612
14887
|
const t = useTranslations61();
|
|
11613
|
-
|
|
14888
|
+
useEffect51(() => {
|
|
11614
14889
|
async function checkCompanyAdminDeletability() {
|
|
11615
14890
|
const roleUsers = await UserService.findAllUsersByRole({
|
|
11616
14891
|
roleId: role.id
|
|
@@ -11637,8 +14912,8 @@ function RemoveUserFromRole({ role, user, refresh }) {
|
|
|
11637
14912
|
}
|
|
11638
14913
|
}, "remove");
|
|
11639
14914
|
const roleName = t(`foundations.role.roles`, { role: role.id.replaceAll(`-`, ``) });
|
|
11640
|
-
return /* @__PURE__ */
|
|
11641
|
-
/* @__PURE__ */
|
|
14915
|
+
return /* @__PURE__ */ jsxs123(Dialog, { open, onOpenChange: setOpen, children: [
|
|
14916
|
+
/* @__PURE__ */ jsx190(
|
|
11642
14917
|
DialogTrigger,
|
|
11643
14918
|
{
|
|
11644
14919
|
onClick: (e) => {
|
|
@@ -11646,19 +14921,19 @@ function RemoveUserFromRole({ role, user, refresh }) {
|
|
|
11646
14921
|
e.preventDefault();
|
|
11647
14922
|
setOpen(true);
|
|
11648
14923
|
},
|
|
11649
|
-
children: /* @__PURE__ */
|
|
14924
|
+
children: /* @__PURE__ */ jsx190("span", { className: "hover:text-destructive cursor-pointer", children: t(`foundations.role.remove_user.title`) })
|
|
11650
14925
|
}
|
|
11651
14926
|
),
|
|
11652
|
-
/* @__PURE__ */
|
|
11653
|
-
/* @__PURE__ */
|
|
11654
|
-
/* @__PURE__ */
|
|
11655
|
-
/* @__PURE__ */
|
|
14927
|
+
/* @__PURE__ */ jsxs123(DialogContent, { className: `flex max-h-[70vh] max-w-3xl flex-col overflow-y-auto`, children: [
|
|
14928
|
+
/* @__PURE__ */ jsxs123(DialogHeader, { children: [
|
|
14929
|
+
/* @__PURE__ */ jsx190(DialogTitle, { children: t(`foundations.role.remove_user.title`) }),
|
|
14930
|
+
/* @__PURE__ */ jsx190(DialogDescription, { children: canRemove ? t(`foundations.role.remove_user.subtitle_allowed`) : t(`foundations.role.remove_user.subtitle_not_allowed`) })
|
|
11656
14931
|
] }),
|
|
11657
|
-
canRemove ? /* @__PURE__ */
|
|
14932
|
+
canRemove ? /* @__PURE__ */ jsxs123(Fragment39, { children: [
|
|
11658
14933
|
t(`foundations.role.remove_user.description_allowed`, { role: roleName, user: user.name }),
|
|
11659
|
-
/* @__PURE__ */
|
|
11660
|
-
/* @__PURE__ */
|
|
11661
|
-
/* @__PURE__ */
|
|
14934
|
+
/* @__PURE__ */ jsxs123("div", { className: "flex justify-end", children: [
|
|
14935
|
+
/* @__PURE__ */ jsx190(Button, { className: "mr-2", variant: "outline", type: `button`, onClick: () => setOpen(false), children: t(`generic.buttons.cancel`) }),
|
|
14936
|
+
/* @__PURE__ */ jsx190(
|
|
11662
14937
|
Button,
|
|
11663
14938
|
{
|
|
11664
14939
|
type: "submit",
|
|
@@ -11671,7 +14946,7 @@ function RemoveUserFromRole({ role, user, refresh }) {
|
|
|
11671
14946
|
}
|
|
11672
14947
|
)
|
|
11673
14948
|
] })
|
|
11674
|
-
] }) : /* @__PURE__ */
|
|
14949
|
+
] }) : /* @__PURE__ */ jsx190(Fragment39, { children: t(`foundations.role.remove_user.description_not_allowed`, { role: roleName, user: user.name }) })
|
|
11675
14950
|
] })
|
|
11676
14951
|
] });
|
|
11677
14952
|
}
|
|
@@ -11680,14 +14955,14 @@ __name(RemoveUserFromRole, "RemoveUserFromRole");
|
|
|
11680
14955
|
// src/features/role/components/forms/UserRoleAdd.tsx
|
|
11681
14956
|
import { PlusCircle } from "lucide-react";
|
|
11682
14957
|
import { useTranslations as useTranslations62 } from "next-intl";
|
|
11683
|
-
import { useCallback as
|
|
14958
|
+
import { useCallback as useCallback20, useEffect as useEffect52, useRef as useRef17, useState as useState70 } from "react";
|
|
11684
14959
|
import { toast as toast11 } from "sonner";
|
|
11685
|
-
import { Fragment as
|
|
14960
|
+
import { Fragment as Fragment40, jsx as jsx191, jsxs as jsxs124 } from "react/jsx-runtime";
|
|
11686
14961
|
function UserRoleAdd({ user, refresh }) {
|
|
11687
|
-
const [open, setOpen] =
|
|
14962
|
+
const [open, setOpen] = useState70(false);
|
|
11688
14963
|
const inputRef = useRef17(null);
|
|
11689
|
-
const [searchTerm, setSearchTerm] =
|
|
11690
|
-
const [roles, setRoles] =
|
|
14964
|
+
const [searchTerm, setSearchTerm] = useState70("");
|
|
14965
|
+
const [roles, setRoles] = useState70([]);
|
|
11691
14966
|
const t = useTranslations62();
|
|
11692
14967
|
const addUserToRole = /* @__PURE__ */ __name(async (role) => {
|
|
11693
14968
|
await RoleService.addUserToRole({
|
|
@@ -11711,7 +14986,7 @@ function UserRoleAdd({ user, refresh }) {
|
|
|
11711
14986
|
);
|
|
11712
14987
|
refresh();
|
|
11713
14988
|
}, "addUserToRole");
|
|
11714
|
-
const searchRoles =
|
|
14989
|
+
const searchRoles = useCallback20(
|
|
11715
14990
|
async (term) => {
|
|
11716
14991
|
setRoles(
|
|
11717
14992
|
await RoleService.findAllRolesUserNotIn({
|
|
@@ -11723,32 +14998,32 @@ function UserRoleAdd({ user, refresh }) {
|
|
|
11723
14998
|
[searchTerm, user]
|
|
11724
14999
|
);
|
|
11725
15000
|
const updateSearchTerm = useDebounce(searchRoles, 500);
|
|
11726
|
-
|
|
15001
|
+
useEffect52(() => {
|
|
11727
15002
|
if (open) updateSearchTerm(searchTerm);
|
|
11728
15003
|
}, [open, searchTerm]);
|
|
11729
|
-
|
|
15004
|
+
useEffect52(() => {
|
|
11730
15005
|
if (open) searchRoles("");
|
|
11731
15006
|
}, [open]);
|
|
11732
|
-
return /* @__PURE__ */
|
|
11733
|
-
/* @__PURE__ */
|
|
11734
|
-
/* @__PURE__ */
|
|
15007
|
+
return /* @__PURE__ */ jsxs124(Fragment40, { children: [
|
|
15008
|
+
/* @__PURE__ */ jsxs124(Button, { size: "sm", onClick: () => setOpen(true), children: [
|
|
15009
|
+
/* @__PURE__ */ jsx191(PlusCircle, { className: "mr-3 h-3.5 w-3.5" }),
|
|
11735
15010
|
t(`generic.association.label`, {
|
|
11736
15011
|
source: t(`types.roles`, { count: 1 }),
|
|
11737
15012
|
destination: t(`types.users`, { count: 1 })
|
|
11738
15013
|
})
|
|
11739
15014
|
] }),
|
|
11740
|
-
/* @__PURE__ */
|
|
11741
|
-
/* @__PURE__ */
|
|
15015
|
+
/* @__PURE__ */ jsxs124(CommandDialog, { open, onOpenChange: setOpen, children: [
|
|
15016
|
+
/* @__PURE__ */ jsx191(DialogTitle, { children: t(`generic.association.label`, {
|
|
11742
15017
|
source: t(`types.roles`, { count: 1 }),
|
|
11743
15018
|
destination: t(`types.users`, { count: 1 })
|
|
11744
15019
|
}) }),
|
|
11745
|
-
/* @__PURE__ */
|
|
15020
|
+
/* @__PURE__ */ jsx191(DialogDescription, { children: t(`generic.association.description`, {
|
|
11746
15021
|
source: t(`types.roles`, { count: 1 }),
|
|
11747
15022
|
destination: t(`types.users`, { count: 1 }),
|
|
11748
15023
|
destination_name: user.name
|
|
11749
15024
|
}) }),
|
|
11750
|
-
/* @__PURE__ */
|
|
11751
|
-
/* @__PURE__ */
|
|
15025
|
+
/* @__PURE__ */ jsxs124(Command, { shouldFilter: false, children: [
|
|
15026
|
+
/* @__PURE__ */ jsx191(
|
|
11752
15027
|
CommandInput,
|
|
11753
15028
|
{
|
|
11754
15029
|
placeholder: t(`generic.search.placeholder`, { type: t(`types.roles`, { count: 1 }) }),
|
|
@@ -11757,9 +15032,9 @@ function UserRoleAdd({ user, refresh }) {
|
|
|
11757
15032
|
ref: inputRef
|
|
11758
15033
|
}
|
|
11759
15034
|
),
|
|
11760
|
-
/* @__PURE__ */
|
|
11761
|
-
/* @__PURE__ */
|
|
11762
|
-
roles.map((role) => /* @__PURE__ */
|
|
15035
|
+
/* @__PURE__ */ jsxs124(CommandList, { className: "mt-3 h-auto max-h-96 min-h-96 max-w-full overflow-x-hidden overflow-y-auto", children: [
|
|
15036
|
+
/* @__PURE__ */ jsx191(CommandEmpty, { children: t(`generic.search.no_results`, { type: t(`types.roles`, { count: 1 }) }) }),
|
|
15037
|
+
roles.map((role) => /* @__PURE__ */ jsx191(
|
|
11763
15038
|
CommandItem,
|
|
11764
15039
|
{
|
|
11765
15040
|
className: "cursor-pointer",
|
|
@@ -11778,7 +15053,7 @@ __name(UserRoleAdd, "UserRoleAdd");
|
|
|
11778
15053
|
|
|
11779
15054
|
// src/features/role/components/lists/RolesList.tsx
|
|
11780
15055
|
import { useTranslations as useTranslations63 } from "next-intl";
|
|
11781
|
-
import { jsx as
|
|
15056
|
+
import { jsx as jsx192 } from "react/jsx-runtime";
|
|
11782
15057
|
function RolesList() {
|
|
11783
15058
|
const t = useTranslations63();
|
|
11784
15059
|
const data = useDataListRetriever({
|
|
@@ -11786,7 +15061,7 @@ function RolesList() {
|
|
|
11786
15061
|
retrieverParams: {},
|
|
11787
15062
|
module: Modules.Role
|
|
11788
15063
|
});
|
|
11789
|
-
return /* @__PURE__ */
|
|
15064
|
+
return /* @__PURE__ */ jsx192(
|
|
11790
15065
|
ContentListTable,
|
|
11791
15066
|
{
|
|
11792
15067
|
data,
|
|
@@ -11800,7 +15075,7 @@ __name(RolesList, "RolesList");
|
|
|
11800
15075
|
|
|
11801
15076
|
// src/features/role/components/lists/UserRolesList.tsx
|
|
11802
15077
|
import { useTranslations as useTranslations64 } from "next-intl";
|
|
11803
|
-
import { jsx as
|
|
15078
|
+
import { jsx as jsx193 } from "react/jsx-runtime";
|
|
11804
15079
|
function UserRolesList({ user }) {
|
|
11805
15080
|
const t = useTranslations64();
|
|
11806
15081
|
const data = useDataListRetriever({
|
|
@@ -11808,7 +15083,7 @@ function UserRolesList({ user }) {
|
|
|
11808
15083
|
retrieverParams: { userId: user.id },
|
|
11809
15084
|
module: Modules.Role
|
|
11810
15085
|
});
|
|
11811
|
-
return /* @__PURE__ */
|
|
15086
|
+
return /* @__PURE__ */ jsx193(
|
|
11812
15087
|
ContentListTable,
|
|
11813
15088
|
{
|
|
11814
15089
|
data,
|
|
@@ -11821,9 +15096,6 @@ function UserRolesList({ user }) {
|
|
|
11821
15096
|
__name(UserRolesList, "UserRolesList");
|
|
11822
15097
|
|
|
11823
15098
|
export {
|
|
11824
|
-
JsonApiContext,
|
|
11825
|
-
useJsonApiConfig,
|
|
11826
|
-
useJsonApiConfigOptional,
|
|
11827
15099
|
JsonApiProvider,
|
|
11828
15100
|
useJsonApiGet,
|
|
11829
15101
|
useJsonApiMutation,
|
|
@@ -12146,6 +15418,45 @@ export {
|
|
|
12146
15418
|
Logout,
|
|
12147
15419
|
RefreshUser,
|
|
12148
15420
|
ResetPassword,
|
|
15421
|
+
SubscriptionSummaryCard,
|
|
15422
|
+
PaymentMethodSummaryCard,
|
|
15423
|
+
CustomerInfoCard,
|
|
15424
|
+
InvoicesSummaryCard,
|
|
15425
|
+
BillingUsageSummaryCard,
|
|
15426
|
+
PaymentMethodEditor,
|
|
15427
|
+
PaymentMethodCard,
|
|
15428
|
+
PaymentMethodsList,
|
|
15429
|
+
PaymentMethodsContainer,
|
|
15430
|
+
formatInterval,
|
|
15431
|
+
formatCurrency,
|
|
15432
|
+
formatDate3 as formatDate,
|
|
15433
|
+
InvoiceStatusBadge,
|
|
15434
|
+
InvoiceDetails,
|
|
15435
|
+
InvoicesList,
|
|
15436
|
+
InvoicesContainer,
|
|
15437
|
+
CancelSubscriptionDialog,
|
|
15438
|
+
PricingCard,
|
|
15439
|
+
PricingCardsGrid,
|
|
15440
|
+
ProrationPreview,
|
|
15441
|
+
SubscriptionEditor,
|
|
15442
|
+
SubscriptionStatusBadge,
|
|
15443
|
+
SubscriptionDetails,
|
|
15444
|
+
SubscriptionsList,
|
|
15445
|
+
SubscriptionsContainer,
|
|
15446
|
+
UsageSummaryCard,
|
|
15447
|
+
UsageSummaryCards,
|
|
15448
|
+
UsageContainer,
|
|
15449
|
+
UsageHistoryTable,
|
|
15450
|
+
BillingDetailModal,
|
|
15451
|
+
BillingAlertBanner,
|
|
15452
|
+
BillingDashboardContainer,
|
|
15453
|
+
StripeProvider,
|
|
15454
|
+
isStripeConfigured,
|
|
15455
|
+
PriceEditor,
|
|
15456
|
+
PricesList,
|
|
15457
|
+
ProductEditor,
|
|
15458
|
+
ProductsList,
|
|
15459
|
+
ProductsAdminContainer,
|
|
12149
15460
|
CompanyDetails,
|
|
12150
15461
|
AdminCompanyContainer,
|
|
12151
15462
|
CompanyContainer,
|
|
@@ -12201,4 +15512,4 @@ export {
|
|
|
12201
15512
|
useUserTableStructure,
|
|
12202
15513
|
useContentTableStructure
|
|
12203
15514
|
};
|
|
12204
|
-
//# sourceMappingURL=chunk-
|
|
15515
|
+
//# sourceMappingURL=chunk-SXPXC2TY.mjs.map
|