@lukoweb/apitogo 0.1.53 → 0.1.55
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 +686 -373
- package/dist/declarations/config/apitogo-config-core.d.ts +14 -4
- package/dist/declarations/config/landing-manifest.d.ts +76 -0
- package/dist/declarations/config/local-manifest.d.ts +91 -1
- package/dist/declarations/config/validators/ModulesSchema.d.ts +227 -0
- package/dist/declarations/config/validators/ZudokuConfig.d.ts +84 -2
- package/dist/declarations/lib/authentication/header-nav-filter.d.ts +4 -0
- package/dist/declarations/lib/authentication/providers/dev-portal-auth-pages.d.ts +15 -0
- package/dist/declarations/lib/authentication/providers/dev-portal-constants.d.ts +19 -1
- package/dist/declarations/lib/authentication/providers/dev-portal-utils.d.ts +5 -1
- package/dist/declarations/lib/authentication/providers/dev-portal.d.ts +5 -0
- package/dist/declarations/lib/components/HeaderAuthActions.d.ts +3 -0
- package/dist/declarations/lib/components/SiteTitle.d.ts +6 -0
- package/dist/declarations/lib/components/ThemeSwitch.d.ts +3 -1
- package/dist/declarations/lib/components/TopNavigation.d.ts +1 -1
- package/dist/declarations/lib/core/ZudokuContext.d.ts +1 -0
- package/dist/flat-config.d.ts +61 -1
- package/package.json +1 -1
- package/src/app/main.css +2 -2
- package/src/config/apitogo-config-core.ts +140 -11
- package/src/config/apitogo-config-loader.browser.ts +17 -0
- package/src/config/apitogo-config-loader.node.ts +28 -8
- package/src/config/build-apitogo-config.ts +13 -4
- package/src/config/landing-manifest.ts +11 -0
- package/src/config/landing-openapi-showcase.ts +115 -0
- package/src/config/loader.ts +17 -3
- package/src/config/local-manifest.ts +146 -11
- package/src/config/resolve-modules.ts +22 -1
- package/src/config/validators/ModulesSchema.ts +84 -0
- package/src/config/validators/ZudokuConfig.ts +26 -1
- package/src/lib/authentication/auth-header-nav.ts +5 -15
- package/src/lib/authentication/header-nav-filter.ts +36 -0
- package/src/lib/authentication/providers/dev-portal-auth-pages.tsx +499 -0
- package/src/lib/authentication/providers/dev-portal-constants.ts +15 -1
- package/src/lib/authentication/providers/dev-portal-utils.ts +128 -3
- package/src/lib/authentication/providers/dev-portal.tsx +51 -6
- package/src/lib/components/Header.tsx +49 -39
- package/src/lib/components/HeaderAuthActions.tsx +36 -0
- package/src/lib/components/HeaderNavigation.tsx +11 -9
- package/src/lib/components/MobileTopNavigation.tsx +43 -24
- package/src/lib/components/SiteTitle.tsx +20 -0
- package/src/lib/components/ThemeSwitch.tsx +36 -2
- package/src/lib/components/TopNavigation.tsx +20 -33
- package/src/lib/core/ZudokuContext.ts +1 -0
- package/src/vite/config.ts +21 -0
- package/src/vite/plugin-auth.ts +4 -1
- package/src/vite/plugin-config.ts +39 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import {
|
|
@@ -8,6 +8,10 @@ import {
|
|
|
8
8
|
loadApitogoConfig,
|
|
9
9
|
readPlanCatalogItems,
|
|
10
10
|
} from "./apitogo-config-loader.js";
|
|
11
|
+
import {
|
|
12
|
+
type LandingManifestInput,
|
|
13
|
+
LandingManifestContentSchema,
|
|
14
|
+
} from "./landing-manifest.js";
|
|
11
15
|
|
|
12
16
|
/** @deprecated Legacy manifest filenames — apitogo.config.json is the only supported source. */
|
|
13
17
|
export const APITOGO_MANIFEST_BASE_FILENAME = "apitogo.json";
|
|
@@ -55,8 +59,29 @@ export const DevPortalLocalManifestSchema = z.object({
|
|
|
55
59
|
authentication: z
|
|
56
60
|
.object({
|
|
57
61
|
enabled: z.boolean().optional(),
|
|
58
|
-
type: z.string().optional(),
|
|
59
62
|
apiBaseUrl: z.string().optional(),
|
|
63
|
+
native: z
|
|
64
|
+
.object({
|
|
65
|
+
enabled: z.boolean().optional(),
|
|
66
|
+
})
|
|
67
|
+
.optional(),
|
|
68
|
+
email: z
|
|
69
|
+
.object({
|
|
70
|
+
provider: z.string().optional(),
|
|
71
|
+
from: z.string().optional(),
|
|
72
|
+
})
|
|
73
|
+
.optional(),
|
|
74
|
+
providers: z
|
|
75
|
+
.array(
|
|
76
|
+
z.object({
|
|
77
|
+
id: z.string().min(1),
|
|
78
|
+
displayName: z.string().optional(),
|
|
79
|
+
authority: z.string().optional(),
|
|
80
|
+
clientId: z.string().optional(),
|
|
81
|
+
scopes: z.string().optional(),
|
|
82
|
+
}),
|
|
83
|
+
)
|
|
84
|
+
.optional(),
|
|
60
85
|
})
|
|
61
86
|
.optional(),
|
|
62
87
|
plans: z.array(ManifestPlanInputSchema).optional(),
|
|
@@ -81,12 +106,19 @@ export const DevPortalLocalManifestSchema = z.object({
|
|
|
81
106
|
})
|
|
82
107
|
.optional(),
|
|
83
108
|
projectId: z.string().optional(),
|
|
109
|
+
landing: z
|
|
110
|
+
.object({
|
|
111
|
+
enabled: z.boolean().optional(),
|
|
112
|
+
content: LandingManifestContentSchema,
|
|
113
|
+
})
|
|
114
|
+
.optional(),
|
|
84
115
|
});
|
|
85
116
|
|
|
86
117
|
export type ManifestPlanInput = z.infer<typeof ManifestPlanInputSchema>;
|
|
87
118
|
export type DevPortalLocalManifest = z.infer<
|
|
88
119
|
typeof DevPortalLocalManifestSchema
|
|
89
120
|
>;
|
|
121
|
+
export type { LandingManifestInput };
|
|
90
122
|
|
|
91
123
|
export type DevPortalPreviewPlan = {
|
|
92
124
|
sku: string;
|
|
@@ -116,6 +148,16 @@ export function isAuthenticationEnabled(
|
|
|
116
148
|
return manifest?.authentication?.enabled === true;
|
|
117
149
|
}
|
|
118
150
|
|
|
151
|
+
export function isNativeAuthEnabledInManifest(
|
|
152
|
+
manifest: DevPortalLocalManifest | null | undefined,
|
|
153
|
+
): boolean {
|
|
154
|
+
if (!isAuthenticationEnabled(manifest)) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return manifest?.authentication?.native?.enabled !== false;
|
|
159
|
+
}
|
|
160
|
+
|
|
119
161
|
export function manifestToPreviewCatalog(
|
|
120
162
|
manifest: DevPortalLocalManifest | null | undefined,
|
|
121
163
|
): DevPortalPreviewPlanCatalog {
|
|
@@ -182,6 +224,105 @@ export function mergePlansBySku(
|
|
|
182
224
|
return Array.from(bySku.values());
|
|
183
225
|
}
|
|
184
226
|
|
|
227
|
+
export function mergeLandingContent(
|
|
228
|
+
existing: LandingManifestInput["content"] | undefined,
|
|
229
|
+
patch: LandingManifestInput["content"] | undefined,
|
|
230
|
+
): LandingManifestInput["content"] | undefined {
|
|
231
|
+
if (!patch) {
|
|
232
|
+
return existing;
|
|
233
|
+
}
|
|
234
|
+
if (!existing) {
|
|
235
|
+
return patch;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const merged = { ...existing, ...patch };
|
|
239
|
+
|
|
240
|
+
if (patch.hero) {
|
|
241
|
+
merged.hero = { ...existing.hero, ...patch.hero };
|
|
242
|
+
if (patch.hero.codeExample) {
|
|
243
|
+
merged.hero = {
|
|
244
|
+
...merged.hero,
|
|
245
|
+
codeExample: {
|
|
246
|
+
...existing.hero?.codeExample,
|
|
247
|
+
...patch.hero.codeExample,
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (patch.features) {
|
|
254
|
+
const byTitle = new Map(
|
|
255
|
+
(existing.features ?? []).map((feature) => [feature.title, feature]),
|
|
256
|
+
);
|
|
257
|
+
for (const feature of patch.features) {
|
|
258
|
+
const prior = byTitle.get(feature.title);
|
|
259
|
+
byTitle.set(feature.title, prior ? { ...prior, ...feature } : feature);
|
|
260
|
+
}
|
|
261
|
+
merged.features = Array.from(byTitle.values());
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (patch.ticker) {
|
|
265
|
+
merged.ticker = { ...existing.ticker, ...patch.ticker };
|
|
266
|
+
if (patch.ticker.items) {
|
|
267
|
+
merged.ticker.items = patch.ticker.items;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (patch.trust) {
|
|
272
|
+
merged.trust = { ...existing.trust, ...patch.trust };
|
|
273
|
+
if (patch.trust.logos) {
|
|
274
|
+
merged.trust.logos = patch.trust.logos;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (patch.apiShowcase) {
|
|
279
|
+
merged.apiShowcase = { ...existing.apiShowcase, ...patch.apiShowcase };
|
|
280
|
+
if (patch.apiShowcase.items) {
|
|
281
|
+
const byPath = new Map(
|
|
282
|
+
(existing.apiShowcase?.items ?? []).map((item) => [item.path, item]),
|
|
283
|
+
);
|
|
284
|
+
for (const item of patch.apiShowcase.items) {
|
|
285
|
+
const prior = byPath.get(item.path);
|
|
286
|
+
byPath.set(item.path, prior ? { ...prior, ...item } : item);
|
|
287
|
+
}
|
|
288
|
+
merged.apiShowcase.items = Array.from(byPath.values());
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (patch.pricingCta) {
|
|
293
|
+
merged.pricingCta = { ...existing.pricingCta, ...patch.pricingCta };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (patch.primaryAction) {
|
|
297
|
+
merged.primaryAction = { ...existing.primaryAction, ...patch.primaryAction };
|
|
298
|
+
}
|
|
299
|
+
if (patch.secondaryAction) {
|
|
300
|
+
merged.secondaryAction = {
|
|
301
|
+
...existing.secondaryAction,
|
|
302
|
+
...patch.secondaryAction,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return merged;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function mergeLandingManifest(
|
|
310
|
+
existing: LandingManifestInput | undefined,
|
|
311
|
+
patch: LandingManifestInput | undefined,
|
|
312
|
+
): LandingManifestInput | undefined {
|
|
313
|
+
if (!patch) {
|
|
314
|
+
return existing;
|
|
315
|
+
}
|
|
316
|
+
if (!existing) {
|
|
317
|
+
return patch;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
enabled: patch.enabled ?? existing.enabled,
|
|
322
|
+
content: mergeLandingContent(existing.content, patch.content),
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
185
326
|
export function mergeManifest(
|
|
186
327
|
base: DevPortalLocalManifest,
|
|
187
328
|
override: DevPortalLocalManifest,
|
|
@@ -209,6 +350,9 @@ export function mergeManifest(
|
|
|
209
350
|
if (override.plans) {
|
|
210
351
|
merged.plans = mergePlansBySku(base.plans, override.plans);
|
|
211
352
|
}
|
|
353
|
+
if (override.landing) {
|
|
354
|
+
merged.landing = mergeLandingManifest(base.landing, override.landing);
|
|
355
|
+
}
|
|
212
356
|
|
|
213
357
|
return merged;
|
|
214
358
|
}
|
|
@@ -246,15 +390,6 @@ export function parseLocalManifestJson(
|
|
|
246
390
|
}
|
|
247
391
|
}
|
|
248
392
|
|
|
249
|
-
async function fileExists(filePath: string): Promise<boolean> {
|
|
250
|
-
try {
|
|
251
|
-
await access(filePath);
|
|
252
|
-
return true;
|
|
253
|
-
} catch {
|
|
254
|
-
return false;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
393
|
export async function readManifestFile(
|
|
259
394
|
rootDir: string,
|
|
260
395
|
filename: string,
|
|
@@ -57,7 +57,7 @@ export const resolveLandingContent = (
|
|
|
57
57
|
const siteTitle =
|
|
58
58
|
config.metadata?.applicationName ?? config.site?.title ?? "APIToGo";
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
const resolved: ResolvedLandingContent = {
|
|
61
61
|
hero: {
|
|
62
62
|
title: content?.hero?.title ?? siteTitle,
|
|
63
63
|
subtitle: content?.hero?.subtitle ?? "Developer portal",
|
|
@@ -65,6 +65,9 @@ export const resolveLandingContent = (
|
|
|
65
65
|
content?.hero?.description ??
|
|
66
66
|
config.metadata?.description ??
|
|
67
67
|
"Ship beautiful API documentation and developer experiences with APIToGo.",
|
|
68
|
+
badge: content?.hero?.badge,
|
|
69
|
+
highlights: content?.hero?.highlights,
|
|
70
|
+
codeExample: content?.hero?.codeExample,
|
|
68
71
|
},
|
|
69
72
|
features: content?.features ?? DEFAULT_LANDING_FEATURES,
|
|
70
73
|
primaryAction: content?.primaryAction ?? {
|
|
@@ -77,6 +80,24 @@ export const resolveLandingContent = (
|
|
|
77
80
|
},
|
|
78
81
|
showPoweredBy: content?.showPoweredBy,
|
|
79
82
|
};
|
|
83
|
+
|
|
84
|
+
if (content?.ticker && content.ticker.enabled !== false) {
|
|
85
|
+
resolved.ticker = content.ticker;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (content?.trust && content.trust.enabled !== false) {
|
|
89
|
+
resolved.trust = content.trust;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (content?.apiShowcase && content.apiShowcase.enabled !== false) {
|
|
93
|
+
resolved.apiShowcase = content.apiShowcase;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (content?.pricingCta && content.pricingCta.enabled !== false) {
|
|
97
|
+
resolved.pricingCta = content.pricingCta;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return resolved;
|
|
80
101
|
};
|
|
81
102
|
|
|
82
103
|
const DEFAULT_DASHBOARD_QUICK_LINKS: ResolvedDashboardContent["quickLinks"] = [
|
|
@@ -44,13 +44,76 @@ export const LandingLinkSchema = z.object({
|
|
|
44
44
|
export const LandingFeatureSchema = z.object({
|
|
45
45
|
title: z.string(),
|
|
46
46
|
description: z.string(),
|
|
47
|
+
icon: z.string().optional(),
|
|
47
48
|
});
|
|
48
49
|
|
|
50
|
+
export const LandingCodeExampleSchema = z
|
|
51
|
+
.object({
|
|
52
|
+
filename: z.string().optional(),
|
|
53
|
+
language: z.string().optional(),
|
|
54
|
+
code: z.string(),
|
|
55
|
+
})
|
|
56
|
+
.optional();
|
|
57
|
+
|
|
49
58
|
export const LandingHeroSchema = z
|
|
50
59
|
.object({
|
|
51
60
|
title: z.string().optional(),
|
|
52
61
|
subtitle: z.string().optional(),
|
|
53
62
|
description: z.string().optional(),
|
|
63
|
+
badge: z.string().optional(),
|
|
64
|
+
highlights: z.array(z.string()).optional(),
|
|
65
|
+
codeExample: LandingCodeExampleSchema,
|
|
66
|
+
})
|
|
67
|
+
.optional();
|
|
68
|
+
|
|
69
|
+
export const LandingTickerItemSchema = z.object({
|
|
70
|
+
label: z.string(),
|
|
71
|
+
value: z.string(),
|
|
72
|
+
change: z.string().optional(),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export const LandingTickerSchema = z
|
|
76
|
+
.object({
|
|
77
|
+
enabled: z.boolean().optional(),
|
|
78
|
+
items: z.array(LandingTickerItemSchema).optional(),
|
|
79
|
+
})
|
|
80
|
+
.optional();
|
|
81
|
+
|
|
82
|
+
export const LandingTrustSchema = z
|
|
83
|
+
.object({
|
|
84
|
+
enabled: z.boolean().optional(),
|
|
85
|
+
title: z.string().optional(),
|
|
86
|
+
logos: z.array(z.string()).optional(),
|
|
87
|
+
})
|
|
88
|
+
.optional();
|
|
89
|
+
|
|
90
|
+
export const LandingApiShowcaseItemSchema = z.object({
|
|
91
|
+
name: z.string(),
|
|
92
|
+
path: z.string(),
|
|
93
|
+
icon: z.string().optional(),
|
|
94
|
+
latency: z.string().optional(),
|
|
95
|
+
summary: z.string().optional(),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
export const LandingApiShowcaseSchema = z
|
|
99
|
+
.object({
|
|
100
|
+
enabled: z.boolean().optional(),
|
|
101
|
+
title: z.string().optional(),
|
|
102
|
+
subtitle: z.string().optional(),
|
|
103
|
+
browseAllHref: z.string().optional(),
|
|
104
|
+
browseAllLabel: z.string().optional(),
|
|
105
|
+
autoFromOpenApi: z.boolean().optional(),
|
|
106
|
+
items: z.array(LandingApiShowcaseItemSchema).optional(),
|
|
107
|
+
})
|
|
108
|
+
.optional();
|
|
109
|
+
|
|
110
|
+
export const LandingPricingCtaSchema = z
|
|
111
|
+
.object({
|
|
112
|
+
enabled: z.boolean().optional(),
|
|
113
|
+
title: z.string().optional(),
|
|
114
|
+
description: z.string().optional(),
|
|
115
|
+
primaryAction: LandingLinkSchema.optional(),
|
|
116
|
+
secondaryAction: LandingLinkSchema.optional(),
|
|
54
117
|
})
|
|
55
118
|
.optional();
|
|
56
119
|
|
|
@@ -60,6 +123,10 @@ export const LandingContentSchema = z
|
|
|
60
123
|
features: z.array(LandingFeatureSchema).optional(),
|
|
61
124
|
primaryAction: LandingLinkSchema.optional(),
|
|
62
125
|
secondaryAction: LandingLinkSchema.optional(),
|
|
126
|
+
ticker: LandingTickerSchema,
|
|
127
|
+
trust: LandingTrustSchema,
|
|
128
|
+
apiShowcase: LandingApiShowcaseSchema,
|
|
129
|
+
pricingCta: LandingPricingCtaSchema,
|
|
63
130
|
showPoweredBy: z.boolean().optional(),
|
|
64
131
|
})
|
|
65
132
|
.optional();
|
|
@@ -246,15 +313,32 @@ export type ProfileContentConfig = z.infer<typeof ProfileContentSchema>;
|
|
|
246
313
|
export type PlansContentConfig = z.infer<typeof PlansContentSchema>;
|
|
247
314
|
export type PlanTierConfig = z.infer<typeof PlanTierSchema>;
|
|
248
315
|
|
|
316
|
+
export type LandingCodeExampleConfig = z.infer<typeof LandingCodeExampleSchema>;
|
|
317
|
+
export type LandingTickerItemConfig = z.infer<typeof LandingTickerItemSchema>;
|
|
318
|
+
export type LandingTickerConfig = z.infer<typeof LandingTickerSchema>;
|
|
319
|
+
export type LandingTrustConfig = z.infer<typeof LandingTrustSchema>;
|
|
320
|
+
export type LandingApiShowcaseItemConfig = z.infer<
|
|
321
|
+
typeof LandingApiShowcaseItemSchema
|
|
322
|
+
>;
|
|
323
|
+
export type LandingApiShowcaseConfig = z.infer<typeof LandingApiShowcaseSchema>;
|
|
324
|
+
export type LandingPricingCtaConfig = z.infer<typeof LandingPricingCtaSchema>;
|
|
325
|
+
|
|
249
326
|
export type ResolvedLandingContent = {
|
|
250
327
|
hero?: {
|
|
251
328
|
title?: string;
|
|
252
329
|
subtitle?: string;
|
|
253
330
|
description?: string;
|
|
331
|
+
badge?: string;
|
|
332
|
+
highlights?: string[];
|
|
333
|
+
codeExample?: NonNullable<LandingCodeExampleConfig>;
|
|
254
334
|
};
|
|
255
335
|
features?: LandingFeatureConfig[];
|
|
256
336
|
primaryAction?: LandingLinkConfig;
|
|
257
337
|
secondaryAction?: LandingLinkConfig;
|
|
338
|
+
ticker?: LandingTickerConfig;
|
|
339
|
+
trust?: LandingTrustConfig;
|
|
340
|
+
apiShowcase?: LandingApiShowcaseConfig;
|
|
341
|
+
pricingCta?: LandingPricingCtaConfig;
|
|
258
342
|
showPoweredBy?: boolean;
|
|
259
343
|
};
|
|
260
344
|
|
|
@@ -492,8 +492,32 @@ const AuthenticationSchema = z.discriminatedUnion("type", [
|
|
|
492
492
|
redirectToAfterSignOut: z.string().optional(),
|
|
493
493
|
}),
|
|
494
494
|
z.object({
|
|
495
|
-
type: z.literal("dev-portal"),
|
|
495
|
+
type: z.literal("dev-portal").optional().default("dev-portal"),
|
|
496
496
|
apiBaseUrl: z.string().optional(),
|
|
497
|
+
native: z
|
|
498
|
+
.object({
|
|
499
|
+
enabled: z.boolean().optional(),
|
|
500
|
+
})
|
|
501
|
+
.optional(),
|
|
502
|
+
email: z
|
|
503
|
+
.object({
|
|
504
|
+
provider: z.string().optional(),
|
|
505
|
+
from: z.string().optional(),
|
|
506
|
+
connectionString: z.string().optional(),
|
|
507
|
+
})
|
|
508
|
+
.optional(),
|
|
509
|
+
providers: z
|
|
510
|
+
.array(
|
|
511
|
+
z.object({
|
|
512
|
+
id: z.string().min(1),
|
|
513
|
+
displayName: z.string().optional(),
|
|
514
|
+
authority: z.string().optional(),
|
|
515
|
+
clientId: z.string().optional(),
|
|
516
|
+
clientSecret: z.string().optional(),
|
|
517
|
+
scopes: z.string().optional(),
|
|
518
|
+
}),
|
|
519
|
+
)
|
|
520
|
+
.optional(),
|
|
497
521
|
redirectToAfterSignUp: z.string().optional(),
|
|
498
522
|
redirectToAfterSignIn: z.string().optional(),
|
|
499
523
|
redirectToAfterSignOut: z.string().optional(),
|
|
@@ -558,6 +582,7 @@ const ThemeConfigSchema = z.object({
|
|
|
558
582
|
const SiteSchema = z
|
|
559
583
|
.object({
|
|
560
584
|
title: z.string(),
|
|
585
|
+
showTitleInHeader: z.boolean().optional(),
|
|
561
586
|
logoUrl: z.string(),
|
|
562
587
|
dir: z.enum(["ltr", "rtl"]).optional(),
|
|
563
588
|
logo: LogoSchema,
|
|
@@ -27,22 +27,12 @@ export function applyAuthenticationHeaderNav<T extends ZudokuConfig>(
|
|
|
27
27
|
const authNavEnabled = config.__meta?.authenticationEnabled === true;
|
|
28
28
|
const navigation = config.header?.navigation ?? [];
|
|
29
29
|
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
if (filtered.length === navigation.length) {
|
|
33
|
-
return config;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
...config,
|
|
38
|
-
header: {
|
|
39
|
-
...config.header,
|
|
40
|
-
navigation: filtered,
|
|
41
|
-
},
|
|
42
|
-
};
|
|
30
|
+
if (authNavEnabled) {
|
|
31
|
+
return config;
|
|
43
32
|
}
|
|
44
33
|
|
|
45
|
-
|
|
34
|
+
const filtered = withoutLoginNavigation(navigation);
|
|
35
|
+
if (filtered.length === navigation.length) {
|
|
46
36
|
return config;
|
|
47
37
|
}
|
|
48
38
|
|
|
@@ -50,7 +40,7 @@ export function applyAuthenticationHeaderNav<T extends ZudokuConfig>(
|
|
|
50
40
|
...config,
|
|
51
41
|
header: {
|
|
52
42
|
...config.header,
|
|
53
|
-
navigation:
|
|
43
|
+
navigation: filtered,
|
|
54
44
|
},
|
|
55
45
|
};
|
|
56
46
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { HeaderNavigation } from "../../config/validators/HeaderNavigationSchema.js";
|
|
2
|
+
import { LOGIN_NAV_PATH } from "./auth-header-nav.js";
|
|
3
|
+
|
|
4
|
+
const AUTH_HEADER_PATHS = new Set([
|
|
5
|
+
LOGIN_NAV_PATH,
|
|
6
|
+
"/signin",
|
|
7
|
+
"/login",
|
|
8
|
+
"/signup",
|
|
9
|
+
"/register",
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
const PRICING_HEADER_PATHS = new Set(["/pricing"]);
|
|
13
|
+
|
|
14
|
+
export function withoutAuthHeaderNavigation(
|
|
15
|
+
navigation: HeaderNavigation,
|
|
16
|
+
): HeaderNavigation {
|
|
17
|
+
return navigation.filter(
|
|
18
|
+
(item) => !("to" in item) || !AUTH_HEADER_PATHS.has(item.to),
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function withoutPricingHeaderNavigation(
|
|
23
|
+
navigation: HeaderNavigation,
|
|
24
|
+
): HeaderNavigation {
|
|
25
|
+
return navigation.filter(
|
|
26
|
+
(item) => !("to" in item) || !PRICING_HEADER_PATHS.has(item.to),
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function withoutMarketingCtaHeaderNavigation(
|
|
31
|
+
navigation: HeaderNavigation,
|
|
32
|
+
): HeaderNavigation {
|
|
33
|
+
return withoutPricingHeaderNavigation(
|
|
34
|
+
withoutAuthHeaderNavigation(navigation),
|
|
35
|
+
);
|
|
36
|
+
}
|