@lukoweb/apitogo 0.1.91 → 0.1.92

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/cli/cli.js CHANGED
@@ -4878,6 +4878,13 @@ import {
4878
4878
  runnerImport,
4879
4879
  loadEnv as viteLoadEnv
4880
4880
  } from "vite";
4881
+ function resolvePublishGatewayBaseUrl() {
4882
+ const raw = process.env.APITOGO_GATEWAY_URL?.trim();
4883
+ if (!raw) {
4884
+ return void 0;
4885
+ }
4886
+ return raw.replace(/\/+$/, "");
4887
+ }
4881
4888
  function mergeJsonConfigWithRuntimeOverrides(fileConfig, importedConfig) {
4882
4889
  const jsonConfig = buildApitogoConfig({
4883
4890
  ...fileConfig,
@@ -5037,7 +5044,8 @@ async function loadZudokuConfig(configEnv, rootDir) {
5037
5044
  ...loadedConfig.__meta,
5038
5045
  pricingEnabled: isBillingEnabled(jsonConfig),
5039
5046
  authenticationEnabled: isAuthenticationEnabled(manifest),
5040
- previewPlans
5047
+ previewPlans,
5048
+ gatewayBaseUrl: resolvePublishGatewayBaseUrl()
5041
5049
  }
5042
5050
  };
5043
5051
  const transformedConfig = applyAuthenticationHeaderNav(
@@ -12537,7 +12545,7 @@ import {
12537
12545
  // package.json
12538
12546
  var package_default = {
12539
12547
  name: "@lukoweb/apitogo",
12540
- version: "0.1.91",
12548
+ version: "0.1.92",
12541
12549
  type: "module",
12542
12550
  sideEffects: [
12543
12551
  "**/*.css",
@@ -15289,7 +15297,8 @@ var viteConfigPlugin = () => {
15289
15297
  rootDir: loadedConfig.__meta.rootDir,
15290
15298
  pricingEnabled: loadedConfig.__meta.pricingEnabled,
15291
15299
  authenticationEnabled: loadedConfig.__meta.authenticationEnabled,
15292
- previewPlans: loadedConfig.__meta.previewPlans
15300
+ previewPlans: loadedConfig.__meta.previewPlans,
15301
+ gatewayBaseUrl: loadedConfig.__meta.gatewayBaseUrl
15293
15302
  };
15294
15303
  return `
15295
15304
  import rawConfig from "${normalizePath(configPath)}";
@@ -16252,6 +16261,9 @@ import path39 from "node:path";
16252
16261
  import { normalizePath as normalizePath2 } from "vite";
16253
16262
  var getLandingModuleImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(path39.join(moduleDir, "../module-landing/src/index.tsx")) : "@lukoweb/apitogo-module-landing";
16254
16263
  var getUserPanelModuleImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(path39.join(moduleDir, "../module-user-panel/src/index.tsx")) : "@lukoweb/apitogo-module-user-panel";
16264
+ var getBillingPluginImport = (moduleDir, mode) => mode === "internal" ? normalizePath2(
16265
+ path39.join(moduleDir, "../plugin-dev-portal-billing/src/index.ts")
16266
+ ) : "@lukoweb/apitogo-plugin-dev-portal-billing";
16255
16267
  var resolveProjectPageImport = (rootDir, pagePath) => normalizePath2(path39.resolve(rootDir, pagePath));
16256
16268
  var viteModulesPlugin = () => {
16257
16269
  const virtualModuleId4 = "virtual:zudoku-modules-plugin";
@@ -16317,10 +16329,15 @@ var viteModulesPlugin = () => {
16317
16329
  `import LandingPage from "${resolveProjectPageImport(config2.__meta.rootDir, landingPagePath)}";`
16318
16330
  );
16319
16331
  }
16332
+ const useBillingDashboard = !dashboardPagePath && userPanel.plans.enabled;
16320
16333
  if (dashboardPagePath) {
16321
16334
  code.push(
16322
16335
  `import UserPanelDashboardPage from "${resolveProjectPageImport(config2.__meta.rootDir, dashboardPagePath)}";`
16323
16336
  );
16337
+ } else if (useBillingDashboard) {
16338
+ code.push(
16339
+ `import { OverviewDashboardPage as UserPanelDashboardPage } from "${getBillingPluginImport(config2.__meta.moduleDir, config2.__meta.mode)}";`
16340
+ );
16324
16341
  }
16325
16342
  if (profilePagePath) {
16326
16343
  code.push(
@@ -16361,7 +16378,7 @@ var viteModulesPlugin = () => {
16361
16378
  ` ...${JSON.stringify(userPanelConfig)},`,
16362
16379
  ` dashboard: {`,
16363
16380
  ` ...${JSON.stringify(userPanelConfig.dashboard)},`,
16364
- ` component: config.modules?.userPanel?.dashboard?.component${dashboardPagePath ? " ?? UserPanelDashboardPage" : ""},`,
16381
+ ` component: config.modules?.userPanel?.dashboard?.component${dashboardPagePath || useBillingDashboard ? " ?? UserPanelDashboardPage" : ""},`,
16365
16382
  ` },`,
16366
16383
  ` userManagement: {`,
16367
16384
  ` ...${JSON.stringify(userPanelConfig.userManagement)},`,
@@ -18687,6 +18704,9 @@ function patchApitogoConfigOpenApiToFile(content, input3 = "./openapi.json") {
18687
18704
  var ACCOUNT_PLANS_PAGE_PATH = "user-panel/AccountPlansPage.tsx";
18688
18705
  var ACCOUNT_PLANS_PAGE = `export { AccountPlansPage as default } from "@lukoweb/apitogo-plugin-dev-portal-billing";
18689
18706
  `;
18707
+ var DASHBOARD_PAGE_PATH = "user-panel/DashboardPage.tsx";
18708
+ var DASHBOARD_PAGE = `export { OverviewDashboardPage as default } from "@lukoweb/apitogo-plugin-dev-portal-billing";
18709
+ `;
18690
18710
  function patchAccountPlansPageConfig(content) {
18691
18711
  if (/plans:\s*\{[^}]*page:/s.test(content)) {
18692
18712
  return content;
@@ -18699,6 +18719,18 @@ function patchAccountPlansPageConfig(content) {
18699
18719
  }`
18700
18720
  );
18701
18721
  }
18722
+ function patchDashboardPageConfig(content) {
18723
+ if (/dashboard:\s*\{[^}]*page:/s.test(content)) {
18724
+ return content;
18725
+ }
18726
+ return content.replace(
18727
+ /dashboard:\s*\{\s*enabled:\s*true\s*\}/,
18728
+ `dashboard: {
18729
+ enabled: true,
18730
+ page: "./user-panel/DashboardPage.tsx",
18731
+ }`
18732
+ );
18733
+ }
18702
18734
  async function ensureAccountPlansPage(outputDirectory) {
18703
18735
  const pagePath = path53.join(outputDirectory, ACCOUNT_PLANS_PAGE_PATH);
18704
18736
  try {
@@ -18710,6 +18742,17 @@ async function ensureAccountPlansPage(outputDirectory) {
18710
18742
  return ACCOUNT_PLANS_PAGE_PATH;
18711
18743
  }
18712
18744
  }
18745
+ async function ensureDashboardPage(outputDirectory) {
18746
+ const pagePath = path53.join(outputDirectory, DASHBOARD_PAGE_PATH);
18747
+ try {
18748
+ await readFile13(pagePath, "utf8");
18749
+ return void 0;
18750
+ } catch {
18751
+ await mkdir10(path53.dirname(pagePath), { recursive: true });
18752
+ await writeFile12(pagePath, DASHBOARD_PAGE);
18753
+ return DASHBOARD_PAGE_PATH;
18754
+ }
18755
+ }
18713
18756
  function patchApitogoConfig(content, options) {
18714
18757
  let patched = content;
18715
18758
  if (options.title) {
@@ -18773,12 +18816,14 @@ async function patchScaffoldedSite(outputDirectory, options) {
18773
18816
  const configContent = await readFile13(configPath, "utf8");
18774
18817
  await writeFile12(
18775
18818
  configPath,
18776
- patchAccountPlansPageConfig(
18777
- patchApitogoConfig(configContent, {
18778
- title,
18779
- openApiUrl: options.openApiUrl,
18780
- logoUrl: options.branding?.logoUrl
18781
- })
18819
+ patchDashboardPageConfig(
18820
+ patchAccountPlansPageConfig(
18821
+ patchApitogoConfig(configContent, {
18822
+ title,
18823
+ openApiUrl: options.openApiUrl,
18824
+ logoUrl: options.branding?.logoUrl
18825
+ })
18826
+ )
18782
18827
  )
18783
18828
  );
18784
18829
  filesWritten.push("apitogo.config.tsx");
@@ -18786,6 +18831,10 @@ async function patchScaffoldedSite(outputDirectory, options) {
18786
18831
  if (accountPlansPage) {
18787
18832
  filesWritten.push(accountPlansPage);
18788
18833
  }
18834
+ const dashboardPage = await ensureDashboardPage(outputDirectory);
18835
+ if (dashboardPage) {
18836
+ filesWritten.push(dashboardPage);
18837
+ }
18789
18838
  return filesWritten;
18790
18839
  }
18791
18840
  async function scaffoldDevPortalSite(options) {
@@ -19138,7 +19187,10 @@ function createDefaultApitogoJsonConfig(options) {
19138
19187
  },
19139
19188
  userPanel: {
19140
19189
  enabled: true,
19141
- dashboard: { enabled: true },
19190
+ dashboard: {
19191
+ enabled: true,
19192
+ page: "./user-panel/DashboardPage.tsx"
19193
+ },
19142
19194
  userManagement: { enabled: true },
19143
19195
  plans: {
19144
19196
  enabled: true,
@@ -19963,6 +20015,8 @@ async function ensureTsconfigJson(dir) {
19963
20015
  var LANDING_PAGE_SOURCE = 'import {\n ApiShowcaseSection,\n FeaturesSection,\n HeroSection,\n LandingShell,\n PlansPreviewSection,\n PricingCtaSection,\n StepsSection,\n} from "@lukoweb/apitogo-module-landing";\n\nexport default function LandingPage() {\n return (\n <LandingShell\n title="Test against a real API before you build one"\n description="Sample API gives you realistic endpoints, authentication, rate limits, and webhooks \u2014 a fully working backend for prototypes, demos, and QA."\n >\n <HeroSection\n hero={{\n badge: "SANDBOX API",\n title: "Test against a real API before you build one",\n description:\n "Sample API gives you realistic endpoints, authentication, rate limits, and webhooks \u2014 a fully working backend for prototypes, demos, and QA. No setup, no infrastructure.",\n }}\n primaryAction={{ label: "Start for free", href: "/signup" }}\n secondaryAction={{ label: "Read the docs", href: "/introduction" }}\n codeExamples={[\n {\n filename: "Create a key",\n language: "bash",\n code: `$ curl -X POST https://api.sampleapi.dev/v1/keys\n# sk_live_9f2c7a1e...`,\n },\n {\n filename: "Call an endpoint",\n language: "bash",\n code: `$ curl https://api.sampleapi.dev/v1/users/42 \\\\\n -H "Authorization: Bearer sk_live_9f2c..."\n# { "id": 42, "name": "Priya Shah", "plan": "pro" }`,\n },\n ]}\n />\n <FeaturesSection\n section={{\n label: "Everything a real API has",\n title: "Built for testing, not just browsing",\n description:\n "Realistic data, live auth, rate limits, and webhooks \u2014 so you can ship with confidence.",\n }}\n features={[\n {\n title: "Realistic mock data",\n description:\n "Users, orders, products, and posts with relationships and pagination, not lorem ipsum.",\n icon: "boxes",\n },\n {\n title: "Real auth flow",\n description:\n "Every request checks a live API key, so you can test error handling and expiry.",\n icon: "shield-check",\n },\n {\n title: "Rate limiting",\n description:\n "Per-plan request limits and 429 responses, so you can build and test backoff logic.",\n icon: "gauge",\n },\n {\n title: "Webhooks",\n description:\n "Trigger test events and inspect delivered payloads and retries.",\n icon: "webhook",\n },\n {\n title: "OpenAPI & docs",\n description:\n "A full OpenAPI spec and reference docs for every endpoint.",\n icon: "book-open",\n },\n {\n title: "Usage analytics",\n description:\n "Track requests, latency, and errors per key from your dashboard.",\n icon: "activity",\n },\n ]}\n />\n <StepsSection\n steps={[\n {\n step: "01",\n title: "Create an account",\n description:\n "Sign up and land straight in your dashboard \u2014 no approval queue.",\n },\n {\n step: "02",\n title: "Generate a key",\n description:\n "Every key is scoped and rate-limited to your plan from the moment it\'s created.",\n },\n {\n step: "03",\n title: "Call any endpoint",\n description:\n "Users, orders, products, posts, and webhooks \u2014 all documented, all live.",\n },\n ]}\n />\n <ApiShowcaseSection\n apiShowcase={{\n title: "Sample endpoints",\n subtitle:\n "A slice of what\'s available \u2014 see the full reference in the docs.",\n browseAllHref: "/api",\n browseAllLabel: "Browse all APIs",\n layout: "list",\n items: [\n {\n name: "List paginated users",\n path: "/v1/users",\n method: "GET",\n summary: "List paginated users",\n },\n {\n name: "Create a mock order",\n path: "/v1/orders",\n method: "POST",\n summary: "Create a mock order",\n },\n {\n name: "Fetch one product",\n path: "/v1/products/{id}",\n method: "GET",\n summary: "Fetch one product",\n },\n {\n name: "List posts with comments",\n path: "/v1/posts",\n method: "GET",\n summary: "List posts with comments",\n },\n {\n name: "Fire a test webhook event",\n path: "/v1/webhooks/test",\n method: "POST",\n summary: "Fire a test webhook event",\n },\n {\n name: "Delete a mock order",\n path: "/v1/orders/{id}",\n method: "DELETE",\n summary: "Delete a mock order",\n },\n ],\n }}\n />\n <PlansPreviewSection\n plans={[\n {\n name: "Free",\n price: "$0",\n unit: "per month",\n blurb: "1,000 requests/mo, community support.",\n },\n {\n name: "Developer",\n price: "$19",\n unit: "per month",\n blurb: "50,000 requests/mo, webhooks included.",\n featured: true,\n },\n {\n name: "Team",\n price: "$79",\n unit: "per month",\n blurb: "500,000 requests/mo, 5 team seats.",\n },\n {\n name: "Enterprise",\n price: "Custom",\n unit: "talk to us",\n blurb: "Unlimited requests, SSO, SLA.",\n },\n ]}\n />\n <PricingCtaSection\n variant="banner"\n pricingCta={{\n title: "Start testing in minutes",\n description:\n "Free tier includes 1,000 requests a month \u2014 no card required.",\n primaryAction: { label: "Start for free", href: "/signup" },\n }}\n />\n </LandingShell>\n );\n}\n';
19964
20016
  var ACCOUNT_PLANS_PAGE_SOURCE = `export { AccountPlansPage as default } from "@lukoweb/apitogo-plugin-dev-portal-billing";
19965
20017
  `;
20018
+ var DASHBOARD_PAGE_SOURCE = `export { OverviewDashboardPage as default } from "@lukoweb/apitogo-plugin-dev-portal-billing";
20019
+ `;
19966
20020
  var fileExists2 = async (filePath) => {
19967
20021
  try {
19968
20022
  await access7(filePath, constants.F_OK);
@@ -19982,6 +20036,11 @@ async function ensurePortalStubFiles(dir) {
19982
20036
  await mkdir11(path57.join(dir, "user-panel"), { recursive: true });
19983
20037
  await writeFile14(plansPath, ACCOUNT_PLANS_PAGE_SOURCE, "utf8");
19984
20038
  }
20039
+ const dashboardPath = path57.join(dir, "user-panel", "DashboardPage.tsx");
20040
+ if (!await fileExists2(dashboardPath)) {
20041
+ await mkdir11(path57.join(dir, "user-panel"), { recursive: true });
20042
+ await writeFile14(dashboardPath, DASHBOARD_PAGE_SOURCE, "utf8");
20043
+ }
19985
20044
  await ensurePublicBrandingFiles(dir);
19986
20045
  }
19987
20046
  async function ensurePublicBrandingFiles(dir) {
@@ -13,6 +13,7 @@ export type ConfigWithMeta = ZudokuConfig & {
13
13
  pricingEnabled?: boolean;
14
14
  authenticationEnabled?: boolean;
15
15
  previewPlans?: DevPortalPreviewPlan[];
16
+ gatewayBaseUrl?: string;
16
17
  };
17
18
  __resolvedModules: ResolvedModulesConfig;
18
19
  };
@@ -1,4 +1,4 @@
1
- import type { ReactNode } from "react";
1
+ import { type ReactNode } from "react";
2
2
  export declare const LandingLayout: ({ children }: {
3
3
  children?: ReactNode;
4
4
  }) => import("react/jsx-runtime").JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukoweb/apitogo",
3
- "version": "0.1.91",
3
+ "version": "0.1.92",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -45,6 +45,8 @@ export type ConfigWithMeta = ZudokuConfig & {
45
45
  pricingEnabled?: boolean;
46
46
  authenticationEnabled?: boolean;
47
47
  previewPlans?: DevPortalPreviewPlan[];
48
+ /** Live gateway URL baked in at publish/build (`APITOGO_GATEWAY_URL`). */
49
+ gatewayBaseUrl?: string;
48
50
  };
49
51
  __resolvedModules: ResolvedModulesConfig;
50
52
  };
@@ -69,6 +71,15 @@ const zudokuConfigFiles = [
69
71
 
70
72
  const serverConfigOutputFiles = ["apitogo.config.js", "zudoku.config.js"];
71
73
 
74
+ function resolvePublishGatewayBaseUrl(): string | undefined {
75
+ const raw = process.env.APITOGO_GATEWAY_URL?.trim();
76
+ if (!raw) {
77
+ return undefined;
78
+ }
79
+
80
+ return raw.replace(/\/+$/, "");
81
+ }
82
+
72
83
  export function mergeJsonConfigWithRuntimeOverrides(
73
84
  fileConfig: Record<string, unknown>,
74
85
  importedConfig: ZudokuConfig,
@@ -304,6 +315,7 @@ export async function loadZudokuConfig(
304
315
  pricingEnabled: isBillingEnabled(jsonConfig),
305
316
  authenticationEnabled: isAuthenticationEnabled(manifest),
306
317
  previewPlans,
318
+ gatewayBaseUrl: resolvePublishGatewayBaseUrl(),
307
319
  },
308
320
  };
309
321
  const transformedConfig = applyAuthenticationHeaderNav(
@@ -1,5 +1,4 @@
1
1
  import type { LucideIcon } from "lucide-react";
2
- import { Link } from "react-router";
3
2
  import {
4
3
  isHeaderNavGroup,
5
4
  type HeaderNavGroup,
@@ -18,6 +17,7 @@ import {
18
17
  NavigationMenuTrigger,
19
18
  } from "../ui/NavigationMenu.js";
20
19
  import { cn } from "../util/cn.js";
20
+ import { AnchorLink } from "./AnchorLink.js";
21
21
  import { useZudoku } from "./context/ZudokuContext.js";
22
22
  import { shouldShowItem } from "./navigation/utils.js";
23
23
 
@@ -28,7 +28,7 @@ const NavLinkItem = ({ item }: { item: HeaderNavLinkItem }) => {
28
28
  const Icon = item.icon as LucideIcon | undefined;
29
29
  return (
30
30
  <NavigationMenuLink asChild>
31
- <Link
31
+ <AnchorLink
32
32
  to={item.to}
33
33
  target={item.target}
34
34
  rel={item.target === "_blank" ? "noopener noreferrer" : undefined}
@@ -45,7 +45,7 @@ const NavLinkItem = ({ item }: { item: HeaderNavLinkItem }) => {
45
45
  {item.description}
46
46
  </p>
47
47
  )}
48
- </Link>
48
+ </AnchorLink>
49
49
  </NavigationMenuLink>
50
50
  );
51
51
  };
@@ -117,7 +117,7 @@ const HeaderNavItemComponent = ({
117
117
  return (
118
118
  <NavigationMenuItem>
119
119
  <NavigationMenuLink asChild>
120
- <Link
120
+ <AnchorLink
121
121
  to={item.to}
122
122
  target={item.target}
123
123
  rel={item.target === "_blank" ? "noopener noreferrer" : undefined}
@@ -125,7 +125,7 @@ const HeaderNavItemComponent = ({
125
125
  >
126
126
  {Icon && <Icon size={16} />}
127
127
  {item.label}
128
- </Link>
128
+ </AnchorLink>
129
129
  </NavigationMenuLink>
130
130
  </NavigationMenuItem>
131
131
  );
@@ -1,21 +1,27 @@
1
1
  import { TooltipProvider } from "@lukoweb/apitogo/ui/Tooltip.js";
2
- import type { ReactNode } from "react";
3
- import { Suspense } from "react";
2
+ import { type ReactNode, Suspense } from "react";
4
3
  import { Outlet } from "react-router";
4
+ import { useScrollToAnchor } from "../util/useScrollToAnchor.js";
5
+ import { useScrollToTop } from "../util/useScrollToTop.js";
5
6
  import { Footer } from "./Footer.js";
6
7
  import { Header } from "./Header.js";
7
8
  import { Slot } from "./Slot.js";
8
9
 
9
- export const LandingLayout = ({ children }: { children?: ReactNode }) => (
10
- <TooltipProvider>
11
- <Slot.Target name="layout-before-head" />
12
- <Suspense>
13
- <Header />
14
- </Suspense>
15
- <Slot.Target name="layout-after-head" />
16
- <main className="min-h-[calc(100dvh-var(--top-header-height))] w-full">
17
- {children ?? <Outlet />}
18
- </main>
19
- <Footer />
20
- </TooltipProvider>
21
- );
10
+ export const LandingLayout = ({ children }: { children?: ReactNode }) => {
11
+ useScrollToAnchor();
12
+ useScrollToTop();
13
+
14
+ return (
15
+ <TooltipProvider>
16
+ <Slot.Target name="layout-before-head" />
17
+ <Suspense>
18
+ <Header />
19
+ </Suspense>
20
+ <Slot.Target name="layout-after-head" />
21
+ <main className="min-h-[calc(100dvh-var(--top-header-height))] w-full">
22
+ {children ?? <Outlet />}
23
+ </main>
24
+ <Footer />
25
+ </TooltipProvider>
26
+ );
27
+ };
@@ -28,6 +28,7 @@ import {
28
28
  DrawerTitle,
29
29
  DrawerTrigger,
30
30
  } from "../ui/Drawer.js";
31
+ import { AnchorLink } from "./AnchorLink.js";
31
32
  import { useCurrentNavigation, useZudoku } from "./context/ZudokuContext.js";
32
33
  import { PoweredByZudoku } from "./navigation/PoweredByZudoku.js";
33
34
  import { getFirstMatchingPath, shouldShowItem } from "./navigation/utils.js";
@@ -44,7 +45,7 @@ const MobileHeaderNavLink = ({
44
45
  }) => {
45
46
  const Icon = item.icon as LucideIcon | undefined;
46
47
  return (
47
- <Link
48
+ <AnchorLink
48
49
  to={item.to}
49
50
  target={item.target}
50
51
  rel={item.target === "_blank" ? "noopener noreferrer" : undefined}
@@ -53,7 +54,7 @@ const MobileHeaderNavLink = ({
53
54
  >
54
55
  {Icon && <Icon size={16} />}
55
56
  {item.label}
56
- </Link>
57
+ </AnchorLink>
57
58
  );
58
59
  };
59
60
 
@@ -68,7 +69,7 @@ const MobileHeaderNavItem = ({
68
69
  const Icon = item.icon as LucideIcon | undefined;
69
70
  return (
70
71
  <li className="w-full">
71
- <Link
72
+ <AnchorLink
72
73
  to={item.to}
73
74
  target={item.target}
74
75
  rel={item.target === "_blank" ? "noopener noreferrer" : undefined}
@@ -77,7 +78,7 @@ const MobileHeaderNavItem = ({
77
78
  >
78
79
  {Icon && <Icon size={16} />}
79
80
  {item.label}
80
- </Link>
81
+ </AnchorLink>
81
82
  </li>
82
83
  );
83
84
  }
@@ -57,6 +57,11 @@ export const useScrollToAnchor = () => {
57
57
  return;
58
58
  }
59
59
 
60
+ if (scrollToHash(location.hash)) {
61
+ initialScrolled.current = true;
62
+ return;
63
+ }
64
+
60
65
  const observer = new MutationObserver((_, obs) => {
61
66
  if (!scrollToHash(location.hash)) return;
62
67
  initialScrolled.current = true;
@@ -69,6 +69,7 @@ const viteConfigPlugin = (): Plugin => {
69
69
  pricingEnabled: loadedConfig.__meta.pricingEnabled,
70
70
  authenticationEnabled: loadedConfig.__meta.authenticationEnabled,
71
71
  previewPlans: loadedConfig.__meta.previewPlans,
72
+ gatewayBaseUrl: loadedConfig.__meta.gatewayBaseUrl,
72
73
  };
73
74
 
74
75
  return `
@@ -14,6 +14,13 @@ const getUserPanelModuleImport = (moduleDir: string, mode: string) =>
14
14
  ? normalizePath(path.join(moduleDir, "../module-user-panel/src/index.tsx"))
15
15
  : "@lukoweb/apitogo-module-user-panel";
16
16
 
17
+ const getBillingPluginImport = (moduleDir: string, mode: string) =>
18
+ mode === "internal"
19
+ ? normalizePath(
20
+ path.join(moduleDir, "../plugin-dev-portal-billing/src/index.ts"),
21
+ )
22
+ : "@lukoweb/apitogo-plugin-dev-portal-billing";
23
+
17
24
  const resolveProjectPageImport = (rootDir: string, pagePath: string) =>
18
25
  normalizePath(path.resolve(rootDir, pagePath));
19
26
 
@@ -91,10 +98,17 @@ const viteModulesPlugin = (): Plugin => {
91
98
  );
92
99
  }
93
100
 
101
+ const useBillingDashboard =
102
+ !dashboardPagePath && userPanel.plans.enabled;
103
+
94
104
  if (dashboardPagePath) {
95
105
  code.push(
96
106
  `import UserPanelDashboardPage from "${resolveProjectPageImport(config.__meta.rootDir, dashboardPagePath)}";`,
97
107
  );
108
+ } else if (useBillingDashboard) {
109
+ code.push(
110
+ `import { OverviewDashboardPage as UserPanelDashboardPage } from "${getBillingPluginImport(config.__meta.moduleDir, config.__meta.mode)}";`,
111
+ );
98
112
  }
99
113
 
100
114
  if (profilePagePath) {
@@ -146,7 +160,7 @@ const viteModulesPlugin = (): Plugin => {
146
160
  ` ...${JSON.stringify(userPanelConfig)},`,
147
161
  ` dashboard: {`,
148
162
  ` ...${JSON.stringify(userPanelConfig.dashboard)},`,
149
- ` component: config.modules?.userPanel?.dashboard?.component${dashboardPagePath ? " ?? UserPanelDashboardPage" : ""},`,
163
+ ` component: config.modules?.userPanel?.dashboard?.component${dashboardPagePath || useBillingDashboard ? " ?? UserPanelDashboardPage" : ""},`,
150
164
  ` },`,
151
165
  ` userManagement: {`,
152
166
  ` ...${JSON.stringify(userPanelConfig.userManagement)},`,