Compare commits
2 Commits
7edd4c7f9d
...
4d70993817
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d70993817 | |||
| 32ccd49d78 |
@@ -75,6 +75,7 @@ It is created from [deploy.env.template](deploy.env.template). Current template
|
|||||||
```env
|
```env
|
||||||
APP_VERSION=4.2.3
|
APP_VERSION=4.2.3
|
||||||
ENABLE_GENERAL_ENQUIRIES=false
|
ENABLE_GENERAL_ENQUIRIES=false
|
||||||
|
PUBLIC_ENABLE_MOBILE_CTA_BUTTON=false
|
||||||
TZ=Pacific/Auckland
|
TZ=Pacific/Auckland
|
||||||
|
|
||||||
POSTGRES_DB=goodwalk
|
POSTGRES_DB=goodwalk
|
||||||
@@ -100,6 +101,11 @@ After the first deploy, edit `/docker/goodwalk-svelte/.env` on the server and re
|
|||||||
- `RESEND_API_KEY=replace-me`
|
- `RESEND_API_KEY=replace-me`
|
||||||
- `OWNER_EMAIL=replace-me`
|
- `OWNER_EMAIL=replace-me`
|
||||||
|
|
||||||
|
Frontend flags:
|
||||||
|
|
||||||
|
- `PUBLIC_ENABLE_MOBILE_CTA_BUTTON=false` keeps the sticky mobile booking CTA hidden.
|
||||||
|
- Set `PUBLIC_ENABLE_MOBILE_CTA_BUTTON=true` to show it again.
|
||||||
|
|
||||||
4. Confirm the shared Docker network already exists:
|
4. Confirm the shared Docker network already exists:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ CLIENT_BCC=mattcohen0@gmail.com
|
|||||||
FROM_EMAIL=GoodWalk <info@goodwalk.co.nz>
|
FROM_EMAIL=GoodWalk <info@goodwalk.co.nz>
|
||||||
REPLY_TO=info@goodwalk.co.nz
|
REPLY_TO=info@goodwalk.co.nz
|
||||||
ENABLE_GENERAL_ENQUIRIES=false
|
ENABLE_GENERAL_ENQUIRIES=false
|
||||||
|
PUBLIC_ENABLE_MOBILE_CTA_BUTTON=false
|
||||||
|
|
||||||
FORM_MIN_SECONDS=4
|
FORM_MIN_SECONDS=4
|
||||||
FORM_MAX_SECONDS=7200
|
FORM_MAX_SECONDS=7200
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ services:
|
|||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
PORT: 3000
|
PORT: 3000
|
||||||
ENABLE_GENERAL_ENQUIRIES: ${ENABLE_GENERAL_ENQUIRIES:-false}
|
ENABLE_GENERAL_ENQUIRIES: ${ENABLE_GENERAL_ENQUIRIES:-false}
|
||||||
|
PUBLIC_ENABLE_MOBILE_CTA_BUTTON: ${PUBLIC_ENABLE_MOBILE_CTA_BUTTON:-false}
|
||||||
TZ: ${TZ:-Pacific/Auckland}
|
TZ: ${TZ:-Pacific/Auckland}
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ services:
|
|||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
PORT: ${APP_PORT:-3000}
|
PORT: ${APP_PORT:-3000}
|
||||||
ENABLE_GENERAL_ENQUIRIES: ${ENABLE_GENERAL_ENQUIRIES:-false}
|
ENABLE_GENERAL_ENQUIRIES: ${ENABLE_GENERAL_ENQUIRIES:-false}
|
||||||
|
PUBLIC_ENABLE_MOBILE_CTA_BUTTON: ${PUBLIC_ENABLE_MOBILE_CTA_BUTTON:-false}
|
||||||
TZ: ${TZ:-Pacific/Auckland}
|
TZ: ${TZ:-Pacific/Auckland}
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { afterNavigate } from '$app/navigation';
|
import { afterNavigate } from '$app/navigation';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import Icon from '$lib/components/Icon.svelte';
|
import Icon from '$lib/components/Icon.svelte';
|
||||||
|
import { isMobileCtaButtonEnabled } from '$lib/feature-flags';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sticky bottom CTA shown on mobile only.
|
* Sticky bottom CTA shown on mobile only.
|
||||||
@@ -20,6 +21,8 @@
|
|||||||
* to book while they're already on the form).
|
* to book while they're already on the form).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const mobileCtaButtonEnabled = isMobileCtaButtonEnabled();
|
||||||
|
|
||||||
$: pathname = $page.url.pathname;
|
$: pathname = $page.url.pathname;
|
||||||
$: hidden = pathname === '/contact-us' || pathname === '/booking';
|
$: hidden = pathname === '/contact-us' || pathname === '/booking';
|
||||||
|
|
||||||
@@ -41,7 +44,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function setupObservers() {
|
async function setupObservers() {
|
||||||
if (typeof window === 'undefined') {
|
if (!mobileCtaButtonEnabled || typeof window === 'undefined') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,6 +87,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
afterNavigate(() => {
|
afterNavigate(() => {
|
||||||
|
if (!mobileCtaButtonEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
visible = false;
|
visible = false;
|
||||||
triggerPassed = false;
|
triggerPassed = false;
|
||||||
bookingInView = false;
|
bookingInView = false;
|
||||||
@@ -91,6 +98,10 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
if (!mobileCtaButtonEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void setupObservers();
|
void setupObservers();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@@ -99,7 +110,7 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if !hidden}
|
{#if mobileCtaButtonEnabled && !hidden}
|
||||||
<div
|
<div
|
||||||
class="mobile-book-bar"
|
class="mobile-book-bar"
|
||||||
class:mobile-book-bar-visible={visible}
|
class:mobile-book-bar-visible={visible}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
describe('feature flags', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
vi.unstubAllEnvs();
|
||||||
|
vi.resetModules();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('defaults the mobile CTA button to disabled', async () => {
|
||||||
|
const { isMobileCtaButtonEnabled } = await import('./feature-flags');
|
||||||
|
|
||||||
|
expect(isMobileCtaButtonEnabled()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('enables the mobile CTA button when the public env flag is truthy', async () => {
|
||||||
|
vi.stubEnv('PUBLIC_ENABLE_MOBILE_CTA_BUTTON', 'enabled');
|
||||||
|
|
||||||
|
const { isMobileCtaButtonEnabled } = await import('./feature-flags');
|
||||||
|
|
||||||
|
expect(isMobileCtaButtonEnabled()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('treats explicit false values as disabled', async () => {
|
||||||
|
vi.stubEnv('PUBLIC_ENABLE_MOBILE_CTA_BUTTON', 'off');
|
||||||
|
|
||||||
|
const { isMobileCtaButtonEnabled } = await import('./feature-flags');
|
||||||
|
|
||||||
|
expect(isMobileCtaButtonEnabled()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
export function parseBooleanFlag(value: string | undefined, defaultValue = false) {
|
||||||
|
if (value == null) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalized = value.trim().toLowerCase();
|
||||||
|
|
||||||
|
if (['1', 'true', 'yes', 'on', 'enabled'].includes(normalized)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['0', 'false', 'no', 'off', 'disabled'].includes(normalized)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isMobileCtaButtonEnabled() {
|
||||||
|
return parseBooleanFlag(import.meta.env.PUBLIC_ENABLE_MOBILE_CTA_BUTTON, false);
|
||||||
|
}
|
||||||
@@ -1,20 +1,4 @@
|
|||||||
function parseBooleanFlag(value: string | undefined, defaultValue = false) {
|
import { parseBooleanFlag } from '$lib/feature-flags';
|
||||||
if (value == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalized = value.trim().toLowerCase();
|
|
||||||
|
|
||||||
if (['1', 'true', 'yes', 'on', 'enabled'].includes(normalized)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (['0', 'false', 'no', 'off', 'disabled'].includes(normalized)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isGeneralEnquiryEnabled() {
|
export function isGeneralEnquiryEnabled() {
|
||||||
return parseBooleanFlag(process.env.ENABLE_GENERAL_ENQUIRIES, false);
|
return parseBooleanFlag(process.env.ENABLE_GENERAL_ENQUIRIES, false);
|
||||||
|
|||||||
Reference in New Issue
Block a user