Merge branch 'main' into supposed-improvements

This commit is contained in:
Darshan
2025-06-03 11:19:11 +05:30
38 changed files with 526 additions and 502 deletions
+1 -1
View File
@@ -26,7 +26,7 @@
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "^2.0.0-RC.1",
"@appwrite.io/pink-legacy": "^1.0.3",
"@appwrite.io/pink-svelte": "https://try-module.cloud/-/@appwrite/@appwrite.io/pink-svelte@d74b893",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/%40appwrite.io%2Fpink-svelte@d7e3b86",
"@popperjs/core": "^2.11.8",
"@sentry/sveltekit": "^8.38.0",
"@stripe/stripe-js": "^3.5.0",
+5 -5
View File
@@ -24,8 +24,8 @@ importers:
specifier: ^1.0.3
version: 1.0.3
'@appwrite.io/pink-svelte':
specifier: https://try-module.cloud/-/@appwrite/@appwrite.io/pink-svelte@d74b893
version: https://try-module.cloud/-/@appwrite/%40appwrite.io%2Fpink-svelte@d74b893(svelte@5.25.3)
specifier: https://pkg.vc/-/@appwrite/%40appwrite.io%2Fpink-svelte@d7e3b86
version: https://pkg.vc/-/@appwrite/%40appwrite.io%2Fpink-svelte@d7e3b86(svelte@5.25.3)
'@popperjs/core':
specifier: ^2.11.8
version: 2.11.8
@@ -281,8 +281,8 @@ packages:
'@appwrite.io/pink-legacy@1.0.3':
resolution: {integrity: sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ==}
'@appwrite.io/pink-svelte@https://try-module.cloud/-/@appwrite/%40appwrite.io%2Fpink-svelte@d74b893':
resolution: {tarball: https://try-module.cloud/-/@appwrite/%40appwrite.io%2Fpink-svelte@d74b893}
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/%40appwrite.io%2Fpink-svelte@d7e3b86':
resolution: {tarball: https://pkg.vc/-/@appwrite/%40appwrite.io%2Fpink-svelte@d7e3b86}
version: 2.0.0-RC.2
peerDependencies:
svelte: ^4.0.0
@@ -3654,7 +3654,7 @@ snapshots:
'@appwrite.io/pink-icons': 1.0.0
the-new-css-reset: 1.11.3
'@appwrite.io/pink-svelte@https://try-module.cloud/-/@appwrite/%40appwrite.io%2Fpink-svelte@d74b893(svelte@5.25.3)':
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/%40appwrite.io%2Fpink-svelte@d7e3b86(svelte@5.25.3)':
dependencies:
'@appwrite.io/pink-icons-svelte': 2.0.0-RC.1(svelte@5.25.3)
'@floating-ui/dom': 1.6.13
@@ -0,0 +1,37 @@
<script lang="ts">
import type { Coupon } from '$lib/sdk/billing';
import { formatCurrency } from '$lib/helpers/numbers';
import { IconTag } from '@appwrite.io/pink-icons-svelte';
import { Badge, Icon, Layout, Typography } from '@appwrite.io/pink-svelte';
export let label: string;
export let value: number;
export let couponData: Partial<Coupon> = {
code: null,
status: null,
credits: null
};
let adjustedValue = value;
$: if (label?.toLowerCase() === 'credits' && couponData?.status === 'active') {
adjustedValue = value - (couponData?.credits || 0);
} else {
adjustedValue = value;
}
</script>
{#if adjustedValue > 0}
<Layout.Stack direction="row" justifyContent="space-between">
<Layout.Stack inline direction="row" gap="xxs" alignItems="center" alignContent="center">
<Icon icon={IconTag} color="--fgcolor-success" size="s" />
<Typography.Text color="--fgcolor-neutral-primary">{label}</Typography.Text>
</Layout.Stack>
{#if value >= 100}
<Badge variant="secondary" content="Credits applied" />
{:else}
<Typography.Text color="--fgcolor-success"
>-{formatCurrency(adjustedValue)}</Typography.Text>
{/if}
</Layout.Stack>
{/if}
@@ -7,6 +7,7 @@
import { CreditsApplied } from '.';
import { sdk } from '$lib/stores/sdk';
import { AppwriteException } from '@appwrite.io/console';
import DiscountsApplied from './discountsApplied.svelte';
export let billingPlan: Tier;
export let collaborators: string[];
@@ -35,7 +36,8 @@
if (
e.type === 'billing_coupon_not_found' ||
e.type === 'billing_coupon_already_used' ||
e.type === 'billing_credit_unsupported'
e.type === 'billing_credit_unsupported' ||
e.type === 'billing_coupon_not_eligible'
) {
couponData = {
code: null,
@@ -65,7 +67,8 @@
if (
e.type === 'billing_coupon_not_found' ||
e.type === 'billing_coupon_already_used' ||
e.type === 'billing_credit_unsupported'
e.type === 'billing_credit_unsupported' ||
e.type === 'billing_coupon_not_eligible'
) {
couponData = {
code: null,
@@ -80,13 +83,6 @@
$: organizationId
? getUpdatePlanEstimate(organizationId, billingPlan, collaborators, couponData?.code)
: getEstimate(billingPlan, collaborators, couponData?.code);
$: estimatedTotal =
couponData?.status === 'active'
? estimation?.grossAmount - couponData.credits >= 0
? estimation?.grossAmount - couponData.credits
: 0
: estimation?.grossAmount;
</script>
{#if estimation}
@@ -102,6 +98,9 @@
>{formatCurrency(item.value)}</Typography.Text>
</Layout.Stack>
{/each}
{#each estimation.discounts ?? [] as item}
<DiscountsApplied {couponData} {...item} />
{/each}
{#if couponData?.status === 'active'}
<CreditsApplied bind:couponData {fixedCoupon} />
@@ -110,15 +109,15 @@
<Layout.Stack direction="row" justifyContent="space-between">
<Typography.Text>Total due</Typography.Text>
<Typography.Text>
{formatCurrency(estimatedTotal)}
{formatCurrency(estimation.grossAmount)}
</Typography.Text>
</Layout.Stack>
<Typography.Text>
You'll pay <b>{formatCurrency(estimatedTotal)}</b>
You'll pay <b>{formatCurrency(estimation.grossAmount)}</b>
now.
{#if couponData?.code}Once your credits run out,{:else}Then{/if} you'll be charged
<b>{formatCurrency(estimation.grossAmount)}</b> every 30 days.
<b>{formatCurrency(estimation.amount)}</b> every 30 days.
</Typography.Text>
<InputChoice
+5 -1
View File
@@ -97,7 +97,11 @@
<Layout.Stack gap="s" alignContent="flex-start">
<!-- `Raw time` as per design -->
<Typography.Caption color="--fgcolor-neutral-tertiary" variant="400">
{timeToString}
{#if $$slots.title}
<slot name="title" />
{:else}
{timeToString}
{/if}
</Typography.Caption>
<!-- `Absolute time` as per design -->
+6 -1
View File
@@ -129,5 +129,10 @@
bind:value={expirationSelect} />
{#if expirationSelect === 'custom'}
<InputDateTime required id="expire" label={dateSelectorLabel} bind:value={expirationCustom} />
<InputDateTime
required
type="date"
id="expire"
label={dateSelectorLabel}
bind:value={expirationCustom} />
{/if}
+37 -28
View File
@@ -5,6 +5,7 @@
import { sdk } from '$lib/stores/sdk';
import { repositories } from '$routes/(console)/project-[region]-[project]/functions/function-[function]/store';
import { installation, installations, repository } from '$lib/stores/vcs';
import { isSmallViewport } from '$lib/stores/viewport';
import {
Layout,
Table,
@@ -49,7 +50,9 @@
async function loadInstallations() {
if (installationList) {
if (installationList.installations.length) {
untrack(() => (selectedInstallation = installationList.installations[0].$id));
if (!selectedInstallation) {
untrack(() => (selectedInstallation = installationList.installations[0].$id));
}
installation.set(
installationList.installations.find(
(entry) => entry.$id === selectedInstallation
@@ -62,7 +65,9 @@
.forProject(page.params.region, page.params.project)
.vcs.listInstallations();
if (installations.length) {
untrack(() => (selectedInstallation = installations[0].$id));
if (!selectedInstallation) {
untrack(() => (selectedInstallation = installationList.installations[0].$id));
}
installation.set(installations.find((entry) => entry.$id === selectedInstallation));
}
return installations;
@@ -133,7 +138,7 @@
<InputSearch placeholder="Search repositories" disabled />
</Layout.Stack>
{:then installations}
<Layout.Stack direction="row">
<Layout.Stack direction={$isSmallViewport ? 'column' : 'row'}>
<InputSelect
id="installation"
options={[
@@ -233,39 +238,43 @@
<Layout.Stack
gap="s"
direction="row"
alignItems="center">
<Typography.Text
truncate
color="--fgcolor-neutral-secondary">
{repo.name}
</Typography.Text>
{#if repo.private}
<Icon
size="s"
icon={IconLockClosed}
color="--fgcolor-neutral-tertiary" />
{/if}
<time datetime={repo.pushedAt}>
<Typography.Caption
variant="400"
truncate
color="--fgcolor-neutral-tertiary">
{timeFromNow(repo.pushedAt)}
</Typography.Caption>
</time>
</Layout.Stack>
{#if action === 'button'}
alignItems="center"
justifyContent="space-between">
<Layout.Stack
direction="row"
justifyContent="flex-end">
gap="s"
alignItems="center">
<Typography.Text
truncate
color="--fgcolor-neutral-secondary">
{repo.name}
</Typography.Text>
{#if repo.private}
<Icon
size="s"
icon={IconLockClosed}
color="--fgcolor-neutral-tertiary" />
{/if}
{#if !$isSmallViewport}
<time datetime={repo.pushedAt}>
<Typography.Caption
variant="400"
truncate
color="--fgcolor-neutral-tertiary">
{timeFromNow(repo.pushedAt)}
</Typography.Caption>
</time>
{/if}
</Layout.Stack>
{#if action === 'button'}
<PinkButton.Button
size="xs"
variant="secondary"
on:click={() => connect(repo)}>
Connect
</PinkButton.Button>
</Layout.Stack>
{/if}
{/if}
</Layout.Stack>
</Layout.Stack>
</Table.Cell>
</Table.Row.Base>
+5 -3
View File
@@ -35,6 +35,8 @@
}
}
const none = undefined as never;
// function pagination(page: number, total: number) {
// const pagesShown = 5;
// const start = Math.max(
@@ -56,11 +58,11 @@
{#if !hidePages}
<Pagination
{limit}
page={currentPage}
{total}
type="button"
on:page={handleOptionClick}
createLink={undefined as never} />
createLink={none}
page={currentPage}
on:page={handleOptionClick} />
{:else}
<Layout.Stack direction="row" inline>
<Button.Button
+25 -9
View File
@@ -12,6 +12,7 @@
Layout,
Link,
Selector,
Spinner,
Table,
Typography
} from '@appwrite.io/pink-svelte';
@@ -24,9 +25,10 @@
let search = '';
let offset = 0;
let results: Models.TeamList<Record<string, unknown>>;
let selected: Set<string> = new Set();
let isLoading = false;
let hasSelection = false;
let selected: Set<string> = new Set();
let results: Models.TeamList<Record<string, unknown>>;
function reset() {
offset = 0;
@@ -41,9 +43,11 @@
async function request() {
if (!show) return;
isLoading = true;
results = await sdk
.forProject(page.params.region, page.params.project)
.teams.list([Query.limit(5), Query.offset(offset)], search || undefined);
isLoading = false;
}
function onSelection(role: string) {
@@ -71,23 +75,24 @@
</script>
<Modal title="Select teams" bind:show onSubmit={create} on:close={reset}>
<Typography.Text
<Typography.Text slot="description"
>Grant access to any member of a specific team. To grant access to team members with
specific roles, you will need to set a <Link.Button on:click={() => dispatch('custom')}
>custom permission</Link.Button
>.</Typography.Text>
<InputSearch autofocus placeholder="Search by name or ID" bind:value={search} />
{#if results?.teams?.length}
<Table.Root columns={[{ id: 'checkbox', width: 40 }, { id: 'team' }]} let:root>
<Table.Root columns={[{ id: 'checkbox', width: 20 }, { id: 'team' }]} let:root>
{#each results.teams as team (team.$id)}
{@const role = `team:${team.$id}`}
{@const exists = $groups.has(role)}
<Table.Row.Button {root} on:click={() => onSelection(role)} disabled={exists}>
<Table.Cell column="checkbox" {root}>
<Selector.Checkbox
size="s"
id={team.$id}
checked={exists || selected.has(role)}
disabled={exists} />
disabled={exists}
checked={exists || selected.has(role)} />
</Table.Cell>
<Table.Cell column="team" {root}>
<Layout.Stack direction="row" alignItems="center" gap="s">
@@ -105,10 +110,16 @@
</Table.Row.Button>
{/each}
</Table.Root>
<div class="u-flex u-margin-block-start-32 u-main-space-between">
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
<p class="text">Total results: {results?.total}</p>
<PaginationInline limit={5} bind:offset total={results?.total} hidePages />
</div>
<PaginationInline
limit={5}
bind:offset
total={results?.total}
hidePages
on:change={request} />
</Layout.Stack>
{:else if search}
<EmptySearch bind:search target="teams" hidePages>
<Button
@@ -119,6 +130,11 @@
size="s">Documentation</Button>
<Button secondary on:click={() => (search = '')}>Clear search</Button>
</EmptySearch>
{:else if isLoading}
<!-- 275px nearly matches the height of at-least 5 items in the table above -->
<div style:margin-inline="auto" style:min-height="275px" style:align-content="center">
<Spinner size="m" />
</div>
{:else}
<Card.Base padding="none">
<Empty title="You have no teams. Create a team to see them here." type="secondary">
+23 -6
View File
@@ -14,6 +14,7 @@
Layout,
Link,
Selector,
Spinner,
Table,
Typography
} from '@appwrite.io/pink-svelte';
@@ -27,9 +28,10 @@
let search = '';
let offset = 0;
let results: Models.UserList<Record<string, unknown>>;
let selected: Set<string> = new Set();
let isLoading = false;
let hasSelection = false;
let selected: Set<string> = new Set();
let results: Models.UserList<Record<string, unknown>>;
function reset() {
offset = 0;
@@ -44,9 +46,11 @@
async function request() {
if (!show) return;
isLoading = true;
results = await sdk
.forProject(page.params.region, page.params.project)
.users.list([Query.limit(5), Query.offset(offset)], search || undefined);
isLoading = false;
}
function onSelection(role: string) {
@@ -74,8 +78,11 @@
</script>
<Modal title="Select users" bind:show onSubmit={create} on:close={reset}>
<Typography.Text>Grant access to any authenticated or anonymous user.</Typography.Text>
<Typography.Text slot="description"
>Grant access to any authenticated or anonymous user.</Typography.Text>
<InputSearch autofocus placeholder="Search by name, email, phone or ID" bind:value={search} />
{#if results?.users?.length}
<Table.Root columns={[{ id: 'checkbox', width: 40 }, { id: 'user' }]} let:root>
{#each results.users as user (user.$id)}
@@ -86,8 +93,8 @@
<Selector.Checkbox
size="s"
id={user.$id}
checked={exists || selected.has(role)}
disabled={exists} />
disabled={exists}
checked={exists || selected.has(role)} />
</Table.Cell>
<Table.Cell column="user" {root}>
<Layout.Stack direction="row" alignItems="center" gap="s">
@@ -141,7 +148,12 @@
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
<p class="text">Total results: {results?.total}</p>
<PaginationInline limit={5} bind:offset total={results?.total} hidePages />
<PaginationInline
limit={5}
bind:offset
total={results?.total}
hidePages
on:change={request} />
</Layout.Stack>
{:else if search}
<EmptySearch bind:search target="users" hidePages>
@@ -153,6 +165,11 @@
size="s">Documentation</Button>
<Button secondary on:click={() => (search = '')}>Clear search</Button>
</EmptySearch>
{:else if isLoading}
<!-- 275px nearly matches the height of at-least 5 items in the table above -->
<div style:margin-inline="auto" style:min-height="275px" style:align-content="center">
<Spinner size="m" />
</div>
{:else}
<Card.Base padding="none">
<Empty title="You have no users. Create a user to see them here." type="secondary">
+7 -1
View File
@@ -12,6 +12,7 @@
export let autofocus = false;
export let autocomplete = false;
export let step: number | 'any' = 0.001;
export let type: 'date' | 'time' | 'datetime-local' = 'date';
let error: string;
let element: HTMLInputElement;
@@ -36,6 +37,10 @@
$: if (value) {
error = null;
}
function onChange(event: CustomEvent) {
value = (event.target as HTMLInputElement).value;
}
</script>
<Layout.Stack gap="s" direction="row">
@@ -47,8 +52,9 @@
{required}
{value}
{step}
{type}
helper={error}
on:change={(event) => (value = (event.target as HTMLInputElement).value)}
on:change={onChange}
autocomplete={autocomplete ? 'on' : 'off'}>
{#if nullable}
<Selector.Checkbox
+99
View File
@@ -90,6 +90,105 @@ export async function createRecord(record: Partial<Models.DnsRecord>, domainId:
}
}
export async function updateRecord(record: Partial<Models.DnsRecord>, domainId: string) {
switch (record.type) {
case 'A':
return await sdk.forConsole.domains.updateRecordA(
domainId,
record.$id,
record.name,
record.value,
record.ttl,
record.comment
);
case 'AAAA':
return await sdk.forConsole.domains.updateRecordAAAA(
domainId,
record.$id,
record.name,
record.value,
record.ttl,
record.comment
);
case 'CNAME':
return await sdk.forConsole.domains.updateRecordCNAME(
domainId,
record.$id,
record.name,
record.value,
record.ttl,
record.comment
);
case 'MX':
return await sdk.forConsole.domains.updateRecordMX(
domainId,
record.$id,
record.name,
record.value,
record.ttl,
record.priority,
record.comment
);
case 'TXT':
return await sdk.forConsole.domains.updateRecordTXT(
domainId,
record.$id,
record.name,
record.value,
record.ttl,
record.comment
);
case 'NS':
return await sdk.forConsole.domains.updateRecordNS(
domainId,
record.$id,
record.name,
record.value,
record.ttl,
record.comment
);
case 'SRV':
return await sdk.forConsole.domains.updateRecordSRV(
domainId,
record.$id,
record.name,
record.value,
record.ttl,
record.priority,
record.weight,
record.port,
record?.comment || undefined
);
case 'CAA':
return await sdk.forConsole.domains.updateRecordCAA(
domainId,
record.$id,
record.name,
record.value,
record.ttl,
record.comment
);
case 'HTTPS':
return await sdk.forConsole.domains.updateRecordHTTPS(
domainId,
record.$id,
record.name,
record.value,
record.ttl,
record.comment
);
case 'ALIAS':
return await sdk.forConsole.domains.updateRecordAlias(
domainId,
record.$id,
record.name,
record.value,
record.ttl,
record.comment
);
}
}
export type ParsedRecords = {
A: Partial<Models.DnsRecord>[];
AAAA: Partial<Models.DnsRecord>[];
-1
View File
@@ -15,7 +15,6 @@ export function isASubdomain(domain: string | null): boolean {
const { domain: apex, subdomain } = parse(domain);
if (!apex) return false;
if (subdomain === 'www') return false;
return !!subdomain;
}
-30
View File
@@ -1,30 +0,0 @@
import { get } from 'svelte/store';
import { user } from '$lib/stores/user';
import { sdk } from '$lib/stores/sdk';
const userPreferences = () => get(user)?.prefs;
export const joinWaitlistSites = () => {
const prefs = userPreferences();
const newPrefs = {
...prefs,
joinWaitlistSites: true
};
sdk.forConsole.account.updatePrefs(newPrefs);
if (sessionStorage) {
sessionStorage.setItem('joinWaitlistSites', 'true');
}
};
export const isOnWaitlistSites = (): boolean => {
const prefs = userPreferences();
const joinedInPrefs = 'joinWaitlistSites' in prefs;
let joinedInSession = false;
if (sessionStorage) {
joinedInSession = sessionStorage.getItem('joinWaitlistSites') === 'true';
}
return joinedInSession || joinedInPrefs;
};
+4 -1
View File
@@ -111,6 +111,9 @@
let state: undefined | 'open' | 'closed' | 'icons' = 'closed';
$: state = $isSidebarOpen ? 'open' : 'closed';
let isProjectPage;
$: isProjectPage = $page.route?.id?.includes('project-');
function handleResize() {
$isSidebarOpen = false;
showAccountMenu = false;
@@ -155,7 +158,7 @@
<div
class="content"
class:has-transition={showContentTransition}
class:icons-content={state === 'icons'}
class:icons-content={state === 'icons' && isProjectPage}
class:no-sidebar={!showSideNavigation}>
<section class="main-content" data-test={showSideNavigation}>
{#if $page.data?.header}
@@ -1,6 +1,5 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { page } from '$app/state';
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
import { Modal } from '$lib/components';
import { Dependencies } from '$lib/constants';
@@ -28,10 +27,8 @@
let error: string = null;
onMount(async () => {
const countryList = await sdk
.forProject(page.params.region, page.params.project)
.locale.listCountries();
const locale = await sdk.forProject(page.params.region, page.params.project).locale.get();
const countryList = await sdk.forConsole.locale.listCountries();
const locale = await sdk.forConsole.locale.get();
if (locale.countryCode) {
country = locale.countryCode;
}
@@ -28,7 +28,6 @@
Tag,
Typography
} from '@appwrite.io/pink-svelte';
import { page } from '$app/state';
let show = false;
let showEdit = false;
@@ -38,9 +37,7 @@
let countryList: Models.CountryList;
onMount(async () => {
countryList = await sdk
.forProject(page.params.region, page.params.project)
.locale.listCountries();
countryList = await sdk.forConsole.locale.listCountries();
});
$: orgList = $organizationList.teams as unknown as Organization[];
@@ -22,9 +22,7 @@
];
onMount(async () => {
const countryList = await sdk
.forProject(page.params.region, page.params.project)
.locale.listCountries();
const countryList = await sdk.forConsole.locale.listCountries();
options = countryList.countries.map((country) => {
return {
value: country.code,
+26 -16
View File
@@ -77,7 +77,7 @@
$: selectedOrgId = tempOrgId;
function isUpgrade() {
const newPlan = page.data.plansInfo.get(billingPlan);
const newPlan = $plansInfo.get(billingPlan);
return currentPlan && newPlan && currentPlan.order < newPlan.order;
}
@@ -227,34 +227,42 @@
function getBillingPlan(): Tier | undefined {
const campaignPlan =
campaign?.plan && $plansInfo.get(campaign.plan) ? $plansInfo.get(campaign.plan) : null;
const orgPlan =
selectedOrg?.billingPlan && $plansInfo.get(selectedOrg.billingPlan)
? $plansInfo.get(selectedOrg.billingPlan)
: null;
const newPlan = $plansInfo.get(billingPlan);
if (!campaignPlan && !orgPlan) {
return;
// if campaign has a plan and it's higher than the selected new plan
if (campaignPlan?.order > newPlan?.order) {
return campaignPlan.$id as Tier;
}
if (!campaignPlan) {
return selectedOrg?.billingPlan;
// if current plan's order is higher than the selected new plan
if (currentPlan?.order > newPlan?.order) {
return currentPlan.$id as Tier;
}
if (!orgPlan) {
return campaign.plan;
}
return campaignPlan.order > orgPlan.order ? campaign.plan : selectedOrg?.billingPlan;
return billingPlan;
}
$: if (selectedOrg) {
$: if (currentPlan) {
billingPlan = getBillingPlan();
}
$: isNewOrg = selectedOrgId && selectedOrgId !== newOrgId;
$: {
if (selectedOrgId) {
if (isNewOrg) {
(async () => {
currentPlan = await sdk.forConsole.billing.getOrganizationPlan(selectedOrgId);
})();
}
}
// after adding a payment method, fetch the payment methods again so the input can be updated
$: if (
paymentMethodId &&
!methods?.paymentMethods?.find((method) => method.$id === paymentMethodId)
) {
loadPaymentMethods();
}
</script>
<svelte:head>
@@ -363,7 +371,7 @@
{collaborators}
bind:couponData
bind:billingBudget
organizationId={selectedOrgId}>
organizationId={isNewOrg ? selectedOrgId : null}>
{#if campaign?.template === 'review' && (campaign?.cta || campaign?.claimed || campaign?.unclaimed)}
<div class="u-margin-block-end-24">
<p class="body-text-1 u-bold">{campaign?.cta}</p>
@@ -396,6 +404,8 @@
{/if}
{#if selectedOrgId === newOrgId}
Create organization
{:else if isUpgrade()}
Upgrade
{:else}
Apply
{/if}
@@ -19,8 +19,7 @@
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { sdk } from '$lib/stores/sdk';
import { loading } from '$routes/store';
import type { Models } from '@appwrite.io/console';
import { ID, Region } from '@appwrite.io/console';
import { ID, Region, type Models } from '@appwrite.io/console';
import { openImportWizard } from '../project-[region]-[project]/settings/migrations/(import)';
import { readOnly } from '$lib/stores/billing';
import { onMount, type ComponentType } from 'svelte';
@@ -163,23 +162,28 @@
<svelte:fragment slot="title">
{project.name}
</svelte:fragment>
{#each platforms as platform, i}
{#if i < 3}
{@const icon = getIconForPlatform(platform.icon)}
<Badge variant="secondary" content={platform.name}>
<Icon {icon} size="s" slot="start" />
</Badge>
{/if}
{#each platforms.slice(0, 2) as platform}
{@const icon = getIconForPlatform(platform.icon)}
<Badge
variant="secondary"
content={platform.name}
style="width: max-content;">
<Icon {icon} size="s" slot="start" />
</Badge>
{/each}
{#if platforms?.length > 3}
<Badge variant="secondary" content={`+${project.platforms.length - 3}`} />
{#if platforms.length > 3}
<Badge
variant="secondary"
content={`+${platforms.length - 2}`}
style="width: max-content;" />
{/if}
<svelte:fragment slot="icons">
{#if isCloud && $regionsStore?.regions}
{@const region = findRegion(project)}
<span class="u-color-text-gray u-medium u-line-height-2">
{region?.name}
</span>
<Typography.Text>{region.name}</Typography.Text>
{/if}
</svelte:fragment>
</GridItem1>
@@ -302,8 +302,7 @@
</Layout.Stack>
</Fieldset>
<!-- Show email input if upgrading from free plan -->
{#if selectedPlan !== BillingPlan.FREE && data.organization.billingPlan === BillingPlan.FREE}
{#if isUpgrade}
<Fieldset legend="Payment">
<SelectPaymentMethod
methods={data.paymentMethods}
@@ -17,6 +17,7 @@
import { invalidate } from '$app/navigation';
import type { RecordType } from '$lib/stores/domains';
import { createRecord } from '$lib/helpers/domains';
import { showPriority } from './table.svelte';
export let show = false;
@@ -30,6 +31,20 @@
let weight: number;
let port: number;
const placeholders: Record<RecordType, string> = {
A: '76.75.21.21',
AAAA: '2001:0db8:85a3:0000:0000:8a2e:0370:7334',
CNAME: 'stage.example.com',
MX: 'mail.example.com',
TXT: 'v=spf1 include:_spf.example.com ~all',
NS: 'ns1.example.com',
SRV: '10 5 8080 example.com',
CAA: '0 issue "letsencrypt.org"',
HTTPS: 'https://example.com',
ALIAS: 'www.example.com'
};
$: placeholder = placeholders[type] ?? 'Enter value';
async function handleSubmit() {
const record = {
name,
@@ -77,7 +92,7 @@
</Input.Helper>
</Layout.Stack>
<InputText id="value" label="Value" placeholder="76.75.21.21" bind:value>
<InputText id="value" label="Value" {placeholder} bind:value>
<Tooltip slot="info">
<Icon icon={IconInfo} size="s" />
<span slot="tooltip">
@@ -96,7 +111,7 @@
</span>
</Tooltip>
</InputNumber>
{#if type === 'MX'}
{#if showPriority(type)}
<InputNumber
id="priority"
label="Priority"
@@ -5,7 +5,7 @@ import { derived, writable } from 'svelte/store';
export const domain = derived(page, ($page) => $page.data.domain as Models.Domain);
export const columns = writable<Column[]>([
{ id: 'name', title: 'Name', type: 'string', width: { min: 150 } },
{ id: 'name', title: 'Name', type: 'string', width: { min: 200 } },
{ id: 'type', title: 'Type', type: 'string', width: 125 },
{ id: 'value', title: 'Value', type: 'string', width: 250 },
{ id: 'ttl', title: 'TTL', type: 'integer', width: 100 },
@@ -1,3 +1,12 @@
<script lang="ts" context="module">
import type { RecordType } from '$lib/stores/domains';
export function showPriority(record: Models.DnsRecord | RecordType): boolean {
const type = typeof record === 'string' ? record : record.type;
return type.toLowerCase() === 'mx' || type.toLowerCase() === 'srv';
}
</script>
<script lang="ts">
import { PaginationWithLimit } from '$lib/components';
import {
@@ -29,6 +38,15 @@
let showEdit = false;
let showDelete = false;
let selectedRecord: Models.DnsRecord = null;
function formatRecordName(name: string) {
const limit = 30;
return {
value: name.length > limit ? `${name.slice(0, limit)}...` : name,
truncated: name.length > limit,
whole: name
};
}
</script>
<Layout.Stack>
@@ -47,9 +65,16 @@
{#each $columns as column}
<Table.Cell column={column.id} {root}>
{#if column.id === 'name'}
<Typography.Code>
{record.name}
</Typography.Code>
{@const formatted = formatRecordName(record.name)}
<Tooltip placement="bottom" disabled={!formatted.truncated}>
<Typography.Text truncate>{formatted.value}</Typography.Text>
<span
slot="tooltip"
style:white-space="pre-wrap"
style:word-break="break-all">
{formatted.whole}
</span>
</Tooltip>
{:else if column.id === 'type'}
<Typography.Text>
{record.type}
@@ -70,7 +95,7 @@
</Typography.Text>
{:else if column.id === 'priority'}
<Typography.Text>
{record?.priority ?? '-'}
{showPriority(record) ? (record?.priority ?? '0') : '-'}
</Typography.Text>
{:else if column.id === 'comment'}
<Typography.Text truncate>
@@ -13,12 +13,14 @@
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { symmetricDifference } from '$lib/helpers/array';
import { deepClone } from '$lib/helpers/object';
import { sdk } from '$lib/stores/sdk';
import { page } from '$app/state';
import { recordTypes } from './store';
import { invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
import type { Models } from '@appwrite.io/console';
import type { RecordType } from '$lib/stores/domains';
import { updateRecord } from '$lib/helpers/domains';
import { showPriority } from './table.svelte';
export let show = false;
export let selectedRecord: Models.DnsRecord;
@@ -26,114 +28,23 @@
let record = deepClone(selectedRecord);
let error = '';
const placeholders: Record<RecordType, string> = {
A: '76.75.21.21',
AAAA: '2001:0db8:85a3:0000:0000:8a2e:0370:7334',
CNAME: 'stage.example.com',
MX: 'mail.example.com',
TXT: 'v=spf1 include:_spf.example.com ~all',
NS: 'ns1.example.com',
SRV: '10 5 8080 example.com',
CAA: '0 issue "letsencrypt.org"',
HTTPS: 'https://example.com',
ALIAS: 'www.example.com'
};
$: placeholder = placeholders[record.type] ?? 'Enter value';
async function handleSubmit() {
try {
switch (record.type) {
case 'A':
await sdk.forConsole.domains.updateRecordA(
page.params.domain,
record.$id,
record.name,
record.value,
record.ttl,
record.comment
);
break;
case 'AAAA':
await sdk.forConsole.domains.updateRecordAAAA(
page.params.domain,
record.$id,
record.name,
record.value,
record.ttl,
record.comment
);
break;
case 'CNAME':
await sdk.forConsole.domains.updateRecordCNAME(
page.params.domain,
record.$id,
record.name,
record.value,
record.ttl,
record.comment
);
break;
case 'MX':
await sdk.forConsole.domains.updateRecordMX(
page.params.domain,
record.$id,
record.name,
record.value,
record.ttl,
record.priority,
record.comment
);
break;
case 'TXT':
await sdk.forConsole.domains.updateRecordTXT(
page.params.domain,
record.$id,
record.name,
record.value,
record.ttl,
record.comment
);
break;
case 'NS':
await sdk.forConsole.domains.updateRecordNS(
page.params.domain,
record.$id,
record.name,
record.value,
record.ttl,
record.comment
);
break;
case 'CAA':
await sdk.forConsole.domains.updateRecordCAA(
page.params.domain,
record.$id,
record.name,
record.value,
record.ttl,
record.comment
);
break;
case 'HTTPS':
await sdk.forConsole.domains.updateRecordHTTPS(
page.params.domain,
record.$id,
record.name,
record.value,
record.ttl,
record.comment
);
break;
case 'ALIAS':
await sdk.forConsole.domains.updateRecordAlias(
page.params.domain,
record.$id,
record.name,
record.value,
record.ttl,
record.comment
);
break;
default:
break;
}
await updateRecord(record, page.params.domain);
show = false;
invalidate(Dependencies.DOMAINS);
addNotification({
@@ -173,7 +84,7 @@
</Input.Helper>
</Layout.Stack>
<InputText id="value" label="Value" placeholder="76.75.21.21" bind:value={record.value}>
<InputText id="value" label="Value" {placeholder} bind:value={record.value}>
<Tooltip slot="info">
<Icon icon={IconInfo} size="s" />
<span slot="tooltip">
@@ -191,19 +102,21 @@
</span>
</Tooltip>
</InputNumber>
<InputNumber
id="priority"
label="Priority"
placeholder="Enter number"
bind:value={record.priority}>
<Tooltip slot="info">
<Icon icon={IconInfo} size="s" />
<span slot="tooltip">
Sets the priority for this DNS record. Lower numbers indicate higher
priority (e.g., 10 is higher than 20).
</span>
</Tooltip>
</InputNumber>
{#if showPriority(record)}
<InputNumber
id="priority"
label="Priority"
placeholder="Enter number"
bind:value={record.priority}>
<Tooltip slot="info">
<Icon icon={IconInfo} size="s" />
<span slot="tooltip">
Sets the priority for this DNS record. Lower numbers indicate higher
priority (e.g., 10 is higher than 20).
</span>
</Tooltip>
</InputNumber>
{/if}
</Layout.Stack>
<InputTextarea
@@ -6,11 +6,13 @@
export let label: string;
export let value: string;
export let attribute: Models.AttributeDatetime;
export let type: 'date' | 'time' | 'datetime-local' = 'datetime-local';
</script>
<InputDateTime
{id}
{label}
{type}
bind:value
required={attribute.required}
nullable={!attribute.required}
bind:value />
nullable={!attribute.required} />
@@ -25,6 +25,7 @@
FloatingActionBar,
Typography
} from '@appwrite.io/pink-svelte';
import { toLocaleDateTime } from '$lib/helpers/date';
import DualTimeView from '$lib/components/dualTimeView.svelte';
export let data: PageData;
@@ -244,17 +245,23 @@
{/if}
{:else}
{@const formatted = formatColumn(document[id])}
<Tooltip disabled={!formatted.truncated} placement="bottom">
<Typography.Text truncate>
{formatted.value}
</Typography.Text>
<span
style:white-space="pre-wrap"
style:word-break="break-all"
slot="tooltip">
{formatted.whole}
</span>
</Tooltip>
{@const isDatetimeAttribute = attr.type === 'datetime'}
{#if isDatetimeAttribute}
<DualTimeView time={formatted.whole}>
<span slot="title">Timestamp</span>
{toLocaleDateTime(formatted.whole, true, 'UTC')}
</DualTimeView>
{:else}
<Tooltip placement="bottom" disabled={!formatted.truncated}>
<Typography.Text truncate>{formatted.value}</Typography.Text>
<span
slot="tooltip"
style:white-space="pre-wrap"
style:word-break="break-all">
{formatted.whole}
</span>
</Tooltip>
{/if}
{/if}
</Table.Cell>
{/each}
@@ -18,7 +18,7 @@
import { ConnectRepoModal } from '$lib/components/git/index.js';
import { isValueOfStringEnum } from '$lib/helpers/types.js';
import { isCloud } from '$lib/system';
import { project, regionalProtocol } from '$routes/(console)/project-[region]-[project]/store';
import { project } from '$routes/(console)/project-[region]-[project]/store';
import { getApexDomain } from '$lib/helpers/tlds';
const routeBase = `${base}/project-${page.params.region}-${page.params.project}/functions/function-${page.params.function}/domains`;
@@ -72,7 +72,7 @@
} else if (behaviour === 'REDIRECT') {
rule = await sdk
.forProject(page.params.region, page.params.project)
.proxy.createRedirectRule(domainName, $regionalProtocol + redirect, statusCode);
.proxy.createRedirectRule(domainName, redirect, statusCode);
} else if (behaviour === 'ACTIVE') {
rule = await sdk
.forProject(page.params.region, page.params.project)
@@ -57,10 +57,8 @@ const val APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.region, page.
trackEvent(Submit.PlatformCreate, {
type: 'android'
});
await Promise.all([
invalidate(Dependencies.PROJECT),
invalidate(Dependencies.PLATFORMS)
]);
invalidate(Dependencies.PROJECT);
invalidate(Dependencies.PLATFORMS);
} catch (error) {
trackError(error, Submit.PlatformCreate);
addNotification({
@@ -4,6 +4,7 @@
import { createPlatform } from './wizard/store';
import { Dependencies } from '$lib/constants';
import {
Card as Pink2Card,
Code,
Layout,
Icon,
@@ -26,7 +27,6 @@
import { PlatformType } from '@appwrite.io/console';
import { isCloud } from '$lib/system';
import { app } from '$lib/stores/app';
import { LabelCard } from '$lib/components';
let showExitModal = false;
let isPlatformCreated = false;
@@ -68,10 +68,8 @@ APPWRITE_PUBLIC_ENDPOINT: "${sdk.forProject(page.params.region, page.params.proj
trackEvent(Submit.PlatformCreate, {
type: platform
});
await Promise.all([
invalidate(Dependencies.PROJECT),
invalidate(Dependencies.PLATFORMS)
]);
invalidate(Dependencies.PROJECT);
invalidate(Dependencies.PLATFORMS);
} catch (error) {
trackError(error, Submit.PlatformCreate);
addNotification({
@@ -108,20 +106,18 @@ APPWRITE_PUBLIC_ENDPOINT: "${sdk.forProject(page.params.region, page.params.proj
<Form onSubmit={createApplePlatform}>
<Layout.Stack gap="xxl">
<!-- Step One -->
<Layout.Stack gap="l" direction="row">
<Layout.Grid gap="l" rowGap="l" columns={4} columnsXS={2}>
{#each Object.entries(platforms) as [key, value]}
<div class="u-width-full-line">
<!-- TODO: https://github.com/appwrite/pink/pull/248 for correct spacing -->
<LabelCard
name={key}
bind:group={platform}
variant="primary"
{value}
title={key}
disabled={isPlatformCreated} />
</div>
<Pink2Card.Selector
{value}
id={key}
title={key}
imageRadius="s"
name="framework"
bind:group={platform}
disabled={isCreatingPlatform || isPlatformCreated} />
{/each}
</Layout.Stack>
</Layout.Grid>
<!-- Step Two -->
{#if !isPlatformCreated}
@@ -4,6 +4,7 @@
import { createPlatform } from './wizard/store';
import { Dependencies } from '$lib/constants';
import {
Card as Pink2Card,
Code,
Layout,
Icon,
@@ -25,7 +26,6 @@
import OnboardingPlatformCard from './components/OnboardingPlatformCard.svelte';
import { PlatformType } from '@appwrite.io/console';
import { isCloud } from '$lib/system';
import { LabelCard } from '$lib/components';
let showExitModal = false;
let isPlatformCreated = false;
@@ -125,10 +125,8 @@ static const String APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.reg
type: platform
});
await Promise.all([
invalidate(Dependencies.PROJECT),
invalidate(Dependencies.PLATFORMS)
]);
invalidate(Dependencies.PROJECT);
invalidate(Dependencies.PLATFORMS);
} catch (error) {
trackError(error, Submit.PlatformCreate);
addNotification({
@@ -165,19 +163,18 @@ static const String APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.reg
<Form onSubmit={createFlutterPlatform}>
<Layout.Stack gap="xxl">
<!-- Step One -->
<Layout.Stack gap="l" direction="row">
<Layout.Grid gap="l" rowGap="l" columns={3} columnsXS={2} columnsXXS={1}>
{#each Object.entries(platforms) as [key, value]}
<div class="u-width-full-line">
<!-- TODO: https://github.com/appwrite/pink/pull/248 for correct spacing -->
<LabelCard
name={key}
bind:group={platform}
variant="primary"
{value}
title={key} />
</div>
<Pink2Card.Selector
{value}
id={key}
title={key}
imageRadius="s"
name="framework"
bind:group={platform}
disabled={isCreatingPlatform || isPlatformCreated} />
{/each}
</Layout.Stack>
</Layout.Grid>
<!-- Step Two -->
{#if !isPlatformCreated}
@@ -4,6 +4,7 @@
import { createPlatform } from './wizard/store';
import { Dependencies } from '$lib/constants';
import {
Card as Pink2Card,
Code,
Layout,
Icon,
@@ -25,10 +26,9 @@
import OnboardingPlatformCard from './components/OnboardingPlatformCard.svelte';
import { PlatformType } from '@appwrite.io/console';
import { isCloud } from '$lib/system';
import { LabelCard } from '$lib/components';
let showExitModal = false;
export let isPlatformCreated = false;
let isPlatformCreated = false;
let isCreatingPlatform = false;
let connectionSuccessful = false;
const projectId = page.params.project;
@@ -94,10 +94,9 @@ const APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.region, page.para
trackEvent(Submit.PlatformCreate, {
type: platform
});
await Promise.all([
invalidate(Dependencies.PROJECT),
invalidate(Dependencies.PLATFORMS)
]);
invalidate(Dependencies.PROJECT);
invalidate(Dependencies.PLATFORMS);
} catch (error) {
trackError(error, Submit.PlatformCreate);
addNotification({
@@ -136,16 +135,14 @@ const APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.region, page.para
<!-- Step One -->
<Layout.Stack gap="l" direction="row">
{#each Object.entries(platforms) as [key, value]}
<div class="u-width-full-line">
<!-- TODO: https://github.com/appwrite/pink/pull/248 for correct spacing -->
<LabelCard
name={key}
bind:group={platform}
variant="primary"
{value}
title={key}
disabled={isPlatformCreated} />
</div>
<Pink2Card.Selector
{value}
id={key}
title={key}
imageRadius="s"
name="framework"
bind:group={platform}
disabled={isCreatingPlatform || isPlatformCreated} />
{/each}
</Layout.Stack>
@@ -11,10 +11,9 @@
Fieldset,
InlineCode,
Card,
Button,
Tooltip
} from '@appwrite.io/pink-svelte';
import { Form, InputText } from '$lib/elements/forms';
import { Button, Form, InputText } from '$lib/elements/forms';
import {
IconVue,
IconAppwrite,
@@ -166,10 +165,8 @@ ${prefix}APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.p
type: platform
});
await Promise.all([
invalidate(Dependencies.PROJECT),
invalidate(Dependencies.PLATFORMS)
]);
invalidate(Dependencies.PROJECT);
invalidate(Dependencies.PLATFORMS);
} catch (error) {
trackError(error, Submit.PlatformCreate);
addNotification({
@@ -223,10 +220,10 @@ ${prefix}APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.p
</div>
<Layout.Stack direction="row" justifyContent="flex-end">
{#if isChangingFramework}
<Button.Button
<Button
disabled={!selectedFramework}
on:click={() => (isChangingFramework = false)}>
Save</Button.Button>
Save</Button>
{/if}
</Layout.Stack>
</Layout.Stack>
@@ -249,8 +246,7 @@ ${prefix}APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.p
</Tooltip>
</InputText></Fieldset>
<Layout.Stack direction="row" justifyContent="flex-end"
><Button.Button type="submit" disabled={!selectedFramework}
>Create platform</Button.Button
><Button submit disabled={!selectedFramework}>Create platform</Button
></Layout.Stack>
{/if}
{:else}
@@ -264,12 +260,12 @@ ${prefix}APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.p
<Typography.Text variant="m-500"
>{selectedFramework.label}</Typography.Text>
</Layout.Stack>
<Button.Button
variant="secondary"
<Button
size="s"
secondary
on:click={() => {
isChangingFramework = true;
}}>Change</Button.Button>
}}>Change</Button>
</Layout.Stack></Card.Base>
{/if}
@@ -338,15 +334,15 @@ ${prefix}APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.p
color="--fgcolor-neutral-primary">
Demo app runs on http://localhost:{selectedFramework.portNumber}</Typography.Text
></Layout.Stack>
<Button.Anchor
variant="secondary"
<Button
external
secondary
href={`http://localhost:${selectedFramework.portNumber}`}
target="_blank"
><Layout.Stack direction="row" gap="xs"
>Open <Icon
icon={IconExternalLink}
color="--fgcolor-neutral-tertiary" /></Layout.Stack
></Button.Anchor
></Button
></Layout.Stack
></Card.Base>
{/if}
@@ -394,10 +390,12 @@ ${prefix}APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.p
<svelte:fragment slot="footer">
{#if isPlatformCreated}
<Button.Anchor
<Button
size="s"
secondary
fullWidthMobile
href={location.pathname}
variant="secondary"
disabled={isCreatingPlatform}>Go to dashboard</Button.Anchor>
disabled={isCreatingPlatform}>Go to dashboard</Button>
{/if}
</svelte:fragment>
</Wizard>
@@ -11,14 +11,12 @@
import { isServiceLimited } from '$lib/stores/billing';
import { organization } from '$lib/stores/organization';
import { canWriteSites } from '$lib/stores/roles.js';
import { Card, Icon, Layout, Typography } from '@appwrite.io/pink-svelte';
import { Icon, Layout } 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';
@@ -28,15 +26,10 @@
import { invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
import { sdk } from '$lib/stores/sdk';
import { APPWRITE_OFFICIALS_ORG, isCloud } from '$lib/system';
import { addNotification } from '$lib/stores/notifications';
import { isOnWaitlistSites, joinWaitlistSites } from '$lib/helpers/waitlist';
import { isSmallViewport } from '$lib/stores/viewport';
export let data;
let show = false;
let isOnWaitlist = isOnWaitlistSites();
$: $registerCommands([
{
@@ -46,7 +39,6 @@
},
keys: ['c'],
disabled:
!showSites ||
isServiceLimited('sites', $organization?.billingPlan, data.siteList?.total) ||
!$canWriteSites,
icon: IconPlus,
@@ -63,123 +55,46 @@
}
});
});
/**
* Controls visibility of Sites feature:
* - Shown if running on self-hosted
* - Shown on cloud only if the organization is Appwrite's.
* - Hidden on cloud for any non-Appwrite organization.
*/
$: showSites = !isCloud || $organization.$id === APPWRITE_OFFICIALS_ORG;
$: isDark = $app.themeInUse === 'dark';
$: imgSrc = isDark
? $isSmallViewport
? EmptyDarkMobile
: EmptyDark
: $isSmallViewport
? EmptyLightMobile
: EmptyLight;
$: imgClass = $isSmallViewport ? 'mobile' : 'desktop';
function addToWaitlist() {
joinWaitlistSites();
addNotification({
type: 'success',
title: 'Waitlist joined',
message: "We'll let you know as soon as Appwrite Sites is ready for you."
});
isOnWaitlist = true;
}
</script>
<Container>
{#if showSites}
<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}
</Layout.Stack>
<Layout.Stack direction="row" justifyContent="space-between">
<Layout.Stack direction="row" alignItems="center">
<SearchQuery placeholder="Search by name" />
</Layout.Stack>
{#if data.siteList.total}
{#if data.view === View.Grid}
<Grid siteList={data.siteList} />
{:else}
<Table siteList={data.siteList} />
<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}
<PaginationWithLimit
name="Sites"
limit={data.limit}
offset={data.offset}
total={data.siteList.total} />
{:else if data.search}
<EmptySearch target="sites" />
</Layout.Stack>
</Layout.Stack>
{#if data.siteList.total}
{#if data.view === View.Grid}
<Grid siteList={data.siteList} />
{: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>
<Table siteList={data.siteList} />
{/if}
<PaginationWithLimit
name="Sites"
limit={data.limit}
offset={data.offset}
total={data.siteList.total} />
{:else if data.search}
<EmptySearch target="sites" />
{:else}
<Card.Base padding="m">
<Layout.Stack gap="xxl">
<img src={imgSrc} alt="create" aria-hidden="true" height="242" class={imgClass} />
<Layout.Stack>
{#if isOnWaitlist}
<Typography.Title size="s" align="center" color="--fgcolor-neutral-primary">
You've successfully joined the Sites waitlist
</Typography.Title>
<Typography.Text align="center" color="--fgcolor-neutral-secondary">
We can't wait for you to try out Sites on Cloud. You will get access
soon.
</Typography.Text>
{:else}
<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, were rolling out
access gradually. Join the waitlist and be one of the first to
deploy with Sites.
</Typography.Text>
</div>
<div style:margin-block-start="1rem">
<Button on:click={addToWaitlist}>Join waitlist</Button>
</div>
</Layout.Stack>
{/if}
</Layout.Stack>
</Layout.Stack>
</Card.Base>
<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}
</Container>
@@ -2,15 +2,8 @@ import { Query } from '@appwrite.io/console';
import { sdk } from '$lib/stores/sdk';
import { getLimit, getPage, getSearch, getView, pageToOffset, View } from '$lib/helpers/load';
import { CARD_LIMIT, Dependencies } from '$lib/constants';
import { APPWRITE_OFFICIALS_ORG, isCloud } from '$lib/system';
export const load = async ({ url, depends, route, params, parent }) => {
// don't load anything on cloud unless org is appwrite atm!
const { organization } = await parent();
if (isCloud && organization?.$id !== APPWRITE_OFFICIALS_ORG) {
return;
}
export const load = async ({ url, depends, route, params }) => {
depends(Dependencies.SITES);
const page = getPage(url);
@@ -2,17 +2,9 @@ import Breadcrumbs from './breadcrumbs.svelte';
import Header from './header.svelte';
import { Dependencies } from '$lib/constants';
import { sdk } from '$lib/stores/sdk';
import { error, redirect } from '@sveltejs/kit';
import { APPWRITE_OFFICIALS_ORG, isCloud } from '$lib/system';
import { base } from '$app/paths';
export const load = async ({ depends, params, parent }) => {
// don't load anything on cloud unless org is appwrite atm!
const { organization } = await parent();
if (isCloud && organization?.$id !== APPWRITE_OFFICIALS_ORG) {
redirect(307, `${base}/project-${params.region}-${params.project}/sites`);
}
import { error } from '@sveltejs/kit';
export const load = async ({ depends, params }) => {
depends(Dependencies.SITE);
try {
const [site] = await Promise.all([
@@ -27,12 +27,17 @@ export const load = async ({ params, depends, url, route }) => {
.proxy.listRules(
[
Query.or([
Query.equal('type', RuleType.DEPLOYMENT),
Query.equal('type', RuleType.REDIRECT)
Query.and([
Query.equal('type', RuleType.REDIRECT),
Query.equal('trigger', RuleTrigger.MANUAL)
]),
Query.and([
Query.equal('type', RuleType.DEPLOYMENT),
Query.equal('trigger', RuleTrigger.MANUAL),
Query.equal('deploymentResourceType', DeploymentResourceType.SITE),
Query.equal('deploymentResourceId', params.site)
])
]),
Query.equal('deploymentResourceType', DeploymentResourceType.SITE),
Query.equal('deploymentResourceId', params.site),
Query.equal('trigger', RuleTrigger.MANUAL),
Query.limit(limit),
Query.offset(offset),
Query.orderDesc(''),
@@ -22,7 +22,7 @@
import { writable } from 'svelte/store';
import { onMount } from 'svelte';
import { ConnectRepoModal } from '$lib/components/git/index.js';
import { project, regionalProtocol } from '$routes/(console)/project-[region]-[project]/store';
import { project } from '$routes/(console)/project-[region]-[project]/store';
import { isCloud } from '$lib/system';
import { getApexDomain } from '$lib/helpers/tlds';
@@ -76,7 +76,7 @@
} else if (behaviour === 'REDIRECT') {
rule = await sdk
.forProject(page.params.region, page.params.project)
.proxy.createRedirectRule(domainName, $regionalProtocol + redirect, statusCode);
.proxy.createRedirectRule(domainName, redirect, statusCode);
} else if (behaviour === 'ACTIVE') {
rule = await sdk
.forProject(page.params.region, page.params.project)