Skip to content

Commit 32db8b4

Browse files
committed
feat: enforce custom domain plan limits in UI
1 parent d1729ef commit 32db8b4

File tree

6 files changed

+230
-73
lines changed

6 files changed

+230
-73
lines changed

src/lib/components/billing/planComparisonBox.svelte

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,18 @@
8686
{formatNum(currentPlan.executions)} executions
8787
</span>
8888
</li>
89+
{#if currentPlan.domains > 0}
90+
<li class="list-item u-gap-4 u-cross-center">
91+
<span class="icon-arrow-down u-color-text-danger" aria-hidden="true"></span>
92+
<span class="text">
93+
Limited to {currentPlan.domains} custom {pluralize(
94+
currentPlan.domains,
95+
'domain'
96+
)}
97+
per project
98+
</span>
99+
</li>
100+
{/if}
89101
</ul>
90102
{:else}
91103
<ul class="un-order-list">
@@ -105,12 +117,22 @@
105117
<li>
106118
Limited to {formatNum(currentPlan.executions)} executions
107119
</li>
120+
{#if currentPlan.domains > 0}
121+
<li>
122+
Limited to {currentPlan.domains} custom {pluralize(
123+
currentPlan.domains,
124+
'domain'
125+
)} per project
126+
</li>
127+
{:else}
128+
<li>Unlimited custom domains</li>
129+
{/if}
108130
</ul>
109131
{/if}
110132
{:else if planHasGroup(selectedTab, BillingPlanGroup.Pro)}
111133
<Typography.Text>Everything in the Free plan, plus:</Typography.Text>
112134
<ul class="un-order-list">
113-
<li>Unlimited databases, buckets, functions</li>
135+
<li>Unlimited databases, buckets, functions, and custom domains</li>
114136
<li>Unlimited seats</li>
115137
<li>{currentPlan.bandwidth}GB bandwidth</li>
116138
<li>{currentPlan.storage}GB storage</li>

src/lib/stores/billing.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ export type PlanServices =
200200
| 'bandwidthAddon'
201201
| 'buckets'
202202
| 'databases'
203+
| 'domains'
203204
| 'executions'
204205
| 'executionsAddon'
205206
| 'fileSize'
@@ -316,6 +317,7 @@ export function checkForProjectLimitation(plan: string, id: PlanServices) {
316317

317318
switch (id) {
318319
case 'databases':
320+
case 'domains':
319321
case 'functions':
320322
case 'buckets':
321323
case 'members': // Only applies to Free plan now

src/routes/(console)/organization-[organization]/domains/+page.svelte

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
Layout,
2323
Popover,
2424
Table,
25+
Tooltip,
2526
Typography
2627
} from '@appwrite.io/pink-svelte';
2728
import DeleteDomainModal from './deleteDomainModal.svelte';
@@ -30,12 +31,20 @@
3031
import SearchQuery from '$lib/components/searchQuery.svelte';
3132
import { app } from '$lib/stores/app';
3233
import { Click, trackEvent } from '$lib/actions/analytics';
34+
import {
35+
BODY_TOOLTIP_MAX_WIDTH,
36+
BODY_TOOLTIP_WRAPPER_STYLE_PRELINE
37+
} from '$lib/helpers/tooltipContent';
38+
import { isServiceLimited } from '$lib/stores/billing';
39+
import { organization } from '$lib/stores/organization';
3340
import { columns } from './store';
3441
import { View } from '$lib/helpers/load';
3542
import type { Models } from '@appwrite.io/console';
3643
3744
export let data;
3845
46+
$: isDomainLimitReached = isServiceLimited('domains', $organization, data.domains.total);
47+
3948
let showDelete = false;
4049
let showRetry = false;
4150
let selectedDomain: Models.Domain = null;
@@ -50,16 +59,28 @@
5059
<SearchQuery placeholder="Search domains" />
5160
<Layout.Stack direction="row" gap="m" inline>
5261
<ViewSelector ui="new" view={View.Table} {columns} hideView />
53-
<Button
54-
on:click={() => {
55-
trackEvent(Click.DomainCreateClick, {
56-
source: 'organization_domain_overview'
57-
});
58-
}}
59-
href={`${base}/organization-${page.params.organization}/domains/add-domain`}>
60-
<Icon icon={IconPlus} size="s" />
61-
Add domain
62-
</Button>
62+
<Tooltip disabled={!isDomainLimitReached} maxWidth={BODY_TOOLTIP_MAX_WIDTH}>
63+
<div>
64+
<Button
65+
disabled={isDomainLimitReached}
66+
on:click={() => {
67+
trackEvent(Click.DomainCreateClick, {
68+
source: 'organization_domain_overview'
69+
});
70+
}}
71+
href={isDomainLimitReached
72+
? undefined
73+
: `${base}/organization-${page.params.organization}/domains/add-domain`}>
74+
<Icon icon={IconPlus} size="s" />
75+
Add domain
76+
</Button>
77+
</div>
78+
<svelte:fragment slot="tooltip">
79+
<div style={BODY_TOOLTIP_WRAPPER_STYLE_PRELINE}>
80+
You have reached the maximum number of custom domains for your plan.
81+
</div>
82+
</svelte:fragment>
83+
</Tooltip>
6384
</Layout.Stack>
6485
</Layout.Stack>
6586

@@ -206,17 +227,29 @@
206227
size="s"
207228
ariaLabel="add domain">Documentation</Button>
208229

209-
<Button
210-
secondary
211-
on:click={() => {
212-
trackEvent(Click.DomainCreateClick, {
213-
source: 'organization_domain_overview'
214-
});
215-
}}
216-
href={`${base}/organization-${page.params.organization}/domains/add-domain`}
217-
size="s">
218-
Add domain
219-
</Button>
230+
<Tooltip disabled={!isDomainLimitReached} maxWidth={BODY_TOOLTIP_MAX_WIDTH}>
231+
<div>
232+
<Button
233+
secondary
234+
disabled={isDomainLimitReached}
235+
on:click={() => {
236+
trackEvent(Click.DomainCreateClick, {
237+
source: 'organization_domain_overview'
238+
});
239+
}}
240+
href={isDomainLimitReached
241+
? undefined
242+
: `${base}/organization-${page.params.organization}/domains/add-domain`}
243+
size="s">
244+
Add domain
245+
</Button>
246+
</div>
247+
<svelte:fragment slot="tooltip">
248+
<div style={BODY_TOOLTIP_WRAPPER_STYLE_PRELINE}>
249+
You have reached the maximum number of custom domains for your plan.
250+
</div>
251+
</svelte:fragment>
252+
</Tooltip>
220253
</svelte:fragment>
221254
</Empty>
222255
</Card.Base>

src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/+page.svelte

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,26 @@
66
import Container from '$lib/layout/container.svelte';
77
import type { Models } from '@appwrite.io/console';
88
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
9-
import { Card, Empty, Icon, Layout } from '@appwrite.io/pink-svelte';
9+
import { Card, Empty, Icon, Layout, Tooltip } from '@appwrite.io/pink-svelte';
1010
import DeleteDomainModal from './deleteDomainModal.svelte';
1111
import RetryDomainModal from './retryDomainModal.svelte';
1212
import SearchQuery from '$lib/components/searchQuery.svelte';
1313
import { app } from '$lib/stores/app';
1414
import { Click, trackEvent } from '$lib/actions/analytics';
15+
import {
16+
BODY_TOOLTIP_MAX_WIDTH,
17+
BODY_TOOLTIP_WRAPPER_STYLE_PRELINE
18+
} from '$lib/helpers/tooltipContent';
19+
import { isServiceLimited } from '$lib/stores/billing';
20+
import { organization } from '$lib/stores/organization';
1521
import Table from './table.svelte';
1622
1723
let { data } = $props();
1824
25+
const isDomainLimitReached = $derived(
26+
isServiceLimited('domains', $organization, data.proxyRules.total)
27+
);
28+
1929
let showDelete = $state(false);
2030
let showRetry = $state(false);
2131
let selectedProxyRule: Models.ProxyRule = null;
@@ -24,16 +34,28 @@
2434
<Container>
2535
<Layout.Stack direction="row" justifyContent="space-between">
2636
<SearchQuery placeholder="Search domain" />
27-
<Button
28-
href={`${base}/project-${page.params.region}-${page.params.project}/functions/function-${page.params.function}/domains/add-domain`}
29-
on:click={() => {
30-
trackEvent(Click.DomainCreateClick, {
31-
source: 'functions_domain_overview'
32-
});
33-
}}>
34-
<Icon icon={IconPlus} size="s" />
35-
Add domain
36-
</Button>
37+
<Tooltip disabled={!isDomainLimitReached} maxWidth={BODY_TOOLTIP_MAX_WIDTH}>
38+
<div>
39+
<Button
40+
disabled={isDomainLimitReached}
41+
href={isDomainLimitReached
42+
? undefined
43+
: `${base}/project-${page.params.region}-${page.params.project}/functions/function-${page.params.function}/domains/add-domain`}
44+
on:click={() => {
45+
trackEvent(Click.DomainCreateClick, {
46+
source: 'functions_domain_overview'
47+
});
48+
}}>
49+
<Icon icon={IconPlus} size="s" />
50+
Add domain
51+
</Button>
52+
</div>
53+
<svelte:fragment slot="tooltip">
54+
<div style={BODY_TOOLTIP_WRAPPER_STYLE_PRELINE}>
55+
You have reached the maximum number of custom domains for your plan.
56+
</div>
57+
</svelte:fragment>
58+
</Tooltip>
3759
</Layout.Stack>
3860
{#if data.proxyRules.total}
3961
<Table proxyRules={data.proxyRules} organizationDomains={data.organizationDomains} />
@@ -70,12 +92,24 @@
7092
size="s"
7193
ariaLabel="add domain">Documentation</Button>
7294

73-
<Button
74-
secondary
75-
href={`${base}/project-${page.params.region}-${page.params.project}/functions/function-${page.params.function}/domains/add-domain`}
76-
size="s">
77-
Add domain
78-
</Button>
95+
<Tooltip disabled={!isDomainLimitReached} maxWidth={BODY_TOOLTIP_MAX_WIDTH}>
96+
<div>
97+
<Button
98+
secondary
99+
disabled={isDomainLimitReached}
100+
href={isDomainLimitReached
101+
? undefined
102+
: `${base}/project-${page.params.region}-${page.params.project}/functions/function-${page.params.function}/domains/add-domain`}
103+
size="s">
104+
Add domain
105+
</Button>
106+
</div>
107+
<svelte:fragment slot="tooltip">
108+
<div style={BODY_TOOLTIP_WRAPPER_STYLE_PRELINE}>
109+
You have reached the maximum number of custom domains for your plan.
110+
</div>
111+
</svelte:fragment>
112+
</Tooltip>
79113
</svelte:fragment>
80114
</Empty>
81115
</Card.Base>

src/routes/(console)/project-[region]-[project]/settings/domains/+page.svelte

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,23 @@
55
import { Button } from '$lib/elements/forms';
66
import Container from '$lib/layout/container.svelte';
77
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
8-
import { Card, Empty, Icon } from '@appwrite.io/pink-svelte';
8+
import { Card, Empty, Icon, Tooltip } from '@appwrite.io/pink-svelte';
99
import { app } from '$lib/stores/app';
1010
import { Click, trackEvent } from '$lib/actions/analytics';
11+
import {
12+
BODY_TOOLTIP_MAX_WIDTH,
13+
BODY_TOOLTIP_WRAPPER_STYLE_PRELINE
14+
} from '$lib/helpers/tooltipContent';
15+
import { isServiceLimited } from '$lib/stores/billing';
16+
import { organization } from '$lib/stores/organization';
1117
import Table from './table.svelte';
1218
import { ResponsiveContainerHeader } from '$lib/layout';
1319
1420
let { data } = $props();
21+
22+
const isDomainLimitReached = $derived(
23+
isServiceLimited('domains', $organization, data.rules.total)
24+
);
1525
</script>
1626

1727
<Container>
@@ -20,16 +30,28 @@
2030
hideView
2131
searchPlaceholder="Search by domain"
2232
analyticsSource="settings_domain_overview">
23-
<Button
24-
href={`${base}/project-${page.params.region}-${page.params.project}/settings/domains/add-domain`}
25-
on:click={() => {
26-
trackEvent(Click.DomainCreateClick, {
27-
source: 'settings_domain_overview'
28-
});
29-
}}>
30-
<Icon icon={IconPlus} size="s" />
31-
Add domain
32-
</Button>
33+
<Tooltip disabled={!isDomainLimitReached} maxWidth={BODY_TOOLTIP_MAX_WIDTH}>
34+
<div>
35+
<Button
36+
disabled={isDomainLimitReached}
37+
href={isDomainLimitReached
38+
? undefined
39+
: `${base}/project-${page.params.region}-${page.params.project}/settings/domains/add-domain`}
40+
on:click={() => {
41+
trackEvent(Click.DomainCreateClick, {
42+
source: 'settings_domain_overview'
43+
});
44+
}}>
45+
<Icon icon={IconPlus} size="s" />
46+
Add domain
47+
</Button>
48+
</div>
49+
<svelte:fragment slot="tooltip">
50+
<div style={BODY_TOOLTIP_WRAPPER_STYLE_PRELINE}>
51+
You have reached the maximum number of custom domains for your plan.
52+
</div>
53+
</svelte:fragment>
54+
</Tooltip>
3355
</ResponsiveContainerHeader>
3456
{#if data.rules.total}
3557
<Table domains={data.rules} organizationDomains={data.organizationDomains} />
@@ -64,12 +86,24 @@
6486
size="s"
6587
ariaLabel="add domain">Documentation</Button>
6688

67-
<Button
68-
secondary
69-
href={`${base}/project-${page.params.region}-${page.params.project}/settings/domains/add-domain`}
70-
size="s">
71-
Add domain
72-
</Button>
89+
<Tooltip disabled={!isDomainLimitReached} maxWidth={BODY_TOOLTIP_MAX_WIDTH}>
90+
<div>
91+
<Button
92+
secondary
93+
disabled={isDomainLimitReached}
94+
href={isDomainLimitReached
95+
? undefined
96+
: `${base}/project-${page.params.region}-${page.params.project}/settings/domains/add-domain`}
97+
size="s">
98+
Add domain
99+
</Button>
100+
</div>
101+
<svelte:fragment slot="tooltip">
102+
<div style={BODY_TOOLTIP_WRAPPER_STYLE_PRELINE}>
103+
You have reached the maximum number of custom domains for your plan.
104+
</div>
105+
</svelte:fragment>
106+
</Tooltip>
73107
</svelte:fragment>
74108
</Empty>
75109
</Card.Base>

0 commit comments

Comments
 (0)