Add Sites feature flag and unavailable state

Display waitlist message when Sites feature is disabled. The feature
can be toggled via the sitesLive flag and includes responsive mobile
images for the unavailable state.
This commit is contained in:
Torsten Dittmann
2025-06-03 17:17:20 +02:00
parent 33bd0c3298
commit 4a36260152
2 changed files with 68 additions and 35 deletions
@@ -11,12 +11,14 @@
import { isServiceLimited } from '$lib/stores/billing';
import { organization } from '$lib/stores/organization';
import { canWriteSites } from '$lib/stores/roles.js';
import { Icon, Layout } from '@appwrite.io/pink-svelte';
import { Card, Icon, Layout, Typography } from '@appwrite.io/pink-svelte';
import { Button } from '$lib/elements/forms';
import { app } from '$lib/stores/app';
import CreateSiteModal from './createSiteModal.svelte';
import EmptyLight from './(images)/empty-sites-light.svg';
import EmptyDark from './(images)/empty-sites-dark.svg';
import EmptyLightMobile from './(images)/empty-sites-light-mobile.svg';
import EmptyDarkMobile from './(images)/empty-sites-dark-mobile.svg';
import Grid from './grid.svelte';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import { columns } from './store';
@@ -26,6 +28,7 @@
import { invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
import { sdk } from '$lib/stores/sdk';
import { isSmallViewport } from '$lib/stores/viewport';
export let data;
@@ -55,46 +58,65 @@
}
});
});
$: isDark = $app.themeInUse === 'dark';
$: imgSrc = isDark
? $isSmallViewport
? EmptyDarkMobile
: EmptyDark
: $isSmallViewport
? EmptyLightMobile
: EmptyLight;
$: imgClass = $isSmallViewport ? 'mobile' : 'desktop';
</script>
<Container>
<Layout.Stack direction="row" justifyContent="space-between">
<Layout.Stack direction="row" alignItems="center">
<SearchQuery placeholder="Search by name" />
</Layout.Stack>
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
<ViewSelector {columns} view={data.view} hideColumns hideView={!data.siteList.total} />
{#if $canWriteSites}
<Button on:mousedown={() => (show = true)} event="create_site" size="s">
<Icon icon={IconPlus} slot="start" size="s" />
Create site
</Button>
{#if data.sitesLive}
{#if data.siteList.total}
{#if data.view === View.Grid}
<Grid siteList={data.siteList} />
{:else}
<Table siteList={data.siteList} />
{/if}
</Layout.Stack>
</Layout.Stack>
{#if data.siteList.total}
{#if data.view === View.Grid}
<Grid siteList={data.siteList} />
<PaginationWithLimit
name="Sites"
limit={data.limit}
offset={data.offset}
total={data.siteList.total} />
{:else if data.search}
<EmptySearch target="sites" />
{:else}
<Table siteList={data.siteList} />
<Empty
single
allowCreate={$canWriteSites}
href="https://appwrite.io/docs/products/sites"
description="Deploy and manage your web your web applications with Sites. "
target="site"
src={$app.themeInUse === 'dark' ? EmptyDark : EmptyLight}
on:click={() => (show = true)}>
</Empty>
{/if}
<PaginationWithLimit
name="Sites"
limit={data.limit}
offset={data.offset}
total={data.siteList.total} />
{:else if data.search}
<EmptySearch target="sites" />
{:else}
<Empty
single
allowCreate={$canWriteSites}
href="https://appwrite.io/docs/products/sites"
description="Deploy and manage your web your web applications with Sites. "
target="site"
src={$app.themeInUse === 'dark' ? EmptyDark : EmptyLight}
on:click={() => (show = true)}>
</Empty>
<Card.Base padding="m">
<Layout.Stack gap="xxl">
<img src={imgSrc} alt="create" aria-hidden="true" height="242" class={imgClass} />
<Layout.Stack>
<Layout.Stack gap="m" alignItems="center">
<Typography.Title size="s" align="center" color="--fgcolor-neutral-primary">
Appwrite Sites is in high demand
</Typography.Title>
<div style:max-width="600px">
<Typography.Text align="center" color="--fgcolor-neutral-secondary">
To ensure a smooth experience for everyone, we’re rolling out access
gradually.
</Typography.Text>
</div>
</Layout.Stack>
</Layout.Stack>
</Layout.Stack>
</Card.Base>
{/if}
</Container>
@@ -5,13 +5,23 @@ import { CARD_LIMIT, Dependencies } from '$lib/constants';
export const load = async ({ url, depends, route, params }) => {
depends(Dependencies.SITES);
const page = getPage(url);
const search = getSearch(url);
const limit = getLimit(url, route, CARD_LIMIT);
const offset = pageToOffset(page, limit);
const view = getView(url, route, View.Grid, View.Grid);
const sitesLive = false;
if (!sitesLive)
return {
sitesLive,
offset,
limit,
search,
view
};
const siteList = await sdk
.forProject(params.region, params.project)
.sites.list(
@@ -20,6 +30,7 @@ export const load = async ({ url, depends, route, params }) => {
);
return {
sitesLive,
offset,
limit,
search,