@carlonicora/nextjs-jsonapi 1.15.0 → 1.16.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/{ApiResponseInterface-B4QdWh-y.d.mts → ApiResponseInterface-BvWIeLkq.d.ts} +2 -1
- package/dist/{ApiResponseInterface-QLDnxLA9.d.ts → ApiResponseInterface-CAbw0sv7.d.mts} +2 -1
- package/dist/{BlockNoteEditor-ITJLAOXC.mjs → BlockNoteEditor-HFX7Z5BQ.mjs} +5 -5
- package/dist/{BlockNoteEditor-FGXYUAWI.js → BlockNoteEditor-MBFDWP7X.js} +15 -15
- package/dist/{BlockNoteEditor-FGXYUAWI.js.map → BlockNoteEditor-MBFDWP7X.js.map} +1 -1
- package/dist/JsonApiRequest-45CLE65I.js +24 -0
- package/dist/{JsonApiRequest-FXZCYIER.js.map → JsonApiRequest-45CLE65I.js.map} +1 -1
- package/dist/{JsonApiRequest-HFWXMKMA.mjs → JsonApiRequest-6IPS3DZJ.mjs} +2 -2
- package/dist/{chunk-C6QXZGL7.js → chunk-2AZLCF6D.js} +1617 -158
- package/dist/chunk-2AZLCF6D.js.map +1 -0
- package/dist/{chunk-WAFOKMKT.mjs → chunk-5RAUCUAA.mjs} +3722 -396
- package/dist/chunk-5RAUCUAA.mjs.map +1 -0
- package/dist/{chunk-TGBXBUWM.mjs → chunk-BCKYJQ3K.mjs} +8 -1
- package/dist/chunk-BCKYJQ3K.mjs.map +1 -0
- package/dist/{chunk-JGVXZS7M.mjs → chunk-BCQSE3EU.mjs} +1588 -129
- package/dist/chunk-BCQSE3EU.mjs.map +1 -0
- package/dist/{chunk-FPZPD4JI.js → chunk-GPGJNTHP.js} +17 -10
- package/dist/chunk-GPGJNTHP.js.map +1 -0
- package/dist/{chunk-PK5DRSUD.js → chunk-ONB2DAIV.js} +4090 -764
- package/dist/chunk-ONB2DAIV.js.map +1 -0
- package/dist/{chunk-SJIVGCNM.mjs → chunk-POKIJ56Q.mjs} +7 -2
- package/dist/chunk-POKIJ56Q.mjs.map +1 -0
- package/dist/{chunk-6YD42BP6.js → chunk-R5QSSISB.js} +14 -9
- package/dist/chunk-R5QSSISB.js.map +1 -0
- package/dist/client/index.d.mts +5 -5
- package/dist/client/index.d.ts +5 -5
- package/dist/client/index.js +7 -5
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +6 -4
- package/dist/components/index.d.mts +253 -9
- package/dist/components/index.d.ts +253 -9
- package/dist/components/index.js +83 -5
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +82 -4
- package/dist/{config-eceYM5kN.d.ts → config-CWsTwnsK.d.mts} +7 -2
- package/dist/{config-C5tGGrYf.d.mts → config-DEaUbBqR.d.ts} +7 -2
- package/dist/{content.interface-TB2MfJGs.d.ts → content.interface-D_4b4RQt.d.ts} +1 -1
- package/dist/{content.interface-CxBBC7ec.d.mts → content.interface-Dk4UZcJM.d.mts} +1 -1
- package/dist/contexts/index.d.mts +2 -2
- package/dist/contexts/index.d.ts +2 -2
- package/dist/contexts/index.js +5 -5
- package/dist/contexts/index.mjs +4 -4
- package/dist/core/index.d.mts +521 -18
- package/dist/core/index.d.ts +521 -18
- package/dist/core/index.js +53 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +52 -2
- package/dist/index.d.mts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +56 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +55 -3
- package/dist/{notification.interface-lG6UpTpt.d.mts → notification.interface-BllkURRm.d.mts} +1 -2
- package/dist/{notification.interface-lG6UpTpt.d.ts → notification.interface-BllkURRm.d.ts} +1 -2
- package/dist/{s3.service-DP_hsssD.d.mts → s3.service-BEfGqho0.d.ts} +20 -2
- package/dist/{s3.service-Dq-PTUNa.d.ts → s3.service-DIQRYe93.d.mts} +20 -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 +4 -4
- package/dist/server/index.d.ts +4 -4
- package/dist/server/index.js +12 -12
- package/dist/server/index.mjs +2 -2
- package/dist/stripe-subscription.interface-C63L6hVg.d.mts +226 -0
- package/dist/stripe-subscription.interface-CUvNDvw5.d.ts +226 -0
- package/dist/{useSocket-Bua6MwLi.d.mts → useSocket-BpenBR2z.d.mts} +1 -1
- package/dist/{useSocket-D5dhUp4m.d.ts → useSocket-D-QYA0Sr.d.ts} +1 -1
- package/package.json +9 -1
- 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/components/index.ts +7 -0
- package/src/core/abstracts/AbstractService.ts +104 -0
- package/src/core/endpoint/EndpointCreator.ts +7 -4
- 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/utils/translateResponse.ts +17 -0
- 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/index.ts +1 -1
- package/src/shadcnui/custom/link.tsx +16 -6
- package/src/utils/blocknote-diff.util.ts +2 -1
- package/src/utils/blocknote-word-diff-renderer.util.ts +8 -7
- package/dist/AuthComponent-hxOPs9o8.d.mts +0 -11
- package/dist/AuthComponent-hxOPs9o8.d.ts +0 -11
- 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/dist/{BlockNoteEditor-ITJLAOXC.mjs.map → BlockNoteEditor-HFX7Z5BQ.mjs.map} +0 -0
- /package/dist/{JsonApiRequest-HFWXMKMA.mjs.map → JsonApiRequest-6IPS3DZJ.mjs.map} +0 -0
|
@@ -38,6 +38,14 @@ import {
|
|
|
38
38
|
generateModuleTemplate,
|
|
39
39
|
generateListPageTemplate,
|
|
40
40
|
generateDetailPageTemplate,
|
|
41
|
+
generateBootstrapperTemplate,
|
|
42
|
+
generateEnvTemplate,
|
|
43
|
+
generateMiddlewareEnvTemplate,
|
|
44
|
+
generateMainLayoutTemplate,
|
|
45
|
+
generateSettingsContextTemplate,
|
|
46
|
+
generateSettingsContainerTemplate,
|
|
47
|
+
generateSettingsPageTemplate,
|
|
48
|
+
generateSettingsModulePageTemplate,
|
|
41
49
|
} from "./templates";
|
|
42
50
|
import { writeFiles, printResults, updateBootstrapper, updateI18n } from "./utils";
|
|
43
51
|
|
|
@@ -323,6 +331,81 @@ function generateAllFiles(data: FrontendTemplateData, schema: JsonModuleDefiniti
|
|
|
323
331
|
type: "page",
|
|
324
332
|
});
|
|
325
333
|
|
|
334
|
+
// Project setup files (only if they don't exist)
|
|
335
|
+
const webBasePath = findWebBasePath("");
|
|
336
|
+
|
|
337
|
+
const bootstrapper = generateBootstrapperTemplate(webBasePath);
|
|
338
|
+
if (bootstrapper) {
|
|
339
|
+
files.push({
|
|
340
|
+
path: path.join(webBasePath, "apps/web/src/config/Bootstrapper.ts"),
|
|
341
|
+
content: bootstrapper,
|
|
342
|
+
type: "project-setup",
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const env = generateEnvTemplate(webBasePath);
|
|
347
|
+
if (env) {
|
|
348
|
+
files.push({
|
|
349
|
+
path: path.join(webBasePath, "apps/web/src/config/env.ts"),
|
|
350
|
+
content: env,
|
|
351
|
+
type: "project-setup",
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const middlewareEnv = generateMiddlewareEnvTemplate(webBasePath);
|
|
356
|
+
if (middlewareEnv) {
|
|
357
|
+
files.push({
|
|
358
|
+
path: path.join(webBasePath, "apps/web/src/config/middleware-env.ts"),
|
|
359
|
+
content: middlewareEnv,
|
|
360
|
+
type: "project-setup",
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const mainLayout = generateMainLayoutTemplate(webBasePath);
|
|
365
|
+
if (mainLayout) {
|
|
366
|
+
files.push({
|
|
367
|
+
path: path.join(webBasePath, "apps/web/src/app/[locale]/(main)/layout.tsx"),
|
|
368
|
+
content: mainLayout,
|
|
369
|
+
type: "project-setup",
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const settingsContext = generateSettingsContextTemplate(webBasePath);
|
|
374
|
+
if (settingsContext) {
|
|
375
|
+
files.push({
|
|
376
|
+
path: path.join(webBasePath, "apps/web/src/features/common/contexts/SettingsContext.tsx"),
|
|
377
|
+
content: settingsContext,
|
|
378
|
+
type: "project-setup",
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const settingsContainer = generateSettingsContainerTemplate(webBasePath);
|
|
383
|
+
if (settingsContainer) {
|
|
384
|
+
files.push({
|
|
385
|
+
path: path.join(webBasePath, "apps/web/src/features/common/components/containers/SettingsContainer.tsx"),
|
|
386
|
+
content: settingsContainer,
|
|
387
|
+
type: "project-setup",
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const settingsPage = generateSettingsPageTemplate(webBasePath);
|
|
392
|
+
if (settingsPage) {
|
|
393
|
+
files.push({
|
|
394
|
+
path: path.join(webBasePath, "apps/web/src/app/[locale]/(main)/(features)/settings/page.tsx"),
|
|
395
|
+
content: settingsPage,
|
|
396
|
+
type: "project-setup",
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const settingsModulePage = generateSettingsModulePageTemplate(webBasePath);
|
|
401
|
+
if (settingsModulePage) {
|
|
402
|
+
files.push({
|
|
403
|
+
path: path.join(webBasePath, "apps/web/src/app/[locale]/(main)/(features)/settings/[module]/page.tsx"),
|
|
404
|
+
content: settingsModulePage,
|
|
405
|
+
type: "project-setup",
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
326
409
|
return files;
|
|
327
410
|
}
|
|
328
411
|
|
|
@@ -32,3 +32,13 @@ export { generateModuleTemplate } from "./module.template";
|
|
|
32
32
|
|
|
33
33
|
// Page templates
|
|
34
34
|
export { generateListPageTemplate, generateDetailPageTemplate } from "./pages";
|
|
35
|
+
|
|
36
|
+
// Project setup templates
|
|
37
|
+
export { generateBootstrapperTemplate } from "./project/bootstrapper.template";
|
|
38
|
+
export { generateEnvTemplate } from "./project/env.template";
|
|
39
|
+
export { generateMiddlewareEnvTemplate } from "./project/middleware-env.template";
|
|
40
|
+
export { generateMainLayoutTemplate } from "./project/main-layout.template";
|
|
41
|
+
export { generateSettingsContextTemplate } from "./project/settings-context.template";
|
|
42
|
+
export { generateSettingsContainerTemplate } from "./project/settings-container.template";
|
|
43
|
+
export { generateSettingsPageTemplate } from "./project/settings-page.template";
|
|
44
|
+
export { generateSettingsModulePageTemplate } from "./project/settings-module-page.template";
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootstrapper Template
|
|
3
|
+
*
|
|
4
|
+
* Generates initial Bootstrapper.ts with all foundation and Stripe modules
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
|
|
10
|
+
export function generateBootstrapperTemplate(webBasePath: string): string | null {
|
|
11
|
+
const outputPath = path.join(webBasePath, "apps/web/src/config/Bootstrapper.ts");
|
|
12
|
+
|
|
13
|
+
if (fs.existsSync(outputPath)) {
|
|
14
|
+
return null; // Skip if exists
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return `import { FeatureIds } from "@/enums/feature.ids";
|
|
18
|
+
import {
|
|
19
|
+
AuthModule,
|
|
20
|
+
AuthorModule,
|
|
21
|
+
BillingModule,
|
|
22
|
+
CompanyModule,
|
|
23
|
+
ContentModule,
|
|
24
|
+
DataClassRegistry,
|
|
25
|
+
FeatureModule,
|
|
26
|
+
FieldSelector,
|
|
27
|
+
ModuleModule,
|
|
28
|
+
ModuleRegistry,
|
|
29
|
+
ModuleWithPermissions,
|
|
30
|
+
NotificationModule,
|
|
31
|
+
PushModule,
|
|
32
|
+
RoleModule,
|
|
33
|
+
S3Module,
|
|
34
|
+
setBootstrapper,
|
|
35
|
+
StripeCustomerModule,
|
|
36
|
+
StripeInvoiceModule,
|
|
37
|
+
StripePaymentMethodModule,
|
|
38
|
+
StripePriceModule,
|
|
39
|
+
StripeProductModule,
|
|
40
|
+
StripeSubscriptionModule,
|
|
41
|
+
StripeUsageModule,
|
|
42
|
+
UserModule,
|
|
43
|
+
} from "@carlonicora/nextjs-jsonapi/core";
|
|
44
|
+
import { LucideIcon } from "lucide-react";
|
|
45
|
+
|
|
46
|
+
const moduleFactory = (params: {
|
|
47
|
+
pageUrl?: string;
|
|
48
|
+
name: string;
|
|
49
|
+
cache?: string;
|
|
50
|
+
model: any;
|
|
51
|
+
feature?: FeatureIds;
|
|
52
|
+
moduleId?: string;
|
|
53
|
+
icon?: LucideIcon;
|
|
54
|
+
inclusions?: Record<string, { types?: string[]; fields?: FieldSelector<any>[] }>;
|
|
55
|
+
}): ModuleWithPermissions => ({
|
|
56
|
+
pageUrl: params.pageUrl,
|
|
57
|
+
name: params.name,
|
|
58
|
+
model: params.model,
|
|
59
|
+
feature: params.feature,
|
|
60
|
+
moduleId: params.moduleId,
|
|
61
|
+
cache: params.cache,
|
|
62
|
+
icon: params.icon,
|
|
63
|
+
inclusions: params.inclusions ?? {},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const allModules = {
|
|
67
|
+
// Foundation modules
|
|
68
|
+
Auth: AuthModule(moduleFactory),
|
|
69
|
+
Company: CompanyModule(moduleFactory),
|
|
70
|
+
Feature: FeatureModule(moduleFactory),
|
|
71
|
+
Module: ModuleModule(moduleFactory),
|
|
72
|
+
Notification: NotificationModule(moduleFactory),
|
|
73
|
+
Push: PushModule(moduleFactory),
|
|
74
|
+
Role: RoleModule(moduleFactory),
|
|
75
|
+
S3: S3Module(moduleFactory),
|
|
76
|
+
User: UserModule(moduleFactory),
|
|
77
|
+
Author: AuthorModule(moduleFactory),
|
|
78
|
+
Content: ContentModule(moduleFactory),
|
|
79
|
+
// Billing modules (Stripe)
|
|
80
|
+
Billing: BillingModule(moduleFactory),
|
|
81
|
+
StripeCustomer: StripeCustomerModule(moduleFactory),
|
|
82
|
+
StripePaymentMethod: StripePaymentMethodModule(moduleFactory),
|
|
83
|
+
StripeSubscription: StripeSubscriptionModule(moduleFactory),
|
|
84
|
+
StripeInvoice: StripeInvoiceModule(moduleFactory),
|
|
85
|
+
StripeProduct: StripeProductModule(moduleFactory),
|
|
86
|
+
StripePrice: StripePriceModule(moduleFactory),
|
|
87
|
+
StripeUsage: StripeUsageModule(moduleFactory),
|
|
88
|
+
} satisfies Record<string, ModuleWithPermissions>;
|
|
89
|
+
|
|
90
|
+
export type AllModuleDefinitions = typeof allModules;
|
|
91
|
+
|
|
92
|
+
let bootstrapped = false;
|
|
93
|
+
|
|
94
|
+
export function bootstrap(): void {
|
|
95
|
+
if (bootstrapped) return;
|
|
96
|
+
|
|
97
|
+
Object.entries(allModules).forEach(([name, module]) => {
|
|
98
|
+
ModuleRegistry.register(name, module);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
DataClassRegistry.bootstrap(allModules);
|
|
102
|
+
|
|
103
|
+
bootstrapped = true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
setBootstrapper(bootstrap);
|
|
107
|
+
`;
|
|
108
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Configuration Template
|
|
3
|
+
*
|
|
4
|
+
* Generates env.ts with JSON:API and Stripe configuration
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
|
|
10
|
+
export function generateEnvTemplate(webBasePath: string): string | null {
|
|
11
|
+
const outputPath = path.join(webBasePath, "apps/web/src/config/env.ts");
|
|
12
|
+
|
|
13
|
+
if (fs.existsSync(outputPath)) {
|
|
14
|
+
return null; // Skip if exists
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return `import { bootstrap } from "@/config/Bootstrapper";
|
|
18
|
+
import { ENV } from "@/config/middleware-env";
|
|
19
|
+
import { Link, usePathname, useRouter } from "@/i18n/routing";
|
|
20
|
+
import { useDateFnsLocale } from "@/i18n/useDateFnsLocale";
|
|
21
|
+
import { removeToken, updateToken } from "@/server-actions/auth-cookies";
|
|
22
|
+
import {
|
|
23
|
+
configureAuth,
|
|
24
|
+
configureI18n,
|
|
25
|
+
configureJsonApi,
|
|
26
|
+
configureRoles,
|
|
27
|
+
} from "@carlonicora/nextjs-jsonapi";
|
|
28
|
+
import { Modules } from "@carlonicora/nextjs-jsonapi/core";
|
|
29
|
+
import { RoleId } from "@only35/shared";
|
|
30
|
+
import { useLocale, useTranslations } from "next-intl";
|
|
31
|
+
|
|
32
|
+
// Re-export ENV for use by non-middleware code
|
|
33
|
+
export { ENV };
|
|
34
|
+
|
|
35
|
+
// Bootstrap modules immediately when this file is imported
|
|
36
|
+
bootstrap();
|
|
37
|
+
|
|
38
|
+
// Validate required env vars
|
|
39
|
+
if (!ENV.API_URL) {
|
|
40
|
+
throw new Error("NEXT_PUBLIC_API_URL is required but not set");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!ENV.APP_URL) {
|
|
44
|
+
throw new Error("NEXT_PUBLIC_ADDRESS is required but not set");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Configure JSON:API client
|
|
48
|
+
configureJsonApi({
|
|
49
|
+
apiUrl: ENV.API_URL,
|
|
50
|
+
appUrl: ENV.APP_URL,
|
|
51
|
+
trackablePages: [Modules.Notification, Modules.Company, Modules.User],
|
|
52
|
+
bootstrapper: bootstrap,
|
|
53
|
+
stripePublishableKey: ENV.STRIPE_PUBLISHABLE_KEY,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Configure auth token handling
|
|
57
|
+
configureAuth({
|
|
58
|
+
updateToken,
|
|
59
|
+
removeToken,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Configure i18n
|
|
63
|
+
configureI18n({
|
|
64
|
+
useRouter,
|
|
65
|
+
useTranslations,
|
|
66
|
+
useLocale,
|
|
67
|
+
useDateFnsLocale,
|
|
68
|
+
Link,
|
|
69
|
+
usePathname,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Configure role IDs
|
|
73
|
+
configureRoles(RoleId);
|
|
74
|
+
|
|
75
|
+
// Configure Discord if needed: configureDiscord({ useDiscord: true, useInternalAuth: false });
|
|
76
|
+
`;
|
|
77
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main Layout Template
|
|
3
|
+
*
|
|
4
|
+
* Generates main layout with all providers including Stripe
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
|
|
10
|
+
export function generateMainLayoutTemplate(webBasePath: string): string | null {
|
|
11
|
+
const outputPath = path.join(webBasePath, "apps/web/src/app/[locale]/(main)/layout.tsx");
|
|
12
|
+
|
|
13
|
+
if (fs.existsSync(outputPath)) {
|
|
14
|
+
return null; // Skip if exists
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return `import { ServerSession } from "@carlonicora/nextjs-jsonapi/server";
|
|
18
|
+
import "react-horizontal-scrolling-menu/dist/styles.css";
|
|
19
|
+
|
|
20
|
+
import LayoutDetails from "@/features/common/components/details/LayoutDetails";
|
|
21
|
+
import { routing } from "@/i18n/routing";
|
|
22
|
+
import { PushNotificationProvider, RefreshUser, SidebarProvider, StripeProvider } from "@carlonicora/nextjs-jsonapi/components";
|
|
23
|
+
import { NotificationContextProvider, SocketProvider } from "@carlonicora/nextjs-jsonapi/contexts";
|
|
24
|
+
import { hasLocale } from "next-intl";
|
|
25
|
+
import { getMessages, setRequestLocale } from "next-intl/server";
|
|
26
|
+
import { Inter } from "next/font/google";
|
|
27
|
+
import { cookies } from "next/headers";
|
|
28
|
+
import { notFound } from "next/navigation";
|
|
29
|
+
import "../../globals.css";
|
|
30
|
+
|
|
31
|
+
const fontSans = Inter({ subsets: ["latin"], weight: ["100", "300", "400", "700"], variable: "--font-sans" });
|
|
32
|
+
|
|
33
|
+
export default async function MainLayout(props: { children: React.ReactNode; params: Promise<{ locale: string }> }) {
|
|
34
|
+
const params = await props.params;
|
|
35
|
+
|
|
36
|
+
const { locale } = params;
|
|
37
|
+
const { children } = props;
|
|
38
|
+
|
|
39
|
+
if (!hasLocale(routing.locales, locale)) {
|
|
40
|
+
notFound();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const cookieStore = await cookies();
|
|
44
|
+
const token = cookieStore.get("token")?.value;
|
|
45
|
+
setRequestLocale(locale);
|
|
46
|
+
const messages = await getMessages();
|
|
47
|
+
const defaultOpen = cookieStore.get("sidebar_state")?.value === "true";
|
|
48
|
+
|
|
49
|
+
if (await ServerSession.isLogged())
|
|
50
|
+
return (
|
|
51
|
+
<StripeProvider>
|
|
52
|
+
<SocketProvider token={token}>
|
|
53
|
+
<PushNotificationProvider>
|
|
54
|
+
<NotificationContextProvider>
|
|
55
|
+
<SidebarProvider defaultOpen={defaultOpen}>
|
|
56
|
+
<RefreshUser />
|
|
57
|
+
<LayoutDetails>{children}</LayoutDetails>
|
|
58
|
+
</SidebarProvider>
|
|
59
|
+
</NotificationContextProvider>
|
|
60
|
+
</PushNotificationProvider>
|
|
61
|
+
</SocketProvider>
|
|
62
|
+
</StripeProvider>
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
return <div className="flex min-h-screen w-full flex-col items-center justify-center">{children}</div>;
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
68
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware Environment Template
|
|
3
|
+
*
|
|
4
|
+
* Generates middleware-safe environment configuration
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
|
|
10
|
+
export function generateMiddlewareEnvTemplate(webBasePath: string): string | null {
|
|
11
|
+
const outputPath = path.join(webBasePath, "apps/web/src/config/middleware-env.ts");
|
|
12
|
+
|
|
13
|
+
if (fs.existsSync(outputPath)) {
|
|
14
|
+
return null; // Skip if exists
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return `/**
|
|
18
|
+
* Middleware-safe environment configuration
|
|
19
|
+
* This file ONLY exports ENV constants without any library imports
|
|
20
|
+
* It's safe to use in Next.js middleware which has restricted module support
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
export const ENV = {
|
|
24
|
+
API_URL:
|
|
25
|
+
(typeof window === "undefined" ? process.env.API_INTERNAL_URL : undefined) || process.env.NEXT_PUBLIC_API_URL!,
|
|
26
|
+
APP_URL: process.env.NEXT_PUBLIC_ADDRESS
|
|
27
|
+
? process.env.NEXT_PUBLIC_ADDRESS.trim().replace(/\\/+$/, "") // Trim whitespace & remove trailing slashes
|
|
28
|
+
: "",
|
|
29
|
+
VAPID_PUBLIC_KEY: process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY!,
|
|
30
|
+
STRIPE_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
|
|
31
|
+
} as const;
|
|
32
|
+
`;
|
|
33
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings Container Template
|
|
3
|
+
*
|
|
4
|
+
* Generates SettingsContainer with company and billing sections (including Stripe)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
|
|
10
|
+
export function generateSettingsContainerTemplate(webBasePath: string): string | null {
|
|
11
|
+
const outputPath = path.join(webBasePath, "apps/web/src/features/common/components/containers/SettingsContainer.tsx");
|
|
12
|
+
|
|
13
|
+
if (fs.existsSync(outputPath)) {
|
|
14
|
+
return null; // Skip if exists
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return `"use client";
|
|
18
|
+
|
|
19
|
+
import { useSettingsContext } from "@/features/common/contexts/SettingsContext";
|
|
20
|
+
import { cn } from "@/utils/cn";
|
|
21
|
+
import { Action, ModuleWithPermissions, Modules, getRoleId } from "@carlonicora/nextjs-jsonapi";
|
|
22
|
+
import { usePageUrlGenerator } from "@carlonicora/nextjs-jsonapi/client";
|
|
23
|
+
import {
|
|
24
|
+
CompanyContainer,
|
|
25
|
+
ContentTitle,
|
|
26
|
+
InvoicesContainer,
|
|
27
|
+
PaymentMethodsContainer,
|
|
28
|
+
ProductsAdminContainer,
|
|
29
|
+
SubscriptionsContainer,
|
|
30
|
+
UsageContainer,
|
|
31
|
+
UsersListContainer,
|
|
32
|
+
isStripeConfigured,
|
|
33
|
+
} from "@carlonicora/nextjs-jsonapi/components";
|
|
34
|
+
import { useCurrentUserContext } from "@carlonicora/nextjs-jsonapi/contexts";
|
|
35
|
+
|
|
36
|
+
import { Activity, Building2Icon, CreditCard, LucideIcon, Package, Receipt, UsersIcon, Wallet } from "lucide-react";
|
|
37
|
+
import { useLocale, useTranslations } from "next-intl";
|
|
38
|
+
import { ReactNode, useEffect, useState } from "react";
|
|
39
|
+
|
|
40
|
+
type SidebarItem = {
|
|
41
|
+
id: string;
|
|
42
|
+
icon: LucideIcon;
|
|
43
|
+
label?: string;
|
|
44
|
+
container: ReactNode;
|
|
45
|
+
module: ModuleWithPermissions;
|
|
46
|
+
singleItem?: boolean;
|
|
47
|
+
requiredPermission?: Action;
|
|
48
|
+
requiredRole?: string;
|
|
49
|
+
hidden?: boolean;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export default function SettingsContainer() {
|
|
53
|
+
const { module, setModule } = useSettingsContext();
|
|
54
|
+
const t = useTranslations();
|
|
55
|
+
const locale = useLocale();
|
|
56
|
+
const { hasPermissionToModule, hasRole } = useCurrentUserContext();
|
|
57
|
+
const [selectedComponent, setSelectedComponent] = useState<SidebarItem | null>(null);
|
|
58
|
+
const [subscriptions, setSubscriptions] = useState<any[]>([]);
|
|
59
|
+
const generateUrl = usePageUrlGenerator();
|
|
60
|
+
|
|
61
|
+
// Helper function to check if company has metered subscriptions
|
|
62
|
+
const hasMeteredSubscriptions = (): boolean => {
|
|
63
|
+
return subscriptions.some((sub) => sub.price?.recurring?.usageType === "metered");
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Build sidebars array - only include billing if Stripe is configured
|
|
67
|
+
const billingSection = isStripeConfigured()
|
|
68
|
+
? !hasRole(getRoleId().Administrator)
|
|
69
|
+
? {
|
|
70
|
+
name: "billing",
|
|
71
|
+
label: t("billing.title"),
|
|
72
|
+
items: [
|
|
73
|
+
{
|
|
74
|
+
id: "billing-subscriptions",
|
|
75
|
+
icon: CreditCard,
|
|
76
|
+
label: t("billing.subscriptions.title"),
|
|
77
|
+
container: <SubscriptionsContainer />,
|
|
78
|
+
module: Modules.Billing,
|
|
79
|
+
requiredPermission: Action.Read,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: "billing-payment-methods",
|
|
83
|
+
icon: Wallet,
|
|
84
|
+
label: t("billing.payment_methods.title"),
|
|
85
|
+
container: <PaymentMethodsContainer />,
|
|
86
|
+
module: Modules.Billing,
|
|
87
|
+
requiredPermission: Action.Read,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: "billing-invoices",
|
|
91
|
+
icon: Receipt,
|
|
92
|
+
label: t("billing.invoices.title"),
|
|
93
|
+
container: <InvoicesContainer />,
|
|
94
|
+
module: Modules.Billing,
|
|
95
|
+
requiredPermission: Action.Read,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: "billing-usage",
|
|
99
|
+
icon: Activity,
|
|
100
|
+
label: t("billing.usage.title"),
|
|
101
|
+
container: <UsageContainer />,
|
|
102
|
+
module: Modules.Billing,
|
|
103
|
+
requiredPermission: Action.Read,
|
|
104
|
+
hidden: !hasMeteredSubscriptions(),
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
}
|
|
108
|
+
: {
|
|
109
|
+
name: \`billing\`,
|
|
110
|
+
label: t("billing.title"),
|
|
111
|
+
items: [
|
|
112
|
+
{
|
|
113
|
+
id: "billing-admin-products",
|
|
114
|
+
icon: Package,
|
|
115
|
+
label: t("billing.admin.products.title"),
|
|
116
|
+
container: <ProductsAdminContainer />,
|
|
117
|
+
module: Modules.Billing,
|
|
118
|
+
requiredRole: getRoleId().Administrator,
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
}
|
|
122
|
+
: null;
|
|
123
|
+
|
|
124
|
+
const companySection = !hasRole(getRoleId().Administrator)
|
|
125
|
+
? {
|
|
126
|
+
name: \`company\`,
|
|
127
|
+
items: [
|
|
128
|
+
{
|
|
129
|
+
id: "company",
|
|
130
|
+
icon: Building2Icon,
|
|
131
|
+
container: <CompanyContainer />,
|
|
132
|
+
module: Modules.Company,
|
|
133
|
+
singleItem: true,
|
|
134
|
+
},
|
|
135
|
+
{ id: "user", icon: UsersIcon, container: <UsersListContainer />, module: Modules.User },
|
|
136
|
+
],
|
|
137
|
+
}
|
|
138
|
+
: null;
|
|
139
|
+
|
|
140
|
+
const sidebars: {
|
|
141
|
+
name: string;
|
|
142
|
+
label?: string;
|
|
143
|
+
items: SidebarItem[];
|
|
144
|
+
}[] = [...(companySection ? [companySection] : []), ...(billingSection ? [billingSection] : [])];
|
|
145
|
+
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
if (module) {
|
|
148
|
+
const found = sidebars
|
|
149
|
+
.map((sidebar) => sidebar.items.find((item) => item.module.name === module.name))
|
|
150
|
+
.find((item) => item !== undefined);
|
|
151
|
+
if (found) {
|
|
152
|
+
setSelectedComponent(found);
|
|
153
|
+
} else {
|
|
154
|
+
setSelectedComponent(null);
|
|
155
|
+
setModule(undefined);
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
setSelectedComponent(null);
|
|
159
|
+
}
|
|
160
|
+
}, [module]);
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<div className="flex w-full gap-x-4">
|
|
164
|
+
<div className="sticky top-16 flex h-[calc(100vh-theme(spacing.20))] w-2xl flex-col justify-between border-r pr-4">
|
|
165
|
+
<div className="flex min-h-0 flex-1 overflow-y-auto">
|
|
166
|
+
<div className="flex w-full flex-col">
|
|
167
|
+
<ContentTitle element={t(\`generic.settings\`)} />
|
|
168
|
+
<nav className="space-y-4">
|
|
169
|
+
{sidebars.map((sidebar) => (
|
|
170
|
+
<div key={sidebar.name} className="">
|
|
171
|
+
<h3 className="text-muted-foreground mb-2 text-lg font-light">
|
|
172
|
+
{sidebar.label || t(\`generic.settings_sidebar\`, { item: sidebar.name })}
|
|
173
|
+
</h3>
|
|
174
|
+
{sidebar.items.map((item) => {
|
|
175
|
+
// Check if item is hidden
|
|
176
|
+
if (item.hidden) return null;
|
|
177
|
+
|
|
178
|
+
// Check permission-based access
|
|
179
|
+
if (item.requiredPermission) {
|
|
180
|
+
if (!hasPermissionToModule({ module: item.module, action: item.requiredPermission })) return null;
|
|
181
|
+
} else if (!hasPermissionToModule({ module: item.module, action: Action.Read })) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Check role-based access
|
|
186
|
+
if (item.requiredRole && !hasRole(item.requiredRole)) return null;
|
|
187
|
+
|
|
188
|
+
const Icon = item.icon;
|
|
189
|
+
return (
|
|
190
|
+
<button
|
|
191
|
+
key={item.id}
|
|
192
|
+
onClick={() => {
|
|
193
|
+
setModule(item.module);
|
|
194
|
+
setSelectedComponent(item);
|
|
195
|
+
window.history.replaceState(
|
|
196
|
+
null,
|
|
197
|
+
"",
|
|
198
|
+
generateUrl({ page: \`/settings\`, id: item.module.name, language: locale }),
|
|
199
|
+
);
|
|
200
|
+
}}
|
|
201
|
+
className={cn(
|
|
202
|
+
"flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors",
|
|
203
|
+
selectedComponent?.id === item.id
|
|
204
|
+
? "bg-secondary text-secondary-foreground"
|
|
205
|
+
: "text-muted-foreground hover:text-foreground hover:bg-secondary/50",
|
|
206
|
+
)}
|
|
207
|
+
>
|
|
208
|
+
<Icon className="h-4 w-4" />
|
|
209
|
+
{item.label || t(\`types.\${item.module.name}\`, { count: item.singleItem ? 1 : 2 })}
|
|
210
|
+
</button>
|
|
211
|
+
);
|
|
212
|
+
})}
|
|
213
|
+
</div>
|
|
214
|
+
))}
|
|
215
|
+
</nav>
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
<div className="flex w-full flex-col gap-y-4 pb-20">{selectedComponent ? selectedComponent.container : null}</div>
|
|
220
|
+
</div>
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
`;
|
|
224
|
+
}
|