@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 +70 -11
- package/dist/declarations/config/loader.d.ts +1 -0
- package/dist/declarations/lib/components/LandingLayout.d.ts +1 -1
- package/package.json +1 -1
- package/src/config/loader.ts +12 -0
- package/src/lib/components/HeaderNavigation.tsx +5 -5
- package/src/lib/components/LandingLayout.tsx +21 -15
- package/src/lib/components/MobileTopNavigation.tsx +5 -4
- package/src/lib/util/useScrollToAnchor.ts +5 -0
- package/src/vite/plugin-config.ts +1 -0
- package/src/vite/plugin-modules.ts +15 -1
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.
|
|
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
|
-
|
|
18777
|
-
|
|
18778
|
-
|
|
18779
|
-
|
|
18780
|
-
|
|
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: {
|
|
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) {
|
package/package.json
CHANGED
package/src/config/loader.ts
CHANGED
|
@@ -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
|
-
<
|
|
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
|
-
</
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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)},`,
|