fix: preserve landing page section content when toggled off (#838)
* preserve section content when toggled off * Update types.ts * Remove comment on LandingPageLinks enabled property Removed comment about the default visibility of the section. --------- Co-authored-by: Rotimi Best <best@classroomio.com>
This commit is contained in:
co-authored by
Rotimi Best
parent
0dc32b5210
commit
6537613000
@@ -168,7 +168,7 @@ The repository also contains shared packages under `packages/` (for example `pac
|
|||||||
PUBLIC_SERVER_URL="http://localhost:3002"
|
PUBLIC_SERVER_URL="http://localhost:3002"
|
||||||
PRIVATE_SERVER_URL="http://localhost:3002" # used by the SSR auth proxy — required, or login returns "API upstream not configured"
|
PRIVATE_SERVER_URL="http://localhost:3002" # used by the SSR auth proxy — required, or login returns "API upstream not configured"
|
||||||
PRIVATE_SERVER_KEY="<generate-with-openssl-rand-hex-32>" # must match the API (paste the value generated above)
|
PRIVATE_SERVER_KEY="<generate-with-openssl-rand-hex-32>" # must match the API (paste the value generated above)
|
||||||
PUBLIC_IS_SELFHOSTED=true
|
PUBLIC_IS_SELFHOSTED=false
|
||||||
```
|
```
|
||||||
|
|
||||||
- Running the background workers? Also create `apps/jobs/.env` (e.g. `cp apps/api/.env apps/jobs/.env`); it needs `REDIS_URL`. The `packages/db/.env` used by the DB scripts is set up in step 6.
|
- Running the background workers? Also create `apps/jobs/.env` (e.g. `cp apps/api/.env apps/jobs/.env`); it needs `REDIS_URL`. The `packages/db/.env` used by the DB scripts is set up in step 6.
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ type LegacyLandingPageJson = {
|
|||||||
footerText?: string;
|
footerText?: string;
|
||||||
theme?: OrgLandingPageTheme;
|
theme?: OrgLandingPageTheme;
|
||||||
embed?: {
|
embed?: {
|
||||||
|
enabled?: boolean;
|
||||||
title?: string;
|
title?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
code?: string;
|
code?: string;
|
||||||
@@ -138,6 +139,7 @@ type LegacyLandingPageJson = {
|
|||||||
sandbox?: string;
|
sandbox?: string;
|
||||||
};
|
};
|
||||||
callout?: {
|
callout?: {
|
||||||
|
enabled?: boolean;
|
||||||
heading?: string;
|
heading?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
action?: {
|
action?: {
|
||||||
@@ -413,6 +415,7 @@ function normalizeCallout(callout: NonNullable<LegacyLandingPageJson['callout']>
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
enabled: callout.enabled !== false,
|
||||||
heading,
|
heading,
|
||||||
description: normalizeText(callout.description, ''),
|
description: normalizeText(callout.description, ''),
|
||||||
action: {
|
action: {
|
||||||
@@ -468,6 +471,7 @@ function normalizeLinks(raw: unknown): OrgLandingPageLinks | undefined {
|
|||||||
const classicLearnMoreLabel = normalizeText(raw.classicLearnMoreLabel, '');
|
const classicLearnMoreLabel = normalizeText(raw.classicLearnMoreLabel, '');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
enabled: raw.enabled !== false,
|
||||||
heading,
|
heading,
|
||||||
description: description || undefined,
|
description: description || undefined,
|
||||||
boldVisitLabel: boldVisitLabel || undefined,
|
boldVisitLabel: boldVisitLabel || undefined,
|
||||||
@@ -541,6 +545,7 @@ export function normalizeLandingPageSettings(value: unknown): OrgLandingPageJson
|
|||||||
footer: resolveFooterConfig(landingPage),
|
footer: resolveFooterConfig(landingPage),
|
||||||
embed: landingPage.embed
|
embed: landingPage.embed
|
||||||
? {
|
? {
|
||||||
|
enabled: landingPage.embed.enabled !== false,
|
||||||
title: normalizeText(landingPage.embed.title, 'Section title'),
|
title: normalizeText(landingPage.embed.title, 'Section title'),
|
||||||
description: normalizeText(landingPage.embed.description, ''),
|
description: normalizeText(landingPage.embed.description, ''),
|
||||||
code: normalizeEmbedCode(landingPage.embed),
|
code: normalizeEmbedCode(landingPage.embed),
|
||||||
@@ -587,6 +592,7 @@ export function normalizeLandingPageSettings(value: unknown): OrgLandingPageJson
|
|||||||
footer: resolveFooterConfig(landingPage),
|
footer: resolveFooterConfig(landingPage),
|
||||||
embed: landingPage.embed
|
embed: landingPage.embed
|
||||||
? {
|
? {
|
||||||
|
enabled: landingPage.embed.enabled !== false,
|
||||||
title: normalizeText(landingPage.embed.title, 'Section title'),
|
title: normalizeText(landingPage.embed.title, 'Section title'),
|
||||||
description: normalizeText(landingPage.embed.description, ''),
|
description: normalizeText(landingPage.embed.description, ''),
|
||||||
code: normalizeEmbedCode(landingPage.embed)
|
code: normalizeEmbedCode(landingPage.embed)
|
||||||
@@ -624,6 +630,7 @@ export function normalizeLandingPageSettings(value: unknown): OrgLandingPageJson
|
|||||||
footer: resolveFooterConfig(landingPage),
|
footer: resolveFooterConfig(landingPage),
|
||||||
embed: landingPage.embed
|
embed: landingPage.embed
|
||||||
? {
|
? {
|
||||||
|
enabled: landingPage.embed.enabled !== false,
|
||||||
title: normalizeText(landingPage.embed.title, 'Section title'),
|
title: normalizeText(landingPage.embed.title, 'Section title'),
|
||||||
description: normalizeText(landingPage.embed.description, ''),
|
description: normalizeText(landingPage.embed.description, ''),
|
||||||
code: normalizeEmbedCode(landingPage.embed),
|
code: normalizeEmbedCode(landingPage.embed),
|
||||||
|
|||||||
+4
-12
@@ -33,6 +33,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const callout: OrgLandingPageCallout = {
|
const callout: OrgLandingPageCallout = {
|
||||||
|
enabled: true,
|
||||||
heading: t.get('settings.landing_page.editor.callout.heading_default'),
|
heading: t.get('settings.landing_page.editor.callout.heading_default'),
|
||||||
description: '',
|
description: '',
|
||||||
action: {
|
action: {
|
||||||
@@ -51,16 +52,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setCalloutEnabled(enabled: boolean) {
|
function setCalloutEnabled(enabled: boolean) {
|
||||||
if (!enabled) {
|
setter({ ...ensureCallout(), enabled }, 'callout');
|
||||||
settings = {
|
|
||||||
...settings,
|
|
||||||
callout: undefined
|
|
||||||
};
|
|
||||||
markDirty();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setter(ensureCallout(), 'callout');
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -71,13 +63,13 @@
|
|||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<Field.Field orientation="horizontal">
|
<Field.Field orientation="horizontal">
|
||||||
<Switch
|
<Switch
|
||||||
checked={Boolean(settings.callout)}
|
checked={settings.callout?.enabled ?? false}
|
||||||
onCheckedChange={(checked) => setCalloutEnabled(checked === true)}
|
onCheckedChange={(checked) => setCalloutEnabled(checked === true)}
|
||||||
/>
|
/>
|
||||||
<Field.Label>{$t('settings.landing_page.editor.callout.toggle')}</Field.Label>
|
<Field.Label>{$t('settings.landing_page.editor.callout.toggle')}</Field.Label>
|
||||||
</Field.Field>
|
</Field.Field>
|
||||||
|
|
||||||
{#if settings.callout}
|
{#if settings.callout?.enabled}
|
||||||
<Field.Field>
|
<Field.Field>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<Field.Label>{$t('settings.landing_page.editor.callout.heading')}</Field.Label>
|
<Field.Label>{$t('settings.landing_page.editor.callout.heading')}</Field.Label>
|
||||||
|
|||||||
+9
-12
@@ -34,6 +34,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const embed = {
|
const embed = {
|
||||||
|
enabled: true,
|
||||||
title: t.get('settings.landing_page.editor.embed.title'),
|
title: t.get('settings.landing_page.editor.embed.title'),
|
||||||
description: '',
|
description: '',
|
||||||
code: '',
|
code: '',
|
||||||
@@ -50,16 +51,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setEmbedEnabled(enabled: boolean) {
|
function setEmbedEnabled(enabled: boolean) {
|
||||||
if (!enabled) {
|
setter({ ...ensureEmbed(), enabled }, 'embed');
|
||||||
settings = {
|
|
||||||
...settings,
|
|
||||||
embed: undefined
|
|
||||||
};
|
|
||||||
markDirty();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setter(ensureEmbed(), 'embed');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSecondaryAction(label: string, href: string) {
|
function updateSecondaryAction(label: string, href: string) {
|
||||||
@@ -83,11 +75,15 @@
|
|||||||
|
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<Field.Field orientation="horizontal">
|
<Field.Field orientation="horizontal">
|
||||||
<Switch checked={Boolean(settings.embed)} onCheckedChange={(checked) => setEmbedEnabled(checked === true)} />
|
<Switch
|
||||||
|
checked={settings.embed?.enabled ?? false}
|
||||||
|
onCheckedChange={(checked) => setEmbedEnabled(checked === true)}
|
||||||
|
/>
|
||||||
<Field.Label>{$t('settings.landing_page.editor.embed.toggle')}</Field.Label>
|
<Field.Label>{$t('settings.landing_page.editor.embed.toggle')}</Field.Label>
|
||||||
</Field.Field>
|
</Field.Field>
|
||||||
|
|
||||||
{#if settings.embed}
|
{#if settings.embed?.enabled}
|
||||||
|
<div class="pb-5">
|
||||||
<Field.Field>
|
<Field.Field>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<Field.Label>{$t('settings.landing_page.editor.embed.title')}</Field.Label>
|
<Field.Label>{$t('settings.landing_page.editor.embed.title')}</Field.Label>
|
||||||
@@ -140,6 +136,7 @@
|
|||||||
onLabelInput={(value) => updateSecondaryAction(value, settings.embed.secondaryAction?.href ?? '')}
|
onLabelInput={(value) => updateSecondaryAction(value, settings.embed.secondaryAction?.href ?? '')}
|
||||||
onHrefInput={(value) => updateSecondaryAction(settings.embed.secondaryAction?.label ?? '', value)}
|
onHrefInput={(value) => updateSecondaryAction(settings.embed.secondaryAction?.label ?? '', value)}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</Field.Set>
|
</Field.Set>
|
||||||
|
|||||||
+7
-8
@@ -54,6 +54,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const links: NonNullable<OrgLandingPageJson['links']> = {
|
const links: NonNullable<OrgLandingPageJson['links']> = {
|
||||||
|
enabled: true,
|
||||||
heading: t.get('settings.landing_page.editor.links.heading_default'),
|
heading: t.get('settings.landing_page.editor.links.heading_default'),
|
||||||
description: '',
|
description: '',
|
||||||
boldVisitLabel: t.get('settings.landing_page.editor.links.bold_visit_default'),
|
boldVisitLabel: t.get('settings.landing_page.editor.links.bold_visit_default'),
|
||||||
@@ -90,16 +91,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setLinksEnabled(enabled: boolean) {
|
function setLinksEnabled(enabled: boolean) {
|
||||||
if (!enabled) {
|
|
||||||
settings = {
|
settings = {
|
||||||
...settings,
|
...settings,
|
||||||
links: undefined
|
links: { ...ensureLinks(), enabled }
|
||||||
};
|
};
|
||||||
markDirty();
|
markDirty();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ensureLinks();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function addCard() {
|
function addCard() {
|
||||||
@@ -137,11 +133,14 @@
|
|||||||
|
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<Field.Field orientation="horizontal">
|
<Field.Field orientation="horizontal">
|
||||||
<Switch checked={Boolean(settings.links)} onCheckedChange={(checked) => setLinksEnabled(checked === true)} />
|
<Switch
|
||||||
|
checked={settings.links?.enabled ?? false}
|
||||||
|
onCheckedChange={(checked) => setLinksEnabled(checked === true)}
|
||||||
|
/>
|
||||||
<Field.Label>{$t('settings.landing_page.editor.links.toggle')}</Field.Label>
|
<Field.Label>{$t('settings.landing_page.editor.links.toggle')}</Field.Label>
|
||||||
</Field.Field>
|
</Field.Field>
|
||||||
|
|
||||||
{#if settings.links}
|
{#if settings.links?.enabled}
|
||||||
<Field.Field>
|
<Field.Field>
|
||||||
<Field.Label>{$t('settings.landing_page.editor.links.heading')}</Field.Label>
|
<Field.Label>{$t('settings.landing_page.editor.links.heading')}</Field.Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ export type OrgLandingPageFooterConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export interface OrgLandingPageEmbed {
|
export interface OrgLandingPageEmbed {
|
||||||
|
enabled: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
code: string;
|
code: string;
|
||||||
@@ -60,6 +61,7 @@ export interface OrgLandingPageEmbed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface OrgLandingPageCallout {
|
export interface OrgLandingPageCallout {
|
||||||
|
enabled: boolean;
|
||||||
heading: string;
|
heading: string;
|
||||||
description: string;
|
description: string;
|
||||||
action: {
|
action: {
|
||||||
@@ -88,6 +90,7 @@ export interface OrgLandingPageLinkCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface OrgLandingPageLinks {
|
export interface OrgLandingPageLinks {
|
||||||
|
enabled: boolean;
|
||||||
heading: string;
|
heading: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
boldVisitLabel?: string;
|
boldVisitLabel?: string;
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ Composable page shell used across dashboard list and settings screens. Import as
|
|||||||
The bar also sits at `z-50`, matching the editor root so it paints above editor content (later in DOM wins the tie) while staying below body-portaled dialogs and sheets. Its shadow appears only while it is pinned away from its flow position, tracked by an `IntersectionObserver` on a 1px sentinel rendered as its next sibling.
|
The bar also sits at `z-50`, matching the editor root so it paints above editor content (later in DOM wins the tie) while staying below body-portaled dialogs and sheets. Its shadow appears only while it is pinned away from its flow position, tracked by an `IntersectionObserver` on a 1px sentinel rendered as its next sibling.
|
||||||
|
|
||||||
| Prop | Description |
|
| Prop | Description |
|
||||||
| -------------- | -------------------------------------------------------- |
|
| -------------- | ------------------------------------------------------ |
|
||||||
| `hasChanges` | Enables Save and Discard when the form is dirty |
|
| `hasChanges` | Enables Save and Discard when the form is dirty |
|
||||||
| `loading` | Shows loading state on Save |
|
| `loading` | Shows loading state on Save |
|
||||||
| `disabled` | Disables Save only (e.g. while not initialized) |
|
| `disabled` | Disables Save only (e.g. while not initialized) |
|
||||||
|
|||||||
@@ -107,7 +107,7 @@
|
|||||||
`;
|
`;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if callout}
|
{#if callout && callout.enabled !== false}
|
||||||
<EditableLandingSection sectionKey="callout">
|
<EditableLandingSection sectionKey="callout">
|
||||||
<section
|
<section
|
||||||
class={sectionClasses[variant]}
|
class={sectionClasses[variant]}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
} = $props();
|
} = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if embed}
|
{#if embed && embed.enabled !== false}
|
||||||
<EditableLandingSection sectionKey="embed">
|
<EditableLandingSection sectionKey="embed">
|
||||||
{#if variant === 'terminal'}
|
{#if variant === 'terminal'}
|
||||||
<section class="ui:py-24 ui:px-6" style="border-top: 1px solid #1c1f28; background: #06070a;">
|
<section class="ui:py-24 ui:px-6" style="border-top: 1px solid #1c1f28; background: #06070a;">
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if links && links.cards.length > 0}
|
{#if links && links.enabled !== false && links.cards.length > 0}
|
||||||
<EditableLandingSection sectionKey="links">
|
<EditableLandingSection sectionKey="links">
|
||||||
{#if variant === 'minimal'}
|
{#if variant === 'minimal'}
|
||||||
<section class="ui:py-20 ui:px-4">
|
<section class="ui:py-20 ui:px-4">
|
||||||
|
|||||||
@@ -81,17 +81,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{#if links && links.cards.length > 0}
|
{#if links && links.enabled !== false && links.cards.length > 0}
|
||||||
<div class="plus-row"></div>
|
<div class="plus-row"></div>
|
||||||
<OrgLandingPageLinks {links} {labels} variant="saas" />
|
<OrgLandingPageLinks {links} {labels} variant="saas" />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if embed}
|
{#if embed && embed.enabled !== false}
|
||||||
<div class="plus-row"></div>
|
<div class="plus-row"></div>
|
||||||
<OrgLandingPageEmbed {embed} {labels} variant="saas" />
|
<OrgLandingPageEmbed {embed} {labels} variant="saas" />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if callout}
|
{#if callout && callout.enabled !== false}
|
||||||
<div class="plus-row"></div>
|
<div class="plus-row"></div>
|
||||||
<OrgLandingPageCallout {callout} {labels} variant="saas" />
|
<OrgLandingPageCallout {callout} {labels} variant="saas" />
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ export type OrgLandingPageFooterConfig = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
export type LandingPageEmbed = {
|
export type LandingPageEmbed = {
|
||||||
|
enabled?: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
code: string;
|
code: string;
|
||||||
@@ -101,6 +102,7 @@ export type LandingPageEmbed = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type LandingPageCallout = {
|
export type LandingPageCallout = {
|
||||||
|
enabled?: boolean;
|
||||||
heading: string;
|
heading: string;
|
||||||
description: string;
|
description: string;
|
||||||
action: {
|
action: {
|
||||||
@@ -129,6 +131,7 @@ export type LandingPageLinkCard = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type LandingPageLinks = {
|
export type LandingPageLinks = {
|
||||||
|
enabled?: boolean;
|
||||||
heading: string;
|
heading: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
/** Bold template card footer chip (e.g. Visit). */
|
/** Bold template card footer chip (e.g. Visit). */
|
||||||
|
|||||||
Reference in New Issue
Block a user