@lukoweb/apitogo-plugin-dev-portal-billing 0.1.48 → 0.1.49

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.
Files changed (2) hide show
  1. package/dist/index.mjs +139 -18
  2. package/package.json +2 -2
package/dist/index.mjs CHANGED
@@ -12,6 +12,8 @@ import { Alert, AlertAction, AlertDescription } from "@lukoweb/apitogo/ui/Alert"
12
12
  import { Button as Button$1 } from "@lukoweb/apitogo/ui/Button";
13
13
  import { Secret } from "@lukoweb/apitogo/ui/Secret.js";
14
14
  import { Value } from "@lukoweb/apitogo/ui/Value.js";
15
+ import { access, readFile } from "node:fs/promises";
16
+ import path from "node:path";
15
17
 
16
18
  //#region src/utils/dev-portal-api.ts
17
19
  const DEV_PORTAL_PLACEHOLDER_API_URL = "https://dp-example.azurecontainerapps.io";
@@ -713,14 +715,25 @@ const CredentialsPage = () => {
713
715
  });
714
716
  };
715
717
 
718
+ //#endregion
719
+ //#region src/utils/pricing-config.ts
720
+ function isPreviewPricingEnabled() {
721
+ return manifestCatalog?.pricingEnabled === true;
722
+ }
723
+
716
724
  //#endregion
717
725
  //#region src/pages/PricingPage.tsx
718
726
  const PricingPage = () => {
719
727
  const { pricing } = useDevPortalBillingConfig();
720
728
  const auth = useAuth();
721
729
  const isPreview = useDevPortalPreviewMode();
730
+ const pricingEnabled = isPreviewPricingEnabled();
722
731
  const { data: catalog, isLoading, isError } = usePlans();
723
732
  const { data: me } = useMySubscriptions();
733
+ if (!pricingEnabled) return /* @__PURE__ */ jsx(Navigate, {
734
+ to: "/",
735
+ replace: true
736
+ });
724
737
  if (auth.isAuthenticated) return /* @__PURE__ */ jsx(Navigate, {
725
738
  to: "/account/plans",
726
739
  replace: true
@@ -782,9 +795,25 @@ const PricingPage = () => {
782
795
  mode: "public",
783
796
  isSubscribed: activeSkus.has(plan.sku)
784
797
  }, plan.sku))
785
- }) : !isLoading && !isError ? /* @__PURE__ */ jsx("div", {
798
+ }) : !isLoading && !isError ? /* @__PURE__ */ jsxs("div", {
786
799
  className: "mx-auto max-w-lg rounded-xl border border-dashed p-8 text-center text-sm text-muted-foreground",
787
- children: "No plans are available yet. Sync plans from your APItoGo manifest or platform gateway."
800
+ children: [
801
+ "No plans are configured yet. Add plans in",
802
+ " ",
803
+ /* @__PURE__ */ jsx("code", {
804
+ className: "rounded bg-muted px-1.5 py-0.5",
805
+ children: "apitogo.json"
806
+ }),
807
+ " ",
808
+ "and set",
809
+ " ",
810
+ /* @__PURE__ */ jsx("code", {
811
+ className: "rounded bg-muted px-1.5 py-0.5",
812
+ children: "pricing.enabled"
813
+ }),
814
+ " ",
815
+ "to true."
816
+ ]
788
817
  }) : null
789
818
  ]
790
819
  });
@@ -912,14 +941,103 @@ const SubscriptionsPage = () => {
912
941
  });
913
942
  };
914
943
 
944
+ //#endregion
945
+ //#region src/utils/manifest-pricing.ts
946
+ const APITOGO_MANIFEST_BASE_FILENAME = "apitogo.json";
947
+ const APITOGO_MANIFEST_ENV_FILES = {
948
+ development: "apitogo.local.json",
949
+ production: "apitogo.prod.json"
950
+ };
951
+ async function fileExists(filePath) {
952
+ try {
953
+ await access(filePath);
954
+ return true;
955
+ } catch {
956
+ return false;
957
+ }
958
+ }
959
+ async function readManifestFile(filePath) {
960
+ try {
961
+ const raw = await readFile(filePath, "utf8");
962
+ return JSON.parse(raw);
963
+ } catch {
964
+ return null;
965
+ }
966
+ }
967
+ function mergeManifest(base, override) {
968
+ if (!base) return override;
969
+ if (!override) return base;
970
+ return {
971
+ ...base,
972
+ ...override,
973
+ pricing: {
974
+ ...base.pricing,
975
+ ...override.pricing
976
+ }
977
+ };
978
+ }
979
+ async function readEffectiveManifest(rootDir, mode) {
980
+ const resolvedRoot = path.resolve(rootDir);
981
+ const env = mode === "production" ? "production" : "development";
982
+ const basePath = path.join(resolvedRoot, APITOGO_MANIFEST_BASE_FILENAME);
983
+ const overridePath = path.join(resolvedRoot, APITOGO_MANIFEST_ENV_FILES[env]);
984
+ const hasBase = await fileExists(basePath);
985
+ const hasOverride = await fileExists(overridePath);
986
+ if (!hasBase) {
987
+ const legacy = await readManifestFile(path.join(resolvedRoot, APITOGO_MANIFEST_ENV_FILES.development));
988
+ if (legacy) return legacy;
989
+ if (hasOverride) return readManifestFile(overridePath);
990
+ return null;
991
+ }
992
+ const base = await readManifestFile(basePath);
993
+ if (!hasOverride) return base;
994
+ return mergeManifest(base, await readManifestFile(overridePath));
995
+ }
996
+ async function manifestPricingEnabled(rootDir, mode = "development") {
997
+ return (await readEffectiveManifest(rootDir, mode))?.pricing?.enabled === true;
998
+ }
999
+
1000
+ //#endregion
1001
+ //#region src/utils/navigation-plans.ts
1002
+ function withoutPricingNavigation(items) {
1003
+ if (!items?.length) return [];
1004
+ return items.map((item) => {
1005
+ if (typeof item === "string") return item;
1006
+ if (item.type === "link" && item.to === PRICING_PATH) return null;
1007
+ if (item.items?.length) {
1008
+ const filteredItems = withoutPricingNavigation(item.items);
1009
+ return {
1010
+ ...item,
1011
+ items: filteredItems
1012
+ };
1013
+ }
1014
+ return item;
1015
+ }).filter((item) => item !== null);
1016
+ }
1017
+
915
1018
  //#endregion
916
1019
  //#region src/DevPortalBillingPlugin.tsx
1020
+ async function resolvePricingEnabled(config) {
1021
+ const meta = config;
1022
+ const rootDir = meta.__meta?.rootDir;
1023
+ if (rootDir) return manifestPricingEnabled(rootDir, meta.__meta?.mode);
1024
+ return isPreviewPricingEnabled();
1025
+ }
917
1026
  const devPortalBillingPlugin = createPlugin((options = {}) => ({
918
- transformConfig: ({ config, merge }) => merge({ header: { navigation: [...config.header?.navigation ?? [], {
919
- label: "Pricing",
920
- to: PRICING_PATH,
921
- display: "anon"
922
- }] } }),
1027
+ transformConfig: async ({ config, merge }) => {
1028
+ if (!await resolvePricingEnabled(config)) return merge({
1029
+ navigation: withoutPricingNavigation(config.navigation),
1030
+ header: {
1031
+ ...config.header,
1032
+ navigation: withoutPricingNavigation(config.header?.navigation)
1033
+ }
1034
+ });
1035
+ return merge({ header: { navigation: [...config.header?.navigation ?? [], {
1036
+ label: "Pricing",
1037
+ to: PRICING_PATH,
1038
+ display: "anon"
1039
+ }] } });
1040
+ },
923
1041
  getProfileMenuItems: () => [{
924
1042
  label: "My Subscriptions",
925
1043
  path: SUBSCRIPTIONS_PATH,
@@ -933,15 +1051,8 @@ const devPortalBillingPlugin = createPlugin((options = {}) => ({
933
1051
  category: "middle",
934
1052
  weight: 26
935
1053
  }],
936
- getRoutes: () => [{
937
- element: /* @__PURE__ */ jsx(DevPortalBillingWrapper, { options }),
938
- handle: { layout: "none" },
939
- children: [
940
- {
941
- path: PRICING_PATH,
942
- handle: { layout: "default" },
943
- element: /* @__PURE__ */ jsx(PricingPage, {})
944
- },
1054
+ getRoutes: () => {
1055
+ const children = [
945
1056
  {
946
1057
  path: CHECKOUT_PATH,
947
1058
  handle: { layout: "default" },
@@ -957,8 +1068,18 @@ const devPortalBillingPlugin = createPlugin((options = {}) => ({
957
1068
  handle: { layout: "default" },
958
1069
  element: /* @__PURE__ */ jsx(CredentialsPage, {})
959
1070
  }
960
- ]
961
- }],
1071
+ ];
1072
+ if (isPreviewPricingEnabled()) children.unshift({
1073
+ path: PRICING_PATH,
1074
+ handle: { layout: "default" },
1075
+ element: /* @__PURE__ */ jsx(PricingPage, {})
1076
+ });
1077
+ return [{
1078
+ element: /* @__PURE__ */ jsx(DevPortalBillingWrapper, { options }),
1079
+ handle: { layout: "none" },
1080
+ children
1081
+ }];
1082
+ },
962
1083
  getProtectedRoutes: () => [
963
1084
  CHECKOUT_PATH,
964
1085
  SUBSCRIPTIONS_PATH,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukoweb/apitogo-plugin-dev-portal-billing",
3
- "version": "0.1.48",
3
+ "version": "0.1.49",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/zuplo/zudoku",
@@ -46,7 +46,7 @@
46
46
  "react": "19.2.4",
47
47
  "react-dom": "19.2.4",
48
48
  "tsdown": "0.20.3",
49
- "@lukoweb/apitogo": "0.1.48"
49
+ "@lukoweb/apitogo": "0.1.49"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "react": ">=19.2.0",