mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'main' into supposed-improvements
This commit is contained in:
+2
-2
@@ -22,11 +22,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/svelte": "^1.1.24",
|
||||
"@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@e190a19",
|
||||
"@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@a5e5564",
|
||||
"@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://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4e03f34",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@sentry/sveltekit": "^8.38.0",
|
||||
"@stripe/stripe-js": "^3.5.0",
|
||||
|
||||
Generated
+15
-14
@@ -12,8 +12,8 @@ importers:
|
||||
specifier: ^1.1.24
|
||||
version: 1.1.24(svelte@5.25.3)(zod@3.24.3)
|
||||
'@appwrite.io/console':
|
||||
specifier: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@e190a19
|
||||
version: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@e190a19
|
||||
specifier: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@a5e5564
|
||||
version: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@a5e5564
|
||||
'@appwrite.io/pink-icons':
|
||||
specifier: 0.25.0
|
||||
version: 0.25.0
|
||||
@@ -24,8 +24,8 @@ importers:
|
||||
specifier: ^1.0.3
|
||||
version: 1.0.3
|
||||
'@appwrite.io/pink-svelte':
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4e03f34
|
||||
version: https://pkg.vc/-/@appwrite/%40appwrite.io%2Fpink-svelte@4e03f34(svelte@5.25.3)
|
||||
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778
|
||||
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778(svelte@5.25.3)
|
||||
'@popperjs/core':
|
||||
specifier: ^2.11.8
|
||||
version: 2.11.8
|
||||
@@ -257,12 +257,13 @@ packages:
|
||||
'@analytics/type-utils@0.6.2':
|
||||
resolution: {integrity: sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg==}
|
||||
|
||||
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@e190a19':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@e190a19}
|
||||
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@a5e5564':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@a5e5564}
|
||||
version: 1.8.0
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@2.0.0-RC.1':
|
||||
resolution: {integrity: sha512-iLFlV55hj8mGuAbmxJGenxN5RaZMmVT4GJb9dv/MP1xBAtYibFq7JvBcxm18qV2KU8c31Rntf+Ub4GL7HwqTYg==}
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@ee1b7788cd3f877c9aa1b6487976bd63a6196870':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@ee1b7788cd3f877c9aa1b6487976bd63a6196870}
|
||||
version: 2.0.0-RC.1
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
|
||||
@@ -281,8 +282,8 @@ packages:
|
||||
'@appwrite.io/pink-legacy@1.0.3':
|
||||
resolution: {integrity: sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ==}
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/%40appwrite.io%2Fpink-svelte@4e03f34':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/%40appwrite.io%2Fpink-svelte@4e03f34}
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778}
|
||||
version: 2.0.0-RC.2
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
@@ -3635,9 +3636,9 @@ snapshots:
|
||||
|
||||
'@analytics/type-utils@0.6.2': {}
|
||||
|
||||
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@e190a19': {}
|
||||
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@a5e5564': {}
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@2.0.0-RC.1(svelte@5.25.3)':
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@ee1b7788cd3f877c9aa1b6487976bd63a6196870(svelte@5.25.3)':
|
||||
dependencies:
|
||||
svelte: 5.25.3
|
||||
|
||||
@@ -3654,9 +3655,9 @@ snapshots:
|
||||
'@appwrite.io/pink-icons': 1.0.0
|
||||
the-new-css-reset: 1.11.3
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/%40appwrite.io%2Fpink-svelte@4e03f34(svelte@5.25.3)':
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778(svelte@5.25.3)':
|
||||
dependencies:
|
||||
'@appwrite.io/pink-icons-svelte': 2.0.0-RC.1(svelte@5.25.3)
|
||||
'@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@ee1b7788cd3f877c9aa1b6487976bd63a6196870(svelte@5.25.3)
|
||||
'@floating-ui/dom': 1.6.13
|
||||
'@melt-ui/pp': 0.3.2(@melt-ui/svelte@0.86.6(svelte@5.25.3))(svelte@5.25.3)
|
||||
'@melt-ui/svelte': 0.86.6(svelte@5.25.3)
|
||||
|
||||
@@ -167,13 +167,10 @@
|
||||
<div style="padding: 1rem; padding-block-end: 0;">
|
||||
<Alert.Inline
|
||||
dismissible
|
||||
title="We collect user responses to refine our experimental AI feature."
|
||||
on:dismiss={() => {
|
||||
$preferences.hideAiDisclaimer = true;
|
||||
}}>
|
||||
<span slot="title">
|
||||
We collect user responses to refine our experimental AI feature.
|
||||
</span>
|
||||
</Alert.Inline>
|
||||
}} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -229,8 +226,11 @@
|
||||
<Layout.Stack direction="row" gap="s">
|
||||
<AvatarInitials size="s" name={$user.name} />
|
||||
<form
|
||||
class="input-text-wrapper u-width-full-line"
|
||||
class="u-full-width input-text-wrapper"
|
||||
style="--amount-of-buttons: 1;"
|
||||
style:display="flex"
|
||||
style:width="100%"
|
||||
style:align-items="center"
|
||||
on:submit|preventDefault={(e) => {
|
||||
handleSubmit(e);
|
||||
}}>
|
||||
@@ -238,6 +238,7 @@
|
||||
<input
|
||||
type="text"
|
||||
class="input-text"
|
||||
style:width="100%"
|
||||
placeholder="Ask a question..."
|
||||
autofocus
|
||||
bind:value={$input}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { base } from '$app/paths';
|
||||
import { isTabletViewport } from '$lib/stores/viewport';
|
||||
import { createEventDispatcher, onMount, onDestroy } from 'svelte';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Icon } from '@appwrite.io/pink-svelte';
|
||||
import { IconX } from '@appwrite.io/pink-icons-svelte';
|
||||
import { isTabletViewport } from '$lib/stores/viewport';
|
||||
import PinkBackground from '$lib/images/pink-background.svg';
|
||||
import { createEventDispatcher, onMount, onDestroy } from 'svelte';
|
||||
|
||||
export let variant: 'gradient' | 'image' = 'gradient';
|
||||
|
||||
let container: HTMLElement;
|
||||
const dispatch = createEventDispatcher();
|
||||
@@ -19,8 +22,7 @@
|
||||
const alertHeight = container?.getBoundingClientRect()?.height || 0;
|
||||
const { header, sidebar, content } = queryLayoutElements();
|
||||
const headerHeight = header?.getBoundingClientRect().height || 0;
|
||||
const offset = alertHeight + (!isTabletViewport && header ? headerHeight : 0);
|
||||
|
||||
const offset = alertHeight + (!$isTabletViewport && header ? headerHeight : 0);
|
||||
if (header) header.style.top = `${alertHeight}px`;
|
||||
if (sidebar) {
|
||||
sidebar.style.top = `${offset}px`;
|
||||
@@ -35,23 +37,32 @@
|
||||
|
||||
<svelte:window on:resize={setNavigationHeight} />
|
||||
|
||||
<div bind:this={container} class="top-banner alert is-action is-action-and-top-sticky">
|
||||
<div class="top-banner-bg">
|
||||
<div class="top-banner-bg-1">
|
||||
<img
|
||||
src={`${base}/images/top-banner/bg-pink-desktop.svg`}
|
||||
width="1283"
|
||||
height="1278"
|
||||
alt="" />
|
||||
<div
|
||||
bind:this={container}
|
||||
class:darker={variant === 'image'}
|
||||
class="top-banner alert is-action is-action-and-top-sticky">
|
||||
{#if variant === 'gradient'}
|
||||
<div class="top-banner-bg">
|
||||
<div class="top-banner-bg-1">
|
||||
<img
|
||||
src={`${base}/images/top-banner/bg-pink-desktop.svg`}
|
||||
width="1283"
|
||||
height="1278"
|
||||
alt="" />
|
||||
</div>
|
||||
<div class="top-banner-bg-2">
|
||||
<img
|
||||
src={`${base}/images/top-banner/bg-mint-desktop.svg`}
|
||||
width="1051"
|
||||
height="1271"
|
||||
alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="top-banner-bg-2">
|
||||
<img
|
||||
src={`${base}/images/top-banner/bg-mint-desktop.svg`}
|
||||
width="1051"
|
||||
height="1271"
|
||||
alt="" />
|
||||
{:else}
|
||||
<div class="centered-image-only">
|
||||
<img src={PinkBackground} width="1283" height="1278" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="top-banner-content u-color-text-primary">
|
||||
<slot />
|
||||
@@ -62,17 +73,39 @@
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
<style lang="scss">
|
||||
.alert {
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 100;
|
||||
position: fixed;
|
||||
padding: 0.8rem;
|
||||
|
||||
&.darker {
|
||||
background: var(--bgcolor-neutral-default);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
:global(.top-banner-button.position) {
|
||||
.centered-image-only {
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-left: 25vw;
|
||||
position: absolute;
|
||||
border-bottom: 1px solid var(--border-neutral);
|
||||
|
||||
@media (max-width: 768px) {
|
||||
& img {
|
||||
max-width: 100vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:global(.top-banner-button.position) {
|
||||
z-index: 1;
|
||||
@media (max-width: 768px) {
|
||||
position: absolute;
|
||||
padding-block-start: 1rem;
|
||||
padding-block-end: 3.7625rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { BASE_BILLING_PLANS, BillingPlan } from '$lib/constants';
|
||||
import { formatCurrency } from '$lib/helpers/numbers';
|
||||
import { plansInfo, type Tier, tierFree, tierPro, tierScale } from '$lib/stores/billing';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { currentPlan, organization } from '$lib/stores/organization';
|
||||
import { Badge, Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { LabelCard } from '..';
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
$: freePlan = $plansInfo.get(BillingPlan.FREE);
|
||||
$: proPlan = $plansInfo.get(BillingPlan.PRO);
|
||||
$: scalePlan = $plansInfo.get(BillingPlan.SCALE);
|
||||
|
||||
$: isBasePlan = BASE_BILLING_PLANS.includes($currentPlan?.$id);
|
||||
</script>
|
||||
|
||||
<Layout.Stack>
|
||||
@@ -72,4 +74,25 @@
|
||||
{formatCurrency(scalePlan?.price ?? 0)} per month + usage
|
||||
</Typography.Text>
|
||||
</LabelCard>
|
||||
{#if $currentPlan && !isBasePlan}
|
||||
<LabelCard
|
||||
name="plan"
|
||||
bind:group={billingPlan}
|
||||
value={$currentPlan.$id}
|
||||
title={$currentPlan.name}>
|
||||
<svelte:fragment slot="action">
|
||||
{#if $organization?.billingPlan === $currentPlan.$id && !isNewOrg}
|
||||
<Badge variant="secondary" size="xs" content="Current plan" />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<Typography.Caption variant="400">
|
||||
{$currentPlan.desc}
|
||||
</Typography.Caption>
|
||||
<Typography.Text>
|
||||
{@const isZeroPrice = ($currentPlan?.price ?? 0) <= 0}
|
||||
{@const price = formatCurrency($currentPlan?.price ?? 0)}
|
||||
{isZeroPrice ? price : `${price} per month + usage`}
|
||||
</Typography.Text>
|
||||
</LabelCard>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
import { isCloud } from '$lib/system';
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { newOrgModal } from '$lib/stores/organization';
|
||||
import { currentPlan, newOrgModal } from '$lib/stores/organization';
|
||||
import { Click, trackEvent } from '$lib/actions/analytics';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
@@ -182,6 +182,14 @@
|
||||
$: projectsBottomSheet = createProjectsBottomSheet(selectedOrg);
|
||||
|
||||
$: organizationsBottomSheet = createOrganizationBottomSheet(selectedOrg);
|
||||
|
||||
$: correctPlanName =
|
||||
// the plan names are hardcoded in some cases and are not available locally,
|
||||
// so we rely on the plan's source of truth - `$currentPlan`
|
||||
$currentPlan &&
|
||||
$currentPlan?.name.toLocaleLowerCase() !== selectedOrg?.tierName.toLocaleLowerCase()
|
||||
? $currentPlan.name
|
||||
: selectedOrg?.tierName; // fallback
|
||||
</script>
|
||||
|
||||
<svelte:window on:resize={onResize} />
|
||||
@@ -195,9 +203,9 @@
|
||||
aria-label="Open organizations tab">
|
||||
<span class="orgName">{selectedOrg?.name ?? 'Organization'}</span>
|
||||
<span class="not-mobile"
|
||||
>{#if selectedOrg?.tierName}<Badge
|
||||
>{#if correctPlanName}<Badge
|
||||
variant="secondary"
|
||||
content={selectedOrg?.tierName} />{/if}</span>
|
||||
content={correctPlanName} />{/if}</span>
|
||||
<Icon icon={IconChevronDown} size="s" color="--fgcolor-neutral-secondary" />
|
||||
</button>
|
||||
{:else}
|
||||
@@ -211,7 +219,7 @@
|
||||
<span class="orgName" class:noProjects={!currentProject}
|
||||
>{selectedOrg?.name ?? 'Organization'}</span>
|
||||
<span class="not-mobile"
|
||||
><Badge variant="secondary" content={selectedOrg?.tierName ?? ''} /></span>
|
||||
><Badge variant="secondary" content={correctPlanName ?? ''} /></span>
|
||||
<Icon icon={IconChevronDown} size="s" color="--fgcolor-neutral-secondary" />
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
@@ -18,6 +18,19 @@
|
||||
children: Snippet<[toggle: () => void, selectedColumnsNumber: number]>;
|
||||
} = $props();
|
||||
|
||||
let maxHeight = $state('none');
|
||||
let containerRef;
|
||||
|
||||
const calcMaxHeight = () => {
|
||||
if (containerRef) {
|
||||
// get parent row element for correct top position
|
||||
const parent = containerRef.parentElement.parentElement;
|
||||
const { top } = parent.getBoundingClientRect();
|
||||
|
||||
maxHeight = `${window.innerHeight - top - 48}px`;
|
||||
}
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
if (isCustomCollection) {
|
||||
const prefs = preferences.getCustomCollectionColumns(page.params.collection);
|
||||
@@ -50,6 +63,8 @@
|
||||
preferences.setColumns(columns);
|
||||
}
|
||||
});
|
||||
|
||||
calcMaxHeight();
|
||||
});
|
||||
|
||||
let selectedColumnsNumber = $derived(
|
||||
@@ -61,29 +76,32 @@
|
||||
);
|
||||
</script>
|
||||
|
||||
<svelte:window on:resize={calcMaxHeight} />
|
||||
{#if $columns?.length}
|
||||
<Popover let:toggle placement="bottom-end" padding="none">
|
||||
{@render children(toggle, selectedColumnsNumber)}
|
||||
<svelte:fragment slot="tooltip">
|
||||
<ActionMenu.Root>
|
||||
{#each $columns as column}
|
||||
{#if !column?.exclude}
|
||||
<ActionMenu.Item.Button
|
||||
on:click={() => (column.hide = !column.hide)}
|
||||
disabled={allowNoColumns
|
||||
? false
|
||||
: selectedColumnsNumber <= 1 && column.hide !== true}>
|
||||
<Layout.Stack direction="row" gap="s">
|
||||
<Selector.Checkbox
|
||||
checked={!column.hide}
|
||||
size="s"
|
||||
on:click={() => (column.hide = !column.hide)} />
|
||||
{column.title}
|
||||
</Layout.Stack>
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
{/each}
|
||||
</ActionMenu.Root>
|
||||
<div style:max-height={maxHeight} style:overflow="scroll" bind:this={containerRef}>
|
||||
<ActionMenu.Root>
|
||||
{#each $columns as column}
|
||||
{#if !column?.exclude}
|
||||
<ActionMenu.Item.Button
|
||||
on:click={() => (column.hide = !column.hide)}
|
||||
disabled={allowNoColumns
|
||||
? false
|
||||
: selectedColumnsNumber <= 1 && column.hide !== true}>
|
||||
<Layout.Stack direction="row" gap="s">
|
||||
<Selector.Checkbox
|
||||
checked={!column.hide}
|
||||
size="s"
|
||||
on:click={() => (column.hide = !column.hide)} />
|
||||
{column.title}
|
||||
</Layout.Stack>
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
{/each}
|
||||
</ActionMenu.Root>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
|
||||
@@ -25,14 +25,13 @@
|
||||
Navbar,
|
||||
Icon,
|
||||
Layout,
|
||||
Link,
|
||||
Tooltip,
|
||||
Card,
|
||||
ActionMenu,
|
||||
ToggleButton,
|
||||
Button,
|
||||
Avatar,
|
||||
Typography
|
||||
Typography,
|
||||
Popover
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import { toggleCommandCenter } from '$lib/commandCenter/commandCenter.svelte';
|
||||
import {
|
||||
@@ -214,91 +213,79 @@
|
||||
</Button.Button>
|
||||
<span slot="tooltip">{isMac() ? '⌘ + K' : 'Ctrl + K'}</span></Tooltip>
|
||||
</Layout.Stack>
|
||||
<Link.Button
|
||||
on:click={() => {
|
||||
showAccountMenu = !showAccountMenu;
|
||||
shouldAnimateThemeToggle = false;
|
||||
if (showAccountMenu) {
|
||||
trackEvent(Click.MenuDropDownClick);
|
||||
}
|
||||
}}>
|
||||
<div style:user-select="none">
|
||||
<Popover let:toggle let:showing>
|
||||
<button
|
||||
type="button"
|
||||
on:click|preventDefault={(e) => {
|
||||
toggle(e);
|
||||
shouldAnimateThemeToggle = false;
|
||||
if (showing) {
|
||||
trackEvent(Click.MenuDropDownClick);
|
||||
}
|
||||
}}
|
||||
style:user-select="none">
|
||||
<Avatar size="s" src={avatar} />
|
||||
</div>
|
||||
</Link.Button>
|
||||
{#if showAccountMenu}
|
||||
<div class="account-container">
|
||||
<Card.Base padding="xxxs" shadow={true}>
|
||||
</button>
|
||||
<svelte:fragment slot="tooltip" let:toggle>
|
||||
<ActionMenu.Root noPadding>
|
||||
<Layout.Stack gap="xxs">
|
||||
<ActionMenu.Root>
|
||||
<Layout.Stack gap="xxs">
|
||||
<div
|
||||
style:padding-inline-start="10px"
|
||||
style:padding-inline-end="8px"
|
||||
style:padding-block="4px">
|
||||
<Typography.Text variant="m-500">
|
||||
{$user.email}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<ActionMenu.Item.Anchor
|
||||
trailingIcon={IconUser}
|
||||
size="l"
|
||||
href={`${base}/account`}>
|
||||
Account</ActionMenu.Item.Anchor>
|
||||
<div
|
||||
style:padding-inline-start="10px"
|
||||
style:padding-inline-end="8px"
|
||||
style:padding-block="4px">
|
||||
<Typography.Text variant="m-500">
|
||||
{$user?.email}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<ActionMenu.Item.Anchor
|
||||
trailingIcon={IconUser}
|
||||
on:click={toggle}
|
||||
size="l"
|
||||
href={`${base}/account`}>
|
||||
Account</ActionMenu.Item.Anchor>
|
||||
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconLogoutRight}
|
||||
size="l"
|
||||
on:click={() => logout()}>Sign out</ActionMenu.Item.Button>
|
||||
<div
|
||||
style:padding-inline-start="10px"
|
||||
style:padding-inline-end="8px">
|
||||
<Layout.Stack
|
||||
justifyContent="space-between"
|
||||
direction="row"
|
||||
alignItems="center">
|
||||
<Typography.Text>Theme</Typography.Text>
|
||||
<div
|
||||
class:keepTransformTransition={shouldAnimateThemeToggle}>
|
||||
<ToggleButton
|
||||
bind:active={activeTheme}
|
||||
on:change={() => {
|
||||
setTimeout(() => {
|
||||
shouldAnimateThemeToggle = true;
|
||||
}, 150);
|
||||
}}
|
||||
buttons={[
|
||||
{
|
||||
id: 'light',
|
||||
label: 'Light',
|
||||
icon: IconSun
|
||||
},
|
||||
{
|
||||
id: 'dark',
|
||||
label: 'Dark',
|
||||
icon: IconMoon
|
||||
},
|
||||
{
|
||||
id: 'auto',
|
||||
label: 'System',
|
||||
icon: IconMode
|
||||
}
|
||||
]}></ToggleButton>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconLogoutRight}
|
||||
size="l"
|
||||
on:click={() => logout()}>Sign out</ActionMenu.Item.Button>
|
||||
<div style:padding-inline-start="10px" style:padding-inline-end="8px">
|
||||
<Layout.Stack
|
||||
justifyContent="space-between"
|
||||
direction="row"
|
||||
alignItems="center">
|
||||
<Typography.Text>Theme</Typography.Text>
|
||||
<div class:keepTransformTransition={shouldAnimateThemeToggle}>
|
||||
<ToggleButton
|
||||
bind:active={activeTheme}
|
||||
on:change={() => {
|
||||
setTimeout(() => {
|
||||
shouldAnimateThemeToggle = true;
|
||||
}, 150);
|
||||
}}
|
||||
buttons={[
|
||||
{
|
||||
id: 'light',
|
||||
label: 'Light',
|
||||
icon: IconSun
|
||||
},
|
||||
{
|
||||
id: 'dark',
|
||||
label: 'Dark',
|
||||
icon: IconMoon
|
||||
},
|
||||
{
|
||||
id: 'auto',
|
||||
label: 'System',
|
||||
icon: IconMode
|
||||
}
|
||||
]}></ToggleButton>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
</ActionMenu.Root>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
</Card.Base>
|
||||
</div>
|
||||
<button
|
||||
class="account-backdrop"
|
||||
aria-label="Account menu"
|
||||
on:click={() => {
|
||||
showAccountMenu = false;
|
||||
}}></button>
|
||||
{/if}
|
||||
</ActionMenu.Root>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
</Navbar.Base>
|
||||
@@ -410,24 +397,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.account-container {
|
||||
position: absolute;
|
||||
right: var(--space-7);
|
||||
top: var(--base-44);
|
||||
width: 244px;
|
||||
display: flex;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.account-backdrop {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* The default drop list has a max-inline width of 280px, which squeezes the support modal. */
|
||||
:global(.extended-width) {
|
||||
max-inline-size: none;
|
||||
|
||||
@@ -530,6 +530,8 @@ export enum BillingPlan {
|
||||
ENTERPRISE = 'ent-1'
|
||||
}
|
||||
|
||||
export const BASE_BILLING_PLANS: string[] = [BillingPlan.FREE, BillingPlan.PRO, BillingPlan.SCALE];
|
||||
|
||||
export const feedbackDowngradeOptions = [
|
||||
{
|
||||
value: 'availableFeatures',
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
<svg width="481" height="44" viewBox="0 0 481 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g style="mix-blend-mode:screen" opacity="0.25" filter="url(#filter0_f_2804_2539)">
|
||||
<circle cx="242.19" cy="52.7439" r="110.744" fill="url(#paint0_radial_2804_2539)"/>
|
||||
</g>
|
||||
<g filter="url(#filter1_f_2804_2539)">
|
||||
<ellipse cx="240.479" cy="56.2318" rx="40.0747" ry="119.968" transform="rotate(66.0796 240.479 56.2318)" fill="url(#paint1_radial_2804_2539)" fill-opacity="0.4"/>
|
||||
</g>
|
||||
<g filter="url(#filter2_f_2804_2539)">
|
||||
<ellipse cx="228.238" cy="51.0802" rx="23.6296" ry="70.7377" transform="rotate(66.0796 228.238 51.0802)" fill="url(#paint2_radial_2804_2539)" fill-opacity="0.5"/>
|
||||
</g>
|
||||
<g filter="url(#filter3_f_2804_2539)">
|
||||
<path d="M191.132 89.2277C140.692 116.602 92.3886 125.135 83.2442 108.286C74.0998 91.4362 107.577 55.5854 158.017 28.2107C208.458 0.835917 256.761 -7.69658 265.906 9.15282C275.05 26.0022 241.573 61.853 191.132 89.2277Z" fill="url(#paint3_radial_2804_2539)" fill-opacity="0.5"/>
|
||||
</g>
|
||||
<g filter="url(#filter4_f_2804_2539)">
|
||||
<ellipse cx="128.998" cy="25.9598" rx="16.2007" ry="66.1069" transform="rotate(66.0796 128.998 25.9598)" fill="url(#paint4_radial_2804_2539)" fill-opacity="0.05"/>
|
||||
</g>
|
||||
<g filter="url(#filter5_f_2804_2539)">
|
||||
<path d="M238.738 55.4355C188.298 82.8103 145.088 100.728 142.227 95.4567C139.366 90.1851 177.937 63.72 228.378 36.3452C278.818 8.97044 322.028 -8.94768 324.889 -3.67603C327.75 1.59561 289.179 28.0608 238.738 55.4355Z" fill="url(#paint5_radial_2804_2539)" fill-opacity="0.35"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_f_2804_2539" x="70.3866" y="-119.06" width="343.607" height="343.607" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="30.5299" result="effect1_foregroundBlur_2804_2539"/>
|
||||
</filter>
|
||||
<filter id="filter1_f_2804_2539" x="0.195038" y="-134.073" width="480.567" height="380.61" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="64.7038" result="effect1_foregroundBlur_2804_2539"/>
|
||||
</filter>
|
||||
<filter id="filter2_f_2804_2539" x="62.8613" y="-84.8274" width="330.753" height="271.815" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="50" result="effect1_foregroundBlur_2804_2539"/>
|
||||
</filter>
|
||||
<filter id="filter3_f_2804_2539" x="49.7393" y="-31.4919" width="249.671" height="180.422" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="16" result="effect1_foregroundBlur_2804_2539"/>
|
||||
</filter>
|
||||
<filter id="filter4_f_2804_2539" x="47.2728" y="-25.6037" width="163.45" height="103.127" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="10.4674" result="effect1_foregroundBlur_2804_2539"/>
|
||||
</filter>
|
||||
<filter id="filter5_f_2804_2539" x="110.078" y="-36.5962" width="246.96" height="164.973" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="16" result="effect1_foregroundBlur_2804_2539"/>
|
||||
</filter>
|
||||
<radialGradient id="paint0_radial_2804_2539" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(242.19 52.7439) rotate(90) scale(110.744)">
|
||||
<stop stop-color="#FD366E"/>
|
||||
<stop offset="1" stop-color="#FD366E" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint1_radial_2804_2539" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(240.479 56.2318) rotate(90) scale(119.968 40.0747)">
|
||||
<stop stop-color="#FE9567"/>
|
||||
<stop offset="1" stop-color="#F02E65" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint2_radial_2804_2539" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(228.238 51.0802) rotate(90) scale(70.7377 23.6296)">
|
||||
<stop stop-color="#FFEAE1"/>
|
||||
<stop offset="1" stop-color="#F02E65" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint3_radial_2804_2539" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(174.575 58.7192) rotate(151.511) scale(103.914 34.7119)">
|
||||
<stop stop-color="#F02E65"/>
|
||||
<stop offset="1" stop-color="#F02E65" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint4_radial_2804_2539" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(128.998 25.9598) rotate(90) scale(66.1069 16.2007)">
|
||||
<stop stop-color="#FFEAE1"/>
|
||||
<stop offset="1" stop-color="#F02E65" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint5_radial_2804_2539" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(233.558 45.8904) rotate(151.511) scale(103.914 10.8603)">
|
||||
<stop stop-color="#F02E65"/>
|
||||
<stop offset="1" stop-color="#F02E65" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
@@ -17,7 +17,7 @@
|
||||
|
||||
let {
|
||||
columns,
|
||||
view = View.Table,
|
||||
view = $bindable(View.Table),
|
||||
hideView = false,
|
||||
hideColumns = false,
|
||||
hasSearch = false,
|
||||
|
||||
@@ -11,7 +11,7 @@ import PaymentAuthRequired from '$lib/components/billing/alerts/paymentAuthRequi
|
||||
import PaymentMandate from '$lib/components/billing/alerts/paymentMandate.svelte';
|
||||
import { BillingPlan, NEW_DEV_PRO_UPGRADE_COUPON } from '$lib/constants';
|
||||
import { cachedStore } from '$lib/helpers/cache';
|
||||
import { sizeToBytes, type Size } from '$lib/helpers/sizeConvertion';
|
||||
import { type Size, sizeToBytes } from '$lib/helpers/sizeConvertion';
|
||||
import type {
|
||||
AddressesList,
|
||||
Aggregation,
|
||||
@@ -28,12 +28,18 @@ import { AppwriteException, Query } from '@appwrite.io/console';
|
||||
import { derived, get, writable } from 'svelte/store';
|
||||
import { headerAlert } from './headerAlert';
|
||||
import { addNotification, notifications } from './notifications';
|
||||
import { organization, type Organization, type OrganizationError } from './organization';
|
||||
import {
|
||||
currentPlan,
|
||||
organization,
|
||||
type Organization,
|
||||
type OrganizationError
|
||||
} from './organization';
|
||||
import { canSeeBilling } from './roles';
|
||||
import { sdk } from './sdk';
|
||||
import { user } from './user';
|
||||
import BudgetLimitAlert from '$routes/(console)/organization-[organization]/budgetLimitAlert.svelte';
|
||||
import TeamReadonlyAlert from '$routes/(console)/organization-[organization]/teamReadonlyAlert.svelte';
|
||||
import EnterpriseTrial from '$routes/(console)/organization-[organization]/enterpriseTrial.svelte';
|
||||
|
||||
export type Tier = 'tier-0' | 'tier-1' | 'tier-2' | 'auto-1' | 'cont-1' | 'ent-1';
|
||||
|
||||
@@ -268,6 +274,32 @@ export function isServiceLimited(serviceId: PlanServices, plan: Tier, total: num
|
||||
return isLimited && total >= limit && !hasUsageFees;
|
||||
}
|
||||
|
||||
export function checkForEnterpriseTrial(org: Organization) {
|
||||
if (!org || !org.billingNextInvoiceDate) return;
|
||||
if (calculateEnterpriseTrial(org) > 0) {
|
||||
headerAlert.add({
|
||||
id: 'teamEnterpriseTrial',
|
||||
component: EnterpriseTrial,
|
||||
show: true,
|
||||
importance: 11
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function calculateEnterpriseTrial(org: Organization) {
|
||||
const endDate = new Date(org.billingNextInvoiceDate);
|
||||
const startDate = new Date(org.billingCurrentInvoiceDate);
|
||||
const today = new Date();
|
||||
|
||||
let diffCycle = endDate.getTime() - startDate.getTime();
|
||||
diffCycle = Math.ceil(diffCycle / (1000 * 60 * 60 * 24));
|
||||
if (diffCycle === 14) {
|
||||
const remaining = endDate.getTime() - today.getTime();
|
||||
return Math.ceil(remaining / (1000 * 60 * 60 * 24));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function calculateTrialDay(org: Organization) {
|
||||
if (org?.billingPlan === BillingPlan.FREE) return false;
|
||||
const endDate = new Date(org?.billingStartDate);
|
||||
@@ -323,7 +355,7 @@ export async function checkForUsageLimit(org: Organization) {
|
||||
];
|
||||
|
||||
const members = org.total;
|
||||
const plan = get(plansInfo)?.get(org.billingPlan);
|
||||
const plan = get(currentPlan);
|
||||
const membersOverflow =
|
||||
members > plan.addons.seats.limit ? members - (plan.addons.seats.limit || members) : 0;
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ export type Account = Models.User<
|
||||
>;
|
||||
|
||||
export const user = derived(page, ($page) => {
|
||||
if (browser) sessionStorage.setItem('account', JSON.stringify($page.data.account));
|
||||
return $page.data.account as Account;
|
||||
if ($page.data?.account) {
|
||||
if (browser) {
|
||||
sessionStorage.setItem('account', JSON.stringify($page.data.account));
|
||||
}
|
||||
return $page.data.account as Account;
|
||||
} else return null;
|
||||
});
|
||||
|
||||
@@ -167,7 +167,10 @@
|
||||
{error}
|
||||
{formGroup}
|
||||
groupKey={getAsType(groupKey)}
|
||||
reportValue={report?.[getReportKey(groupKey)]} />
|
||||
reportValue={report?.[getReportKey(groupKey)]}
|
||||
on:updateFormGroup={(e) => {
|
||||
$formData[groupKey] = e.detail;
|
||||
}} />
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import Create from './createOrganization.svelte';
|
||||
import {
|
||||
calculateTrialDay,
|
||||
checkForEnterpriseTrial,
|
||||
checkForMandate,
|
||||
checkForMarkedForDeletion,
|
||||
checkForMissingPaymentMethod,
|
||||
@@ -295,6 +296,7 @@
|
||||
if (currentOrganizationId === org.$id) return;
|
||||
if (isCloud) {
|
||||
currentOrganizationId = org.$id;
|
||||
checkForEnterpriseTrial(org);
|
||||
await checkForUsageLimit(org);
|
||||
checkForMarkedForDeletion(org);
|
||||
await checkForNewDevUpgradePro(org);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
let error: string;
|
||||
let showCustomId = false;
|
||||
let disabled: boolean = false;
|
||||
let name: string = 'Appwrite project';
|
||||
let name: string = 'New project';
|
||||
|
||||
async function create() {
|
||||
try {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
export let showCreateProjectCloud: boolean;
|
||||
|
||||
let id: string = null;
|
||||
let name: string = 'Appwrite project';
|
||||
let name: string = 'New project';
|
||||
let region: string = Region.Fra;
|
||||
let error: string = null;
|
||||
|
||||
|
||||
+21
-7
@@ -39,12 +39,11 @@
|
||||
let showDelete = false;
|
||||
let selectedRecord: Models.DnsRecord = null;
|
||||
|
||||
function formatRecordName(name: string) {
|
||||
const limit = 30;
|
||||
function formatName(name: string, limit: number = 30) {
|
||||
return {
|
||||
value: name.length > limit ? `${name.slice(0, limit)}...` : name,
|
||||
truncated: name.length > limit,
|
||||
whole: name
|
||||
value: name ? (name.length > limit ? `${name.slice(0, limit)}...` : name) : '-',
|
||||
truncated: name ? name.length > limit : undefined,
|
||||
whole: name ?? '-'
|
||||
};
|
||||
}
|
||||
</script>
|
||||
@@ -65,7 +64,7 @@
|
||||
{#each $columns as column}
|
||||
<Table.Cell column={column.id} {root}>
|
||||
{#if column.id === 'name'}
|
||||
{@const formatted = formatRecordName(record.name)}
|
||||
{@const formatted = formatName(record.name)}
|
||||
<Tooltip placement="bottom" disabled={!formatted.truncated}>
|
||||
<Typography.Text truncate>{formatted.value}</Typography.Text>
|
||||
<span
|
||||
@@ -99,7 +98,22 @@
|
||||
</Typography.Text>
|
||||
{:else if column.id === 'comment'}
|
||||
<Typography.Text truncate>
|
||||
{record?.comment ?? '-'}
|
||||
{@const formatted = formatName(record?.comment)}
|
||||
<Tooltip
|
||||
placement="bottom"
|
||||
maxWidth="fit-content"
|
||||
disabled={!formatted.truncated}>
|
||||
<Typography.Text truncate>{formatted.value}</Typography.Text>
|
||||
<span
|
||||
slot="tooltip"
|
||||
let:showing
|
||||
style:white-space="pre-wrap"
|
||||
style:word-break="break-all">
|
||||
{#if showing}
|
||||
{formatted.whole}
|
||||
{/if}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</Typography.Text>
|
||||
{:else if column.id === '$createdAt'}
|
||||
<DualTimeView time={record.$createdAt} />
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { calculateEnterpriseTrial, hideBillingHeaderRoutes } from '$lib/stores/billing';
|
||||
import { base } from '$app/paths';
|
||||
import GradientBanner from '$lib/components/billing/gradientBanner.svelte';
|
||||
import { Badge, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { activeHeaderAlert } from '../store';
|
||||
|
||||
$: upgradeUrl = `${base}/organization-${$organization?.$id}/change-plan`;
|
||||
$: remainingDays = calculateEnterpriseTrial($organization);
|
||||
|
||||
let show = true;
|
||||
|
||||
function handleClose() {
|
||||
show = false;
|
||||
const now = new Date().getTime();
|
||||
localStorage.setItem($activeHeaderAlert.id, now.toString());
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if $organization?.$id && remainingDays > 0 && !hideBillingHeaderRoutes.includes(page.url.pathname) && show}
|
||||
<GradientBanner variant="image" on:close={handleClose}>
|
||||
<div class="banner-fullwidth-grid">
|
||||
<Typography.Text color="--fgcolor-neutral-primary">
|
||||
Your enterprise trial expires in <Badge
|
||||
size="xs"
|
||||
variant="secondary"
|
||||
content={remainingDays.toString()} /> days
|
||||
</Typography.Text>
|
||||
|
||||
<Button size="xs" secondary fullWidthMobile href={upgradeUrl}>Upgrade</Button>
|
||||
</div>
|
||||
</GradientBanner>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.banner-fullwidth-grid {
|
||||
width: 100vw;
|
||||
display: grid;
|
||||
padding: 0 3rem;
|
||||
align-items: center;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
|
||||
> :global(:first-child) {
|
||||
grid-column: 2;
|
||||
justify-self: center;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
grid-column: unset;
|
||||
}
|
||||
}
|
||||
|
||||
> :global(:last-child) {
|
||||
grid-column: 3;
|
||||
justify-self: end;
|
||||
margin-inline-end: 4px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
padding: unset;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -8,7 +8,7 @@
|
||||
import { isTabSelected } from '$lib/helpers/load';
|
||||
import { Cover } from '$lib/layout';
|
||||
import { daysLeftInTrial, getServiceLimit, plansInfo, readOnly } from '$lib/stores/billing';
|
||||
import { members, newMemberModal, organization } from '$lib/stores/organization';
|
||||
import { members, newMemberModal, type Organization } from '$lib/stores/organization';
|
||||
import {
|
||||
canSeeBilling,
|
||||
canSeeProjects,
|
||||
@@ -18,20 +18,21 @@
|
||||
} from '$lib/stores/roles';
|
||||
import { GRACE_PERIOD_OVERRIDE, isCloud } from '$lib/system';
|
||||
import { IconGithub, IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Icon, Tooltip, Typography, Layout, Badge } from '@appwrite.io/pink-svelte';
|
||||
import { Badge, Icon, Layout, Tooltip, Typography } from '@appwrite.io/pink-svelte';
|
||||
|
||||
let areMembersLimited: boolean;
|
||||
$: organization.subscribe(() => {
|
||||
|
||||
$: {
|
||||
const limit = getServiceLimit('members') || Infinity;
|
||||
const isLimited = limit !== 0 && limit < Infinity;
|
||||
areMembersLimited =
|
||||
isCloud &&
|
||||
(($readOnly && !GRACE_PERIOD_OVERRIDE) || (isLimited && $members?.total >= limit));
|
||||
});
|
||||
}
|
||||
|
||||
$: organization = page.data.organization as Organization;
|
||||
$: avatars = $members.memberships?.map((m) => m.userName || m.userEmail) ?? [];
|
||||
$: organizationId = $organization?.$id ?? page.params.organization;
|
||||
$: path = `${base}/organization-${organizationId}`;
|
||||
$: path = `${base}/organization-${organization.$id}`;
|
||||
$: tabs = [
|
||||
{
|
||||
href: path,
|
||||
@@ -75,26 +76,26 @@
|
||||
].filter((tab) => !tab.disabled);
|
||||
</script>
|
||||
|
||||
{#if $organization.$id}
|
||||
{#if organization?.$id}
|
||||
<Cover>
|
||||
<svelte:fragment slot="header">
|
||||
<span class="u-flex u-cross-center u-gap-8 u-min-width-0">
|
||||
<Typography.Title color="--fgcolor-neutral-primary" size="xl" truncate>
|
||||
{$organization.name}
|
||||
{organization.name}
|
||||
</Typography.Title>
|
||||
{#if isCloud && $organization?.billingPlan === BillingPlan.GITHUB_EDUCATION}
|
||||
{#if isCloud && organization?.billingPlan === BillingPlan.GITHUB_EDUCATION}
|
||||
<Badge variant="secondary" content="Education">
|
||||
<Icon icon={IconGithub} size="s" slot="start" />
|
||||
</Badge>
|
||||
{:else if isCloud && $organization?.billingPlan === BillingPlan.FREE}
|
||||
{:else if isCloud && organization?.billingPlan === BillingPlan.FREE}
|
||||
<Badge variant="secondary" content="Free"></Badge>
|
||||
{/if}
|
||||
{#if isCloud && $organization?.billingTrialStartDate && $daysLeftInTrial > 0 && $organization.billingPlan !== BillingPlan.FREE && $plansInfo.get($organization.billingPlan)?.trialDays}
|
||||
{#if isCloud && organization?.billingTrialStartDate && $daysLeftInTrial > 0 && organization.billingPlan !== BillingPlan.FREE && $plansInfo.get(organization.billingPlan)?.trialDays}
|
||||
<Tooltip>
|
||||
<Badge variant="secondary" content="Trial" />
|
||||
<svelte:fragment slot="tooltip">
|
||||
{`Your trial ends on ${toLocaleDate(
|
||||
$organization.billingStartDate
|
||||
organization.billingStartDate
|
||||
)}. ${$daysLeftInTrial} days remaining.`}
|
||||
</svelte:fragment>
|
||||
</Tooltip>
|
||||
|
||||
+15
-21
@@ -1,37 +1,31 @@
|
||||
<script lang="ts">
|
||||
import { Empty, PaginationWithLimit, SearchQuery, ViewSelector } from '$lib/components';
|
||||
import { Empty, PaginationWithLimit } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Container } from '$lib/layout';
|
||||
import { Container, ResponsiveContainerHeader } from '$lib/layout';
|
||||
import { columns, showCreate } from './store';
|
||||
import Table from './table.svelte';
|
||||
import Grid from './grid.svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { canWriteCollections } from '$lib/stores/roles';
|
||||
import { Icon, Layout } from '@appwrite.io/pink-svelte';
|
||||
import { Icon } from '@appwrite.io/pink-svelte';
|
||||
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<Layout.Stack direction="row" justifyContent="space-between">
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<SearchQuery placeholder="Search by name or ID" />
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<ViewSelector
|
||||
{columns}
|
||||
view={data.view}
|
||||
hideColumns={!data.collections.total}
|
||||
hideView={!data.collections.total} />
|
||||
{#if $canWriteCollections}
|
||||
<Button on:click={() => ($showCreate = true)} event="create_collection">
|
||||
<Icon icon={IconPlus} slot="start" size="s" />
|
||||
Create collection
|
||||
</Button>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
<ResponsiveContainerHeader
|
||||
bind:view={data.view}
|
||||
{columns}
|
||||
hasSearch
|
||||
searchPlaceholder="Search by name or ID">
|
||||
{#if $canWriteCollections}
|
||||
<Button on:click={() => ($showCreate = true)} event="create_collection">
|
||||
<Icon icon={IconPlus} slot="start" size="s" />
|
||||
Create collection
|
||||
</Button>
|
||||
{/if}
|
||||
</ResponsiveContainerHeader>
|
||||
|
||||
{#if data.collections.total}
|
||||
{#if data.view === 'grid'}
|
||||
|
||||
+18
-9
@@ -11,7 +11,7 @@
|
||||
import { sortBranches } from '$lib/stores/vcs';
|
||||
import { IconInfo } from '@appwrite.io/pink-icons-svelte';
|
||||
import { LabelCard } from '$lib/components';
|
||||
import { Runtime, StatusCode, type Models } from '@appwrite.io/console';
|
||||
import { type Models, ProxyResourceType, Runtime, StatusCode } from '@appwrite.io/console';
|
||||
import { statusCodeOptions } from '$lib/stores/domains';
|
||||
import { writable } from 'svelte/store';
|
||||
import { onMount } from 'svelte';
|
||||
@@ -48,18 +48,21 @@
|
||||
|
||||
async function addDomain() {
|
||||
const apexDomain = getApexDomain(domainName);
|
||||
let domain = data.domains?.domains.find((d) => d.domain === apexDomain);
|
||||
let domain = data.domains?.domains.find((d: Models.Domain) => d.domain === apexDomain);
|
||||
|
||||
if (apexDomain && !domain && isCloud) {
|
||||
try {
|
||||
domain = await sdk.forConsole.domains.create($project.teamId, apexDomain);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
|
||||
return;
|
||||
// apex might already be added on organization level, skip.
|
||||
const alreadyAdded = error?.type === 'domain_already_exists';
|
||||
if (!alreadyAdded) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +75,13 @@
|
||||
} else if (behaviour === 'REDIRECT') {
|
||||
rule = await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.proxy.createRedirectRule(domainName, redirect, statusCode);
|
||||
.proxy.createRedirectRule(
|
||||
domainName,
|
||||
redirect,
|
||||
statusCode,
|
||||
page.params.function,
|
||||
ProxyResourceType.Function
|
||||
);
|
||||
} else if (behaviour === 'ACTIVE') {
|
||||
rule = await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/state';
|
||||
import {
|
||||
Empty,
|
||||
EmptyFilter,
|
||||
EmptySearch,
|
||||
Id,
|
||||
PaginationWithLimit,
|
||||
SearchQuery,
|
||||
ViewSelector
|
||||
} from '$lib/components';
|
||||
import { Filters, hasPageQueries } from '$lib/components/filters';
|
||||
import { Empty, EmptyFilter, EmptySearch, Id, PaginationWithLimit } from '$lib/components';
|
||||
import { hasPageQueries } from '$lib/components/filters';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import { Container } from '$lib/layout';
|
||||
import { Container, ResponsiveContainerHeader } from '$lib/layout';
|
||||
import { MessagingProviderType } from '@appwrite.io/console';
|
||||
import CreateMessageDropdown from './createMessageDropdown.svelte';
|
||||
import FailedModal from './failedModal.svelte';
|
||||
@@ -105,18 +97,18 @@
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<Layout.Stack direction="row" justifyContent="space-between">
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<SearchQuery placeholder="Search by description, type, status, or ID" />
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<Filters query={data.query} {columns} analyticsSource="messaging_messages" />
|
||||
<ViewSelector view={data.view} {columns} hideView />
|
||||
{#if $canWriteMessages}
|
||||
<CreateMessageDropdown />
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
<ResponsiveContainerHeader
|
||||
{columns}
|
||||
bind:view={data.view}
|
||||
hideView
|
||||
hasFilters
|
||||
hasSearch
|
||||
analyticsSource="messaging_messages"
|
||||
searchPlaceholder="Search by description, type, status, or ID">
|
||||
{#if $canWriteMessages}
|
||||
<CreateMessageDropdown />
|
||||
{/if}
|
||||
</ResponsiveContainerHeader>
|
||||
|
||||
{#if data.messages.total}
|
||||
<Table.Root
|
||||
|
||||
@@ -1,27 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import {
|
||||
Empty,
|
||||
EmptySearch,
|
||||
SearchQuery,
|
||||
PaginationWithLimit,
|
||||
EmptyFilter,
|
||||
ViewSelector
|
||||
} from '$lib/components';
|
||||
import { Empty, EmptySearch, PaginationWithLimit, EmptyFilter } from '$lib/components';
|
||||
import Create from './create.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { Container } from '$lib/layout';
|
||||
import { Container, ResponsiveContainerHeader } from '$lib/layout';
|
||||
import { base } from '$app/paths';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import type { PageData } from './$types';
|
||||
import { showCreate } from './store';
|
||||
import { Filters, hasPageQueries } from '$lib/components/filters';
|
||||
import { hasPageQueries } from '$lib/components/filters';
|
||||
import Table from './table.svelte';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import { writable } from 'svelte/store';
|
||||
import { canWriteTopics } from '$lib/stores/roles';
|
||||
import { Icon, Layout } from '@appwrite.io/pink-svelte';
|
||||
import { Icon } from '@appwrite.io/pink-svelte';
|
||||
import { View } from '$lib/helpers/load';
|
||||
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Click, trackEvent } from '$lib/actions/analytics';
|
||||
@@ -72,26 +65,26 @@
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<Layout.Stack direction="row" justifyContent="space-between">
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<SearchQuery placeholder="Search by name or ID" />
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<Filters query={data.query} {columns} analyticsSource="messaging_topics_filter" />
|
||||
<ViewSelector view={View.Table} {columns} hideView />
|
||||
{#if $canWriteTopics}
|
||||
<Button
|
||||
on:click={() => {
|
||||
$showCreate = true;
|
||||
trackEvent(Click.MessagingTopicCreateClick);
|
||||
}}
|
||||
event="create_topic">
|
||||
<Icon icon={IconPlus} slot="start" size="s" />
|
||||
Create topic
|
||||
</Button>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
<ResponsiveContainerHeader
|
||||
{columns}
|
||||
view={View.Table}
|
||||
hideView
|
||||
hasFilters
|
||||
hasSearch
|
||||
analyticsSource="messaging_topics_filter"
|
||||
searchPlaceholder="Search by name or ID">
|
||||
{#if $canWriteTopics}
|
||||
<Button
|
||||
on:click={() => {
|
||||
$showCreate = true;
|
||||
trackEvent(Click.MessagingTopicCreateClick);
|
||||
}}
|
||||
event="create_topic">
|
||||
<Icon icon={IconPlus} slot="start" size="s" />
|
||||
Create topic
|
||||
</Button>
|
||||
{/if}
|
||||
</ResponsiveContainerHeader>
|
||||
|
||||
{#if data.topics.total}
|
||||
<Table columns={$columns} {data} />
|
||||
|
||||
+34
-16
@@ -115,9 +115,11 @@ static const String APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.reg
|
||||
projectId,
|
||||
platform,
|
||||
$createPlatform.name,
|
||||
$createPlatform.key || undefined,
|
||||
platform === PlatformType.Flutterweb ? undefined : $createPlatform.key || undefined,
|
||||
undefined,
|
||||
undefined
|
||||
platform === PlatformType.Flutterweb
|
||||
? $createPlatform.hostname || undefined
|
||||
: undefined
|
||||
);
|
||||
|
||||
isPlatformCreated = true;
|
||||
@@ -189,19 +191,35 @@ static const String APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.reg
|
||||
bind:value={$createPlatform.name} />
|
||||
|
||||
<!-- Tooltips on InputText don't work as of now -->
|
||||
<InputText
|
||||
id="hostname"
|
||||
label={hostname[platform]}
|
||||
placeholder={placeholder[platform].hostname}
|
||||
required
|
||||
bind:value={$createPlatform.key}>
|
||||
<Tooltip slot="info" maxWidth="15rem">
|
||||
<Icon icon={IconInfo} size="s" />
|
||||
<Typography.Caption variant="400" slot="tooltip">
|
||||
{placeholder[platform].tooltip}
|
||||
</Typography.Caption>
|
||||
</Tooltip>
|
||||
</InputText>
|
||||
{#if platform === PlatformType.Flutterweb}
|
||||
<InputText
|
||||
id="hostname"
|
||||
label={hostname[platform]}
|
||||
placeholder={placeholder[platform].hostname}
|
||||
required
|
||||
bind:value={$createPlatform.hostname}>
|
||||
<Tooltip slot="info" maxWidth="15rem">
|
||||
<Icon icon={IconInfo} size="s" />
|
||||
<Typography.Caption variant="400" slot="tooltip">
|
||||
{placeholder[platform].tooltip}
|
||||
</Typography.Caption>
|
||||
</Tooltip>
|
||||
</InputText>
|
||||
{:else}
|
||||
<InputText
|
||||
id="key"
|
||||
label={hostname[platform]}
|
||||
placeholder={placeholder[platform].hostname}
|
||||
required
|
||||
bind:value={$createPlatform.key}>
|
||||
<Tooltip slot="info" maxWidth="15rem">
|
||||
<Icon icon={IconInfo} size="s" />
|
||||
<Typography.Caption variant="400" slot="tooltip">
|
||||
{placeholder[platform].tooltip}
|
||||
</Typography.Caption>
|
||||
</Tooltip>
|
||||
</InputText>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
<Button
|
||||
@@ -212,7 +230,7 @@ static const String APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.reg
|
||||
submissionLoader={isCreatingPlatform}
|
||||
disabled={!platform ||
|
||||
!$createPlatform.name ||
|
||||
!$createPlatform.key ||
|
||||
(!$createPlatform.key && !$createPlatform.hostname) ||
|
||||
isCreatingPlatform}>
|
||||
Create platform
|
||||
</Button>
|
||||
|
||||
+11
-7
@@ -12,6 +12,7 @@
|
||||
import { isCloud } from '$lib/system';
|
||||
import { project } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import { getApexDomain } from '$lib/helpers/tlds';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
const routeBase = `${base}/project-${page.params.region}-${page.params.project}/settings/domains`;
|
||||
|
||||
@@ -29,18 +30,21 @@
|
||||
|
||||
async function addDomain() {
|
||||
const apexDomain = getApexDomain(domainName);
|
||||
let domain = data.domains?.domains.find((d) => d.domain === apexDomain);
|
||||
let domain = data.domains?.domains.find((d: Models.Domain) => d.domain === apexDomain);
|
||||
|
||||
if (apexDomain && !domain && isCloud) {
|
||||
try {
|
||||
domain = await sdk.forConsole.domains.create($project.teamId, apexDomain);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
|
||||
return;
|
||||
// apex might already be added on organization level, skip.
|
||||
const alreadyAdded = error?.type === 'domain_already_exists';
|
||||
if (!alreadyAdded) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-2
@@ -9,12 +9,15 @@
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import type { MigrationFormData } from '$lib/stores/migration';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let error: boolean = false;
|
||||
export let groupKey: keyof MigrationFormData;
|
||||
export let formGroup: MigrationFormData[typeof groupKey];
|
||||
export let reportValue: string | number | undefined = undefined;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const labelMap = {
|
||||
users: { root: 'Users', teams: 'Include teams' },
|
||||
databases: { root: 'Databases', documents: 'Include documents' },
|
||||
@@ -61,9 +64,12 @@
|
||||
|
||||
function onParentChange(event: CustomEvent<boolean | 'indeterminate'>) {
|
||||
if (event.detail === 'indeterminate') return;
|
||||
const updated = { ...formGroup };
|
||||
for (const key of Object.keys(formGroup)) {
|
||||
formGroup[key] = event.detail;
|
||||
updated[key] = event.detail;
|
||||
}
|
||||
|
||||
dispatch('updateFormGroup', updated);
|
||||
}
|
||||
|
||||
$: isLoading = !error;
|
||||
@@ -74,7 +80,13 @@
|
||||
<Button extraCompact on:click={() => (formGroup.root = !formGroup.root)}>
|
||||
<Layout.Stack direction="row" gap="s" alignItems="center">
|
||||
<Layout.Stack inline direction="row" gap="l" alignItems="flex-start">
|
||||
<Selector.Checkbox size="s" bind:checked={formGroup.root} />
|
||||
<Selector.Checkbox
|
||||
size="s"
|
||||
bind:checked={formGroup.root}
|
||||
on:change={(event) => {
|
||||
const updated = { ...formGroup, root: event.detail };
|
||||
dispatch('updateFormGroup', updated);
|
||||
}} />
|
||||
|
||||
<Typography.Text variant="m-500" color="--fgcolor-neutral-primary">
|
||||
{labelMap[groupKey]?.root ?? groupKey}
|
||||
|
||||
@@ -26,18 +26,10 @@ export const load = async ({ params, depends, url, route }) => {
|
||||
.forProject(params.region, params.project)
|
||||
.proxy.listRules(
|
||||
[
|
||||
Query.or([
|
||||
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('type', [RuleType.DEPLOYMENT, RuleType.REDIRECT]),
|
||||
Query.equal('deploymentResourceType', DeploymentResourceType.SITE),
|
||||
Query.equal('deploymentResourceId', params.site),
|
||||
Query.equal('trigger', RuleTrigger.MANUAL),
|
||||
Query.limit(limit),
|
||||
Query.offset(offset),
|
||||
Query.orderDesc(''),
|
||||
|
||||
+25
-9
@@ -16,13 +16,17 @@
|
||||
BuildRuntime,
|
||||
Framework,
|
||||
type Models,
|
||||
ProxyResourceType,
|
||||
StatusCode
|
||||
} from '@appwrite.io/console';
|
||||
import { statusCodeOptions } from '$lib/stores/domains';
|
||||
import { writable } from 'svelte/store';
|
||||
import { onMount } from 'svelte';
|
||||
import { ConnectRepoModal } from '$lib/components/git/index.js';
|
||||
import { project } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import {
|
||||
project,
|
||||
regionalConsoleVariables
|
||||
} from '$routes/(console)/project-[region]-[project]/store';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { getApexDomain } from '$lib/helpers/tlds';
|
||||
|
||||
@@ -53,17 +57,23 @@
|
||||
|
||||
async function addDomain() {
|
||||
const apexDomain = getApexDomain(domainName);
|
||||
let domain = data.domains?.domains.find((d) => d.domain === apexDomain);
|
||||
let domain = data.domains?.domains.find((d: Models.Domain) => d.domain === apexDomain);
|
||||
|
||||
if (apexDomain && !domain && isCloud) {
|
||||
const isSiteDomain = domainName.endsWith($regionalConsoleVariables._APP_DOMAIN_SITES);
|
||||
|
||||
if (isCloud && apexDomain && !domain && !isSiteDomain) {
|
||||
try {
|
||||
domain = await sdk.forConsole.domains.create($project.teamId, apexDomain);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
return;
|
||||
// apex might already be added on organization level, skip.
|
||||
const alreadyAdded = error?.type === 'domain_already_exists';
|
||||
if (!alreadyAdded) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +86,13 @@
|
||||
} else if (behaviour === 'REDIRECT') {
|
||||
rule = await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.proxy.createRedirectRule(domainName, redirect, statusCode);
|
||||
.proxy.createRedirectRule(
|
||||
domainName,
|
||||
redirect,
|
||||
statusCode,
|
||||
page.params.site,
|
||||
ProxyResourceType.Site
|
||||
);
|
||||
} else if (behaviour === 'ACTIVE') {
|
||||
rule = await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
helper="Password must be at least 8 characters long"
|
||||
required
|
||||
bind:value={pass} />
|
||||
<InputChoice required value={terms} id="terms" label="terms" showLabel={false}>
|
||||
<InputChoice required bind:value={terms} id="terms" label="terms" showLabel={false}>
|
||||
By registering, you agree that you have read, understand, and acknowledge our <Link.Anchor
|
||||
href="https://appwrite.io/privacy"
|
||||
target="_blank"
|
||||
@@ -119,10 +119,16 @@
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">General Terms of Use</Link.Anchor
|
||||
>.</InputChoice>
|
||||
<Button fullWidth submit {disabled}>Sign up</Button>
|
||||
|
||||
<Button fullWidth submit disabled={disabled || !terms}>Sign up</Button>
|
||||
|
||||
{#if isCloud}
|
||||
<span class="with-separators eyebrow-heading-3">or</span>
|
||||
<Button secondary fullWidth on:click={onGithubLogin} {disabled}>
|
||||
<Button
|
||||
secondary
|
||||
fullWidth
|
||||
on:click={onGithubLogin}
|
||||
disabled={disabled || !terms}>
|
||||
<span class="icon-github" aria-hidden="true"></span>
|
||||
<span class="text">Sign up with GitHub</span>
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user