Merge branch 'feat-project-changes-2' into feat-SER-205-new-pricing-component

This commit is contained in:
Harsh Mahajan
2025-08-26 11:08:52 +05:30
committed by GitHub
62 changed files with 679 additions and 439 deletions
+1 -1
View File
@@ -99,7 +99,7 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
with:
images: appwrite/console
tags: |
+3 -3
View File
@@ -22,11 +22,11 @@
},
"dependencies": {
"@ai-sdk/svelte": "^1.1.24",
"@appwrite.io/console": "^1.10.0",
"@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2408",
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@e6b154b",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@0b77850",
"@appwrite.io/pink-legacy": "^1.0.3",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@e6b154b",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@0b77850",
"@popperjs/core": "^2.11.8",
"@sentry/sveltekit": "^8.38.0",
"@stripe/stripe-js": "^3.5.0",
+16 -15
View File
@@ -12,20 +12,20 @@ importers:
specifier: ^1.1.24
version: 1.1.24(svelte@5.25.3)(zod@3.24.3)
'@appwrite.io/console':
specifier: ^1.10.0
version: 1.10.0
specifier: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2408
version: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2408
'@appwrite.io/pink-icons':
specifier: 0.25.0
version: 0.25.0
'@appwrite.io/pink-icons-svelte':
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@e6b154b
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@e6b154b(svelte@5.25.3)
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@0b77850
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@0b77850(svelte@5.25.3)
'@appwrite.io/pink-legacy':
specifier: ^1.0.3
version: 1.0.3
'@appwrite.io/pink-svelte':
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@e6b154b
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@e6b154b(svelte@5.25.3)
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@0b77850
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@0b77850(svelte@5.25.3)
'@popperjs/core':
specifier: ^2.11.8
version: 2.11.8
@@ -257,16 +257,17 @@ packages:
'@analytics/type-utils@0.6.2':
resolution: {integrity: sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg==}
'@appwrite.io/console@1.10.0':
resolution: {integrity: sha512-7ErAZgS0djmB41ZFXPTlJH5kBNvJeKv2UHOEcI6uAOyXO4UoVWlhQk0D/3oktNylwOsr/K/98EWfsUDeYbq68w==}
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2408':
resolution: {tarball: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2408}
version: 1.10.0
'@appwrite.io/pink-icons-svelte@2.0.0-RC.1':
resolution: {integrity: sha512-iLFlV55hj8mGuAbmxJGenxN5RaZMmVT4GJb9dv/MP1xBAtYibFq7JvBcxm18qV2KU8c31Rntf+Ub4GL7HwqTYg==}
peerDependencies:
svelte: ^4.0.0
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@e6b154b':
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@e6b154b}
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@0b77850':
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@0b77850}
version: 2.0.0-RC.1
peerDependencies:
svelte: ^4.0.0
@@ -280,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://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@e6b154b':
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@e6b154b}
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@0b77850':
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@0b77850}
version: 2.0.0-RC.2
peerDependencies:
svelte: ^4.0.0
@@ -3645,13 +3646,13 @@ snapshots:
'@analytics/type-utils@0.6.2': {}
'@appwrite.io/console@1.10.0': {}
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2408': {}
'@appwrite.io/pink-icons-svelte@2.0.0-RC.1(svelte@5.25.3)':
dependencies:
svelte: 5.25.3
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@e6b154b(svelte@5.25.3)':
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@0b77850(svelte@5.25.3)':
dependencies:
svelte: 5.25.3
@@ -3664,7 +3665,7 @@ snapshots:
'@appwrite.io/pink-icons': 1.0.0
the-new-css-reset: 1.11.3
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@e6b154b(svelte@5.25.3)':
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@0b77850(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
@@ -9,17 +9,22 @@
upgradeURL
} from '$lib/stores/billing';
import { currentPlan } from '$lib/stores/organization';
import { onMount } from 'svelte';
import SelectProjectCloud from './selectProjectCloud.svelte';
import { toLocaleDate } from '$lib/helpers/date';
let showSelectProject: boolean = $state(false);
let selectedProjects: string[] = $state([]);
onMount(() => {
selectedProjects = page.data.organization?.projects || [];
let showSelectProject: boolean = $state(false);
const organizationId = $derived(page.data.organization?.$id);
$effect(() => {
if (organizationId) {
selectedProjects = page.data.organization?.projects || [];
}
});
</script>
<SelectProjectCloud bind:showSelectProject bind:selectedProjects />
<SelectProjectCloud bind:showSelectProject bind:selectedProjects {organizationId} />
{#if $currentPlan && $currentPlan.projects > 0 && !hideBillingHeaderRoutes.includes(page.url.pathname)}
<HeaderAlert
@@ -1,31 +1,30 @@
<script lang="ts">
import { type Models } from '@appwrite.io/console';
import { Alert, Button, Table } from '@appwrite.io/pink-svelte';
import { type Models, Query } from '@appwrite.io/console';
import { Alert, Button, Skeleton, Table } from '@appwrite.io/pink-svelte';
import { Modal } from '$lib/components';
import { onMount } from 'svelte';
import { sdk } from '$lib/stores/sdk';
import { addNotification } from '$lib/stores/notifications';
import { invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
import { billingProjectsLimitDate } from '$lib/stores/billing';
import { page } from '$app/state';
import { toLocaleDate, toLocaleDateTime } from '$lib/helpers/date';
import { currentPlan } from '$lib/stores/organization';
let {
showSelectProject = $bindable(false),
selectedProjects = $bindable([])
selectedProjects = $bindable([]),
organizationId
}: {
showSelectProject: boolean;
selectedProjects: string[];
organizationId: string;
} = $props();
let projects = $state<Array<Models.Project>>([]);
let error = $state<string | null>(null);
let loading = $state(false);
let projectsLoadingError = $state<string | null>(null);
onMount(() => {
projects = page.data.allProjects?.projects || [];
});
let error = $state<string | null>(null);
let projects = $state<Array<Models.Project>>([]);
let projectsToArchive = $derived(
projects.filter((project) => !selectedProjects.includes(project.$id))
@@ -37,6 +36,7 @@
projects[0].teamId,
selectedProjects
);
showSelectProject = false;
invalidate(Dependencies.ORGANIZATION);
addNotification({
@@ -67,46 +67,95 @@
return result;
}
$effect(() => {
if (!showSelectProject) return;
const areProjectsLoaded = projects.length > 0;
const teamIdInLoadedProjects = areProjectsLoaded ? projects[0].teamId : null;
if (organizationId != teamIdInLoadedProjects) {
loading = true;
sdk.forConsole.projects
.list([
Query.equal('teamId', organizationId),
Query.limit(1000) // Get all projects for organization
])
.then((loadedProjects) => (projects = loadedProjects.projects))
.catch((err) => (projectsLoadingError = err.message))
.finally(() => (loading = false));
}
});
</script>
<Modal bind:show={showSelectProject} title={'Manage projects'} onSubmit={updateSelected}>
<Modal bind:show={showSelectProject} title="Manage projects" onSubmit={updateSelected}>
<svelte:fragment slot="description">
Choose which two projects to keep. Projects over the limit will be blocked after this date.
Choose which {$currentPlan?.projects || 2} projects to keep. Projects over the limit will be
blocked after this date.
</svelte:fragment>
{#if error}
<Alert.Inline status="error" title="Error">{error}</Alert.Inline>
{/if}
<Table.Root
let:root
allowSelection
bind:selectedRows={selectedProjects}
columns={[{ id: 'name' }, { id: 'created' }]}>
<svelte:fragment slot="header" let:root>
<Table.Header.Cell column="name" {root}>Project Name</Table.Header.Cell>
<Table.Header.Cell column="created" {root}>Created</Table.Header.Cell>
</svelte:fragment>
{#each projects as project}
<Table.Row.Base {root} id={project.$id}>
<Table.Cell column="name" {root}>{project.name}</Table.Cell>
<Table.Cell column="created" {root}
>{toLocaleDateTime(project.$createdAt)}</Table.Cell>
</Table.Row.Base>
{/each}
</Table.Root>
{#if selectedProjects.length > $currentPlan?.projects}
<div class="u-text-warning u-mb-4">
You can only select {$currentPlan?.projects} projects. Please deselect others to continue.
{#if loading}
<div class="skeleton-projects">
<Table.Root let:root allowSelection columns={[{ id: 'name' }, { id: 'created' }]}>
<svelte:fragment slot="header" let:root>
<Table.Header.Cell column="name" {root}>Project Name</Table.Header.Cell>
<Table.Header.Cell column="created" {root}>Created</Table.Header.Cell>
</svelte:fragment>
{#each Array.from({ length: 5 }) as _}
<Table.Row.Base {root} select="disabled">
<Table.Cell column="name" {root}>
<Skeleton variant="line" height={20} width="80%" />
</Table.Cell>
<Table.Cell column="created" {root}>
<Skeleton variant="line" height={20} width="80%" />
</Table.Cell>
</Table.Row.Base>
{/each}
</Table.Root>
</div>
{/if}
{#if selectedProjects.length === $currentPlan?.projects}
<Alert.Inline
status="warning"
title={`${projects.length - selectedProjects.length} projects will be archived on ${toLocaleDate(billingProjectsLimitDate)}`}>
<span>
{@html formatProjectsToArchive()}
will be archived.
</span>
</Alert.Inline>
{:else if projectsLoadingError}
<Alert.Inline status="error" title="Error">{projectsLoadingError}</Alert.Inline>
{:else}
{#if error}
<Alert.Inline status="error" title="Error">{error}</Alert.Inline>
{/if}
<Table.Root
let:root
allowSelection
bind:selectedRows={selectedProjects}
columns={[{ id: 'name' }, { id: 'created' }]}>
<svelte:fragment slot="header" let:root>
<Table.Header.Cell column="name" {root}>Project Name</Table.Header.Cell>
<Table.Header.Cell column="created" {root}>Created</Table.Header.Cell>
</svelte:fragment>
{#each projects as project}
<Table.Row.Base {root} id={project.$id}>
<Table.Cell column="name" {root}>{project.name}</Table.Cell>
<Table.Cell column="created" {root}
>{toLocaleDateTime(project.$createdAt)}</Table.Cell>
</Table.Row.Base>
{/each}
</Table.Root>
{#if selectedProjects.length > $currentPlan?.projects}
<div class="u-text-warning u-mb-4">
You can only select {$currentPlan?.projects} projects. Please deselect others to continue.
</div>
{/if}
{#if selectedProjects.length === $currentPlan?.projects}
<Alert.Inline
status="warning"
title={`${projects.length - selectedProjects.length} projects will be archived on ${toLocaleDate(billingProjectsLimitDate)}`}>
<span>
{@html formatProjectsToArchive()}
will be archived.
</span>
</Alert.Inline>
{/if}
{/if}
<svelte:fragment slot="footer">
<Button.Button size="s" variant="secondary" on:click={() => (showSelectProject = false)}
@@ -115,3 +164,13 @@
>Save</Button.Button>
</svelte:fragment>
</Modal>
<style lang="scss">
/* disable the top select all selector button */
.skeleton-projects :global([role='rowheader'] button) {
opacity: 0.4;
cursor: default;
pointer-events: none;
background: var(--bgcolor-neutral-tertiary);
}
</style>
@@ -42,7 +42,7 @@
const card = await submitStripeCard(name, page?.params?.organization ?? null);
if (card && Object.hasOwn(card, 'id')) {
if ((card as PaymentMethod).card.country === 'US') {
if ((card as PaymentMethod).card?.country === 'US') {
paymentMethod = card as PaymentMethod;
showState = true;
return;
@@ -9,8 +9,8 @@
import { page } from '$app/state';
export let billingPlan: Tier;
export let anyOrgFree = false;
export let isNewOrg = false;
export let anyOrgFree = false;
export let selfService = true;
$: plans = Object.values(page.data.plans.plans) as Plan[];
+36 -8
View File
@@ -22,7 +22,9 @@
import { base } from '$app/paths';
import { currentPlan, newOrgModal } from '$lib/stores/organization';
import { Click, trackEvent } from '$lib/actions/analytics';
import type { Models } from '@appwrite.io/console';
import { type Models, Query } from '@appwrite.io/console';
import { sdk } from '$lib/stores/sdk';
import { page } from '$app/state';
type Organization = {
name: string;
@@ -63,15 +65,15 @@
}
} = createMenu();
let isLoadingProjects = true;
let isLoadingProjects = false;
let loadedProjects: Models.ProjectList = { total: 0, projects: [] };
export let organizations: Organization[] = [];
export let currentProject: Models.Project | null = null;
export let projects: Promise<Models.ProjectList> = Promise.resolve(loadedProjects);
let projectsBottomSheetOpen = false;
let organisationBottomSheetOpen = false;
let projectsBottomSheet: Promise<SheetMenu | null> = null;
function createOrg() {
trackEvent(Click.OrganizationClickCreate, { source: 'breadcrumbs' });
@@ -99,13 +101,25 @@
}
};
async function createProjectsBottomSheet(organization: Organization): Promise<SheetMenu> {
async function createProjectsBottomSheet(
organization: Organization
): Promise<SheetMenu | null> {
if (!isOnProjects) return null;
isLoadingProjects = true;
// null on non-org/project path like `onboarding`.
loadedProjects = (await projects) ?? loadedProjects;
loadedProjects =
(await sdk.forConsole.projects.list([
Query.equal('teamId', organizationId ?? page.data.currentOrgId),
Query.limit(5),
Query.orderDesc('$updatedAt')
])) ?? loadedProjects;
for (const project of loadedProjects.projects) {
project.region ??= 'default';
}
isLoadingProjects = false;
const createProjectItem = {
@@ -185,8 +199,6 @@
$: selectedOrg = organizations.find((org) => org.isSelected);
$: projectsBottomSheet = createProjectsBottomSheet(selectedOrg);
$: organizationsBottomSheet = createOrganizationBottomSheet(selectedOrg);
$: correctPlanName =
@@ -198,6 +210,20 @@
: selectedOrg?.tierName; // fallback
$: derivedKey = `${selectedOrg?.$id}-${currentProject?.$id}`;
$: organizationId = currentProject?.teamId;
$: isOnProjects = page.route.id.includes('project-[region]-[project]');
$: shouldReloadProjects = isLoadingProjects
? false
: currentProject && loadedProjects.projects.length
? loadedProjects.projects[0].teamId != currentProject.teamId
: !loadedProjects.projects.length;
$: if (shouldReloadProjects) {
projectsBottomSheet = createProjectsBottomSheet(selectedOrg);
}
</script>
<svelte:window on:resize={onResize} />
@@ -380,7 +406,9 @@
<BottomSheet.Menu bind:isOpen={organisationBottomSheetOpen} menu={organizationsBottomSheet} />
{#await projectsBottomSheet then menu}
<BottomSheet.Menu bind:isOpen={projectsBottomSheetOpen} {menu} />
{#if menu}
<BottomSheet.Menu bind:isOpen={projectsBottomSheetOpen} {menu} />
{/if}
{/await}
{/key}
+3 -11
View File
@@ -1,15 +1,6 @@
<script lang="ts" context="module">
import type { HTMLAttributes } from 'svelte/elements';
export type NavbarProject = {
name: string;
$id: string;
region: string;
isSelected: boolean;
platformCount: number;
pingCount: number;
};
export type BaseNavbarProps = HTMLAttributes<HTMLHeadElement> & {
logo: {
src: string;
@@ -106,7 +97,6 @@
export let sideBarIsOpen: $$Props['sideBarIsOpen'] = false;
export let showAccountMenu = false;
export let currentProject: Models.Project = undefined;
export let projects: Promise<Models.ProjectList> = undefined;
let activeTheme = $app.theme;
let shouldAnimateThemeToggle = false;
@@ -138,7 +128,9 @@
class="only-desktop">
<img src={logo.src} alt={logo.alt} />
</a>
<Breadcrumbs {organizations} {projects} {currentProject} />
<Breadcrumbs {organizations} {currentProject} />
{#if page.route?.id?.includes('/project-[region]-[project]') && currentProject && currentProject.pingCount === 0}
<div class="only-desktop" style:margin-inline-start="-16px">
<Button.Anchor
@@ -29,7 +29,8 @@
style:width={`${(item.size / maxSize) * 100}%`}>
</div>
<div slot="tooltip">
<span class="u-bold">{item.tooltip.title}</span> ${item.tooltip.label}
<span class="u-bold">{item.tooltip.title}</span>
{item.tooltip.label}
</div>
</Tooltip>
{/each}
+2
View File
@@ -6,10 +6,12 @@ export const NEW_DEV_PRO_UPGRADE_COUPON = 'appw50';
export const REGION_FRA = 'fra';
export const REGION_SYD = 'syd';
export const REGION_NYC = 'nyc';
export const REGION_SFO = 'sfo';
export const SUBDOMAIN_FRA = 'fra.';
export const SUBDOMAIN_SYD = 'syd.';
export const SUBDOMAIN_NYC = 'nyc.';
export const SUBDOMAIN_SFO = 'sfo.';
export enum Dependencies {
FACTORS = 'dependency:factors',
+16 -2
View File
@@ -18,6 +18,19 @@
let error: string;
function coerceToNumber(event: Event & { currentTarget: EventTarget & HTMLInputElement }) {
const raw = event.currentTarget?.value ?? '';
if (raw === '') {
value = nullable ? null : (undefined as unknown as number);
return;
}
const parsed = Number(raw);
if (Number.isFinite(parsed)) {
value = parsed;
}
}
const handleInvalid = (event: Event & { currentTarget: EventTarget & HTMLInputElement }) => {
event.preventDefault();
@@ -39,7 +52,7 @@
error = event.currentTarget.validationMessage;
};
$: if (value) {
$: if (value !== null && value !== undefined && !Number.isNaN(value)) {
error = null;
}
</script>
@@ -61,7 +74,8 @@
helper={error || helper}
state={error ? 'error' : 'default'}
on:invalid={handleInvalid}
on:input>
on:input={coerceToNumber}
on:change={coerceToNumber}>
<svelte:fragment slot="info">
<slot name="info" slot="info" />
</svelte:fragment>
+9 -2
View File
@@ -15,8 +15,15 @@ export function filterRegions(regions: Models.ConsoleRegion[]): RegionOption[] {
return regions
.filter((region) => region.$id !== 'default')
.sort((a, b) => {
if (a.disabled && !b.disabled) return 1;
if (!a.disabled && b.disabled) return -1;
// Check if regions are truly available (not disabled and available)
const aAvailable = !a.disabled && a.available;
const bAvailable = !b.disabled && b.available;
// Prioritize truly available regions
if (aAvailable && !bAvailable) return -1;
if (!aAvailable && bAvailable) return 1;
// Within the same availability group, sort alphabetically
return a.name.localeCompare(b.name);
})
.map((region) => ({
+26
View File
@@ -0,0 +1,26 @@
import type { Models } from '@appwrite.io/console';
/**
* Build VCS repo URL from the template response model.
* Example (GitHub): https://github.com/appwrite/templates-for-sites
*/
export function getTemplateSourceUrl(
t: Models.TemplateSite | Models.TemplateFunction
): string | null {
const owner = t.providerOwner;
const repo = t.providerRepositoryId;
const provider = t.vcsProvider; // e.g., "github"
if (!owner || !repo || !provider) return null;
// Map provider → host (extend if needed)
const hostMap: Record<string, string> = {
github: 'github.com',
gitlab: 'gitlab.com',
bitbucket: 'bitbucket.org'
};
const host = hostMap[provider.toLowerCase()] ?? provider; // fallback
return `https://${host}/${owner}/${repo}`;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 949 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 426 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

+24 -21
View File
@@ -50,6 +50,30 @@
{#if showTitle}
<Typography.Title size="l">Create your project</Typography.Title>
{/if}
{#if projectsLimited}
{#if isProPlan}
<Alert.Inline status="info" title="Extra projects cost $15/month">
You've reached your limit of 2 projects included on the Pro plan. Each extra
project costs $15/month.
</Alert.Inline>
{:else}
<Alert.Inline
status="warning"
title={`You've reached your limit of ${$currentPlan?.projects || 2} projects`}>
Extra projects are available on paid plans for an additional fee
<svelte:fragment slot="actions">
<Button
compact
size="s"
href={`${base}/organization-${page.params.organization}/billing`}
external
text>Upgrade</Button>
</svelte:fragment>
</Alert.Inline>
{/if}
{/if}
<Layout.Stack direction="column" gap="xxl">
<Layout.Stack direction="column" gap="xxl">
<Layout.Stack direction="column" gap="s">
@@ -72,27 +96,6 @@
<CustomId bind:show={showCustomId} name="Project" isProject bind:id />
</Layout.Stack>
{#if projectsLimited}
{#if isProPlan}
<Alert.Inline status="info" title="Extra projects cost $15/month">
You've reached your limit of 2 projects included on the Pro plan. Each extra
project costs $15/month.
</Alert.Inline>
{:else}
<Alert.Inline status="warning" title="You've reached your limit of 2 projects">
Extra projects are available on paid plans for an additional fee
<svelte:fragment slot="actions">
<Button
compact
size="s"
href={`${base}/organization-${page.params.organization}/billing`}
external
text>Upgrade</Button>
</svelte:fragment>
</Alert.Inline>
{/if}
{/if}
{#if isCloud && regions.length > 0}
<Layout.Stack gap="xs">
<Input.Select
+48 -52
View File
@@ -11,7 +11,7 @@
Button,
Badge
} from '@appwrite.io/pink-svelte';
import { isSmallViewport, isTabletViewport } from '$lib/stores/viewport';
import { isSmallViewport } from '$lib/stores/viewport';
const currentYear = new Date().getFullYear();
</script>
@@ -23,7 +23,6 @@
direction="row"
alignItems="center"
gap={$isSmallViewport ? 'm' : 'l'}
inline={$isTabletViewport}
justifyContent="flex-start">
<Typography.Caption variant="400">
ⓒ {currentYear} Appwrite. All rights reserved.
@@ -54,63 +53,37 @@
</Button.Anchor>
</Layout.Stack>
</Layout.Stack>
{#if isCloud && $isSmallViewport}
<div class="extra-margin">
<Layout.Stack direction="row" justifyContent={'flex-start'} alignItems="center">
{#if $version}
{#if isCloud}
<Icon size="s" icon={IconCloud} />
{/if}
<Link.Anchor
size="s"
variant="quiet"
href="https://github.com/appwrite/appwrite/releases"
aria-label="Appwrite releases on Github"
target="_blank"
rel="noreferrer">
Version {$version}
</Link.Anchor>
<span class="divider-wrapper">
<Divider vertical />
</span>
{/if}
<Badge
size="xs"
type="success"
variant="secondary"
content="Generally Available"
style="white-space: nowrap;" />
</Layout.Stack>
</div>
{/if}
<Layout.Stack
direction="row"
justifyContent={$isSmallViewport ? 'flex-start' : 'flex-end'}
alignItems="center">
{#if isCloud}
{#if !$isSmallViewport}
alignItems="center"
wrap={$isSmallViewport ? 'wrap' : 'normal'}>
{#if !$isSmallViewport}
{#if isCloud}
<Badge
size="xs"
type="success"
variant="secondary"
content="Generally Available"
style="white-space: nowrap;" />
<Icon size="s" icon={IconCloud} />
{#if $version}
<Link.Anchor
size="s"
variant="quiet"
href="https://github.com/appwrite/appwrite/releases"
aria-label="Appwrite releases on Github"
target="_blank"
rel="noreferrer">
Version {$version}
</Link.Anchor>
<span class="divider-wrapper">
<Divider vertical />
</span>
{/if}
{/if}
{#if $version}
<Link.Anchor
size="s"
variant="quiet"
href="https://github.com/appwrite/appwrite/releases"
aria-label="Appwrite releases on Github"
target="_blank"
rel="noreferrer"
style="white-space: nowrap;">
Version {$version}
</Link.Anchor>
<span class="divider-wrapper">
<Divider vertical />
</span>
{/if}
{/if}
@@ -157,6 +130,32 @@
Cookies
</Link.Anchor>
{/if}
{#if $isSmallViewport}
{#if $version}
<span class="divider-wrapper">
<Divider vertical />
</span>
<Link.Anchor
size="s"
variant="quiet"
href="https://github.com/appwrite/appwrite/releases"
aria-label="Appwrite releases on Github"
target="_blank"
rel="noreferrer"
style="white-space: nowrap;">
Version {$version}
</Link.Anchor>
{#if isCloud}
<Icon size="s" icon={IconCloud} />
<Badge
size="xs"
type="success"
variant="secondary"
content="Generally Available"
style="white-space: nowrap;" />
{/if}
{/if}
{/if}
</Layout.Stack>
</Layout.Stack>
</footer>
@@ -180,7 +179,4 @@
margin-inline-end: 2rem;
}
}
.extra-margin {
margin-block-start: var(--space-2, 4px);
}
</style>
-3
View File
@@ -22,7 +22,6 @@
export let showFooter = true;
export let showSideNavigation = false;
export let selectedProject: Models.Project = null;
export let projects: Promise<Models.ProjectList> = undefined;
let yOnMenuOpen: number;
let showContentTransition = false;
@@ -104,8 +103,6 @@
};
}),
projects: projects,
currentProject: selectedProject
};
+27 -5
View File
@@ -10,6 +10,7 @@
import type { Campaign } from '$lib/stores/campaigns';
import { Typography, Layout, Avatar } from '@appwrite.io/pink-svelte';
import { getCampaignImageUrl } from '$routes/(public)/card/helpers';
import { isSmallViewport } from '$lib/stores/viewport';
export const imgLight = LoginLight;
export const imgDark = LoginDark;
@@ -44,7 +45,7 @@
<main class="grid-1-1 is-full-page" id="main">
<section
class={`u-flex u-flex-vertical ${variation !== 'default' ? 'u-cross-center u-main-center u-height-100-percent u-width-full-line side-bg' : 'side-default'}`}
class={`u-flex u-flex-vertical ${variation !== 'default' ? 'u-cross-center u-main-center u-height-100-percent u-width-full-line side-bg' : !$isSmallViewport ? 'side-default' : ''}`}
style:--url={variation !== 'default'
? ''
: `url(${$app.themeInUse === 'dark' ? imgDark : imgLight})`}>
@@ -159,7 +160,6 @@
<div class="u-margin-block-start-24">
<slot />
</div>
<ul
class="inline-links is-center is-with-sep u-margin-block-start-32"
class:u-hide={!$$slots?.links}>
@@ -167,18 +167,18 @@
</ul>
</div>
<div
class="logo u-flex u-gap-16 u-margin-inline-auto is-only-mobile u-margin-block-start-32">
class="logo u-flex u-gap-16 u-margin-inline-auto is-only-mobile u-margin-block-start-32 mobile-logo">
<a href={base}>
{#if $app.themeInUse === 'dark'}
<img
src={AppwriteLogoDark}
width="93"
width="120"
class="u-block u-only-dark"
alt="Appwrite Logo" />
{:else}
<img
src={AppwriteLogoLight}
width="93"
width="120"
class="u-block u-only-light"
alt="Appwrite Logo" />
{/if}
@@ -359,4 +359,26 @@
padding-block-start: 5rem;
}
}
// mobile logo positioned at bottom in mobile view
.mobile-logo {
position: absolute;
bottom: 20px;
left: 50%;
transform: translate(-50%, -50%);
max-width: 100%;
margin-block-start: 0 !important;
}
@media #{devices.$break1} {
.auth-container {
margin-block-end: 5rem;
margin-block-start: 2rem;
}
.container {
min-height: 100%;
position: relative;
}
}
</style>
+8 -3
View File
@@ -317,16 +317,21 @@ export function calculateTrialDay(org: Organization) {
return days;
}
export async function checkForProjectsLimit(org: Organization, projects: number) {
export async function checkForProjectsLimit(org: Organization, orgProjectCount?: number) {
if (!isCloud) return;
if (!org || !projects) return;
if (!org) return;
const plan = await sdk.forConsole.billing.getOrganizationPlan(org.$id);
if (!plan) return;
if (plan.$id !== BillingPlan.FREE) return;
if (!org.projects) return;
if (org.projects.length > 0) return;
if (plan.projects > 0 && projects > plan.projects) {
const projectCount = orgProjectCount;
if (projectCount === undefined) return;
if (plan.projects > 0 && projectCount > plan.projects) {
headerAlert.add({
id: 'projectsLimitReached',
component: ProjectsLimit,
+4
View File
@@ -30,8 +30,10 @@ import {
REGION_FRA,
REGION_NYC,
REGION_SYD,
REGION_SFO,
SUBDOMAIN_FRA,
SUBDOMAIN_NYC,
SUBDOMAIN_SFO,
SUBDOMAIN_SYD
} from '$lib/constants';
import { building } from '$app/environment';
@@ -59,6 +61,8 @@ const getSubdomain = (region?: string) => {
return SUBDOMAIN_SYD;
case REGION_NYC:
return SUBDOMAIN_NYC;
case REGION_SFO:
return SUBDOMAIN_SFO;
default:
return '';
}
+1 -1
View File
@@ -102,7 +102,7 @@ export async function submitStripeCard(name: string, organizationId?: string) {
}
if (setupIntent && setupIntent.status === 'succeeded') {
if ((setupIntent.payment_method as PaymentMethod).card.country === 'US') {
if ((setupIntent.payment_method as PaymentMethod).card?.country === 'US') {
// need to get state
return setupIntent.payment_method as PaymentMethod;
}
+4
View File
@@ -103,6 +103,8 @@ const createUploader = () => {
newFile.progress = p.progress;
newFile.status = p.progress === 100 ? 'success' : 'pending';
updateFile(p.$id, newFile);
return {};
}
);
newFile.$id = uploadedFile.$id;
@@ -133,6 +135,8 @@ const createUploader = () => {
newDeployment.progress = p.progress;
newDeployment.status = p.progress === 100 ? 'success' : 'pending';
updateFile(p.$id, newDeployment);
return {};
});
newDeployment.$id = uploadedFile.$id;
newDeployment.progress = 100;
+3 -18
View File
@@ -55,8 +55,6 @@
IconSwitchHorizontal
} from '@appwrite.io/pink-icons-svelte';
import type { LayoutData } from './$types';
import { sdk } from '$lib/stores/sdk';
import { Query } from '@appwrite.io/console';
export let data: LayoutData;
@@ -297,7 +295,9 @@
if (currentOrganizationId === org.$id) return;
if (isCloud) {
currentOrganizationId = org.$id;
checkForProjectsLimit(org, data.allProjects?.projects?.length || 0);
const orgProjectCount =
data.currentOrgId === org.$id ? data.allProjectsCount : undefined;
await checkForProjectsLimit(org, orgProjectCount);
checkForEnterpriseTrial(org);
await checkForUsageLimit(org);
checkForMarkedForDeletion(org);
@@ -318,20 +318,6 @@
$: checkForUsageLimits($organization);
$: isOnOnboarding = page.route?.id?.includes('/(console)/onboarding');
$: projects = isOnOnboarding
? null
: sdk.forConsole.projects.list([
Query.equal(
'teamId',
// id from page params ?? id from store ?? id from preferences
page.params.organization ?? currentOrganizationId ?? data.currentOrgId
),
Query.limit(5),
Query.orderDesc('$updatedAt')
]);
$: if ($requestedMigration) {
openMigrationWizard();
}
@@ -352,7 +338,6 @@
!page.url.pathname.includes('/console/onboarding')}
showHeader={!page.url.pathname.includes('/console/onboarding/create-project')}
showFooter={!page.url.pathname.includes('/console/onboarding/create-project')}
{projects}
selectedProject={page.data?.project}>
<!-- <Header slot="header" />-->
<slot />
+9 -11
View File
@@ -30,19 +30,17 @@ export const load: LayoutLoad = async ({ depends, parent }) => {
(organizations.teams.length > 0 ? organizations.teams[0].$id : undefined);
// Load projects for the current organization if one is selected
let projects = null;
let projectsCount = 0;
if (currentOrgId) {
try {
projects = await sdk.forConsole.projects.list([
Query.equal('teamId', currentOrgId),
Query.limit(1000) // Get all projects for organization
]);
for (const project of projects.projects) {
project.region ??= 'default';
}
projectsCount = (
await sdk.forConsole.projects.list([
Query.equal('teamId', currentOrgId),
Query.limit(1)
])
).total;
} catch (e) {
// Handle error silently - projects might not be accessible
projects = {};
projectsCount = 0;
}
}
@@ -56,7 +54,7 @@ export const load: LayoutLoad = async ({ depends, parent }) => {
organizations,
consoleVariables,
version: versionData?.version ?? null,
allProjects: projects
allProjectsCount: projectsCount
};
};
@@ -13,7 +13,8 @@
import type { PageData } from './$types';
import { addNotification } from '$lib/stores/notifications';
import { onMount } from 'svelte';
import { Badge, Layout, Table, Typography } from '@appwrite.io/pink-svelte';
import { Badge, Layout, Table, Typography, Icon } from '@appwrite.io/pink-svelte';
import { IconGlobeAlt } from '@appwrite.io/pink-icons-svelte';
export let data: PageData;
@@ -90,9 +91,9 @@
let:root
columns={[
{ id: 'client' },
{ id: 'location', width: 120 },
{ id: 'ip', width: 120 },
{ id: 'actions', width: 70 }
{ id: 'location', width: 200 },
{ id: 'ip', width: 200 },
{ id: 'actions', width: 100 }
]}>
<svelte:fragment slot="header" let:root>
<Table.Header.Cell column="client" {root}>Client</Table.Header.Cell>
@@ -112,13 +113,9 @@
height="20"
width="20"
src={getBrowser(session.clientCode).toString()}
style="--p-text-size: 1.25rem"
alt={session.clientName} />
{:else}
<span
class="icon-globe-alt"
style="--p-text-size: 1.25rem"
aria-hidden="true"></span>
<Icon icon={IconGlobeAlt} size="s" />
{/if}
</div>
<Trim>
+11 -11
View File
@@ -1,12 +1,12 @@
import { isCloud } from '$lib/system';
import { isSameDay } from '$lib/helpers/date';
import { type BottomModalAlertItem, showBottomModalAlert } from '$lib/stores/bottom-alerts';
import AutoIncrementDark from '$lib/images/promos/auto-increment-dark.png';
import AutoIncrementLight from '$lib/images/promos/auto-increment-light.png';
import AtomicNumericOperationsDark from '$lib/images/promos/atomic-numeric-operations-dark.png';
import AtomicNumericOperationsLight from '$lib/images/promos/atomic-numeric-operations-light.png';
import AppwriteGeneralAvailabiltyLight from '$lib/images/promos/appwrite-general-availability-light.png';
import AppwriteGeneralAvailabiltyDark from '$lib/images/promos/appwrite-general-availability-dark.png';
import TimestampOverridesDark from '$lib/images/promos/timestamp-overrides-dark.png';
import TimestampOverridesLight from '$lib/images/promos/timestamp-overrides-light.png';
const listOfPromotions: BottomModalAlertItem[] = [];
@@ -30,21 +30,21 @@ if (isCloud) {
},
show: true
};
const autoIncrementPromo: BottomModalAlertItem = {
id: 'modal:auto_increment_announcement',
const timestampOverridesPromo: BottomModalAlertItem = {
id: 'modal:timestamp_overrides_announcement',
src: {
dark: AutoIncrementDark,
light: AutoIncrementLight
dark: TimestampOverridesDark,
light: TimestampOverridesLight
},
title: 'Announcing Auto-increment support',
title: 'Announcing Timestamp Overrides',
message:
'Get a built-in numeric identifier that increases predictably with every new document added.',
'Move historical data into Appwrite without losing context or disrupting chronological accuracy.',
plan: 'free',
importance: 8,
scope: 'project',
cta: {
text: 'Read announcement',
link: () => 'https://appwrite.io/blog/post/announcing-auto-increment-support',
link: () => 'https://appwrite.io/blog/post/announcing-timestamp-overrides',
external: true,
hideOnClick: true
},
@@ -71,9 +71,9 @@ if (isCloud) {
show: true
};
listOfPromotions.push(
timestampOverridesPromo,
appwriteGeneralAvailabiltyPromo,
atomicNumericOperationsPromo,
autoIncrementPromo
atomicNumericOperationsPromo
);
}
@@ -114,9 +114,8 @@
trackError(e, Submit.ProjectCreate);
}
};
onMount(async () => {
checkPricingRefAndRedirect(page.url.searchParams);
});
onMount(async () => checkPricingRefAndRedirect(page.url.searchParams));
function findRegion(project: Models.Project) {
return $regionsStore.regions.find((region) => region.$id === project.region);
@@ -140,7 +139,10 @@
);
</script>
<SelectProjectCloud selectedProjects={data.organization.projects || []} bind:showSelectProject />
<SelectProjectCloud
bind:showSelectProject
organizationId={page.params.organization}
selectedProjects={data.organization.projects || []} />
<Container>
<div class="u-flex u-gap-12 common-section u-main-space-between">
@@ -167,7 +169,7 @@
</DropList>
</div>
{#if isCloud && $currentPlan?.projects && $currentPlan?.projects > 0 && data.organization.projects.length > 0 && data.projects.total > 2 && $canWriteProjects}
{#if isCloud && $currentPlan?.projects && $currentPlan?.projects > 0 && data.organization.projects.length > 0 && data.projects.total > $currentPlan.projects && $canWriteProjects}
<Alert.Inline
title={`${data.projects.total - data.organization.projects.length} projects will be archived on ${toLocaleDate(billingProjectsLimitDate)}`}>
<Typography.Text>
@@ -54,7 +54,7 @@
} else {
const card = await submitStripeCard(name, organization.$id);
if (card && Object.hasOwn(card, 'id')) {
if ((card as PaymentMethod).card.country === 'US') {
if ((card as PaymentMethod).card?.country === 'US') {
paymentMethod = card as PaymentMethod;
showState = true;
return;
@@ -66,7 +66,7 @@
} else {
const card = await submitStripeCard(name, $organization.$id);
if (card && Object.hasOwn(card, 'id')) {
if ((card as PaymentMethod).card.country === 'US') {
if ((card as PaymentMethod).card?.country === 'US') {
paymentMethod = card as PaymentMethod;
showState = true;
return;
@@ -34,7 +34,7 @@
} else {
const card = await submitStripeCard(name, $organization.$id);
if (card && Object.hasOwn(card, 'id')) {
if ((card as PaymentMethod).card.country === 'US') {
if ((card as PaymentMethod).card?.country === 'US') {
paymentMethod = card as PaymentMethod;
showState = true;
return;
@@ -150,8 +150,7 @@
await goto(previousPage);
addNotification({
type: 'success',
isHtml: true,
message: `<b>${$organization.name}</b> plan has been successfully updated.`
message: `${$organization.name} plan has been successfully updated.`
});
trackEvent(Submit.OrganizationDowngrade, {
@@ -262,9 +261,11 @@
}
}
$: isUpgrade = $plansInfo.get(selectedPlan)?.order > $currentPlan?.order;
$: isDowngrade = $plansInfo.get(selectedPlan)?.order < $currentPlan?.order;
$: isButtonDisabled = $organization?.billingPlan === selectedPlan;
$: isUpgrade = $plansInfo.get(selectedPlan).order > $currentPlan?.order;
$: isDowngrade = $plansInfo.get(selectedPlan).order < $currentPlan?.order;
$: isButtonDisabled =
$organization?.billingPlan === selectedPlan ||
(isDowngrade && selectedPlan === BillingPlan.FREE && data.hasFreeOrgs);
</script>
<svelte:head>
@@ -297,8 +298,21 @@
selfService={data.selfService}
anyOrgFree={data.hasFreeOrgs} />
{#if isDowngrade && selectedPlan === BillingPlan.FREE && data.hasFreeOrgs}
<Alert.Inline
status="warning"
title="You can only have one free organization per account">
To downgrade this organization, first migrate or delete one of your
existing paid organizations.
<Button
compact
href="https://appwrite.io/docs/advanced/migrations/cloud"
>Migration guide</Button>
</Alert.Inline>
{/if}
{#if isDowngrade}
{#if selectedPlan === BillingPlan.FREE}
{#if selectedPlan === BillingPlan.FREE && !data.hasFreeOrgs}
<PlanExcess tier={BillingPlan.FREE} />
{:else if selectedPlan === BillingPlan.PRO && data.organization.billingPlan === BillingPlan.SCALE && collaborators?.length > 0}
{@const extraMembers = collaborators?.length ?? 0}
@@ -364,7 +378,7 @@
id="members" />
</Fieldset>
{/if}
{#if isDowngrade && selectedPlan === BillingPlan.FREE}
{#if isDowngrade && selectedPlan === BillingPlan.FREE && !data.hasFreeOrgs}
<Fieldset legend="Feedback">
<Layout.Stack gap="xl">
<InputSelect
@@ -395,7 +409,7 @@
bind:billingBudget
organizationId={data.organization.$id} />
{:else if data.organization.billingPlan !== BillingPlan.CUSTOM}
<PlanComparisonBox downgrade={isDowngrade} />
<PlanComparisonBox downgrade={data.hasFreeOrgs ? false : isDowngrade} />
{/if}
</svelte:fragment>
<svelte:fragment slot="footer">
@@ -12,8 +12,8 @@
import { Dependencies } from '$lib/constants';
export let teamId: string;
export let showCreateProjectCloud: boolean;
export let projects: number;
export let showCreateProjectCloud: boolean;
export let regions: Array<Models.ConsoleRegion> = [];
let id: string = null;
@@ -11,6 +11,7 @@
import { PaginationWithLimit } from '$lib/components';
import {
ActionMenu,
Badge,
Button,
InteractiveText,
Icon,
@@ -79,11 +80,24 @@
{record.type}
</Typography.Text>
{:else if column.id === 'value'}
{@const isARecord = record.value === 'a.a.a.a'}
{#if isARecord}
{@const isARecord = record.type.toLowerCase() === 'a'}
{@const isAAAARecord = record.type.toLowerCase() === 'aaaa'}
{@const isCAARecord = record.type.toLowerCase() === 'caa'}
{#if record.lock && (isARecord || isAAAARecord)}
<!-- to align with InteractiveText -->
<div style:padding-inline-start="4px">
<Typography.Text>Served by Appwrite</Typography.Text>
<Badge
variant="secondary"
size="s"
content="Served by Appwrite" />
</div>
{:else if record.lock && isCAARecord}
<!-- to align with InteractiveText -->
<div style:padding-inline-start="4px">
<Badge
variant="secondary"
size="s"
content="Generated by Appwrite" />
</div>
{:else}
<InteractiveText variant="copy" text={record.value} isVisible />
@@ -1,18 +1,10 @@
<script lang="ts">
import { EmptySearch } from '$lib/components';
import { Badge } from '@appwrite.io/pink-svelte';
import { EmptySearch, Trim } from '$lib/components';
import { Badge, Layout, Table, Typography, Icon } from '@appwrite.io/pink-svelte';
import { IconGlobeAlt } from '@appwrite.io/pink-icons-svelte';
import { Button } from '$lib/elements/forms';
import {
TableBody,
TableCell,
TableCellHead,
TableCellText,
TableHeader,
TableRow,
TableScroll
} from '$lib/elements/table';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Container, ContainerHeader } from '$lib/layout';
import { Container } from '$lib/layout';
import { sdk } from '$lib/stores/sdk';
import { Browser } from '@appwrite.io/console';
import DeleteAllSessions from '../deleteAllSessions.svelte';
@@ -33,77 +25,81 @@
</script>
<Container>
<ContainerHeader title="Sessions">
<Layout.Stack direction="row" justifyContent="space-between">
<Typography.Title>Sessions</Typography.Title>
{#if data.sessions.total}
<Button secondary on:click={() => (showDeleteAll = true)}>
<span class="text">Delete All</span>
</Button>
<Button secondary on:click={() => (showDeleteAll = true)}>Sign out all sessions</Button>
{/if}
</ContainerHeader>
</Layout.Stack>
{#if data.sessions.total}
<TableScroll>
<TableHeader>
<TableCellHead width={140}>Browser and device</TableCellHead>
<TableCellHead width={140}>Session</TableCellHead>
<TableCellHead width={140}>Location</TableCellHead>
<TableCellHead width={140}>IP</TableCellHead>
<TableCellHead width={30} />
</TableHeader>
<TableBody>
{#each data.sessions.sessions as session}
{@const browser = getBrowser(session.clientCode)}
<TableRow>
<TableCell title="Client">
<div class="u-flex u-gap-12 u-cross-center">
<div class="avatar">
{#if browser}
<img
height="20"
width="20"
src={getBrowser(session.clientCode).toString()}
style="--p-text-size: 1.25rem"
alt={session.clientName} />
{:else}
<span
class="icon-globe-alt"
style="--p-text-size: 1.25rem"
aria-hidden="true">
</span>
{/if}
</div>
<p class="text">
{session.clientName}
{session.clientVersion} on {session.osName}
{session.osVersion}
</p>
{#if session.current}
<Badge
variant="secondary"
type="success"
content="current session" />
<Table.Root
let:root
columns={[
{ id: 'client' },
{ id: 'location', width: 200 },
{ id: 'ip', width: 200 },
{ id: 'actions', width: 100 }
]}>
<svelte:fragment slot="header" let:root>
<Table.Header.Cell column="client" {root}>Client</Table.Header.Cell>
<Table.Header.Cell column="location" {root}>Location</Table.Header.Cell>
<Table.Header.Cell column="ip" {root}>IP</Table.Header.Cell>
<Table.Header.Cell column="actions" {root} />
</svelte:fragment>
{#each data.sessions.sessions as session}
{@const browser = getBrowser(session.clientCode)}
<Table.Row.Base {root}>
<Table.Cell column="client" {root}>
<Layout.Stack direction="row" alignItems="center">
<div class="avatar is-size-small">
{#if browser}
<img
height="20"
width="20"
src={getBrowser(session.clientCode).toString()}
alt={session.clientName} />
{:else}
<Icon icon={IconGlobeAlt} size="s" />
{/if}
</div>
</TableCell>
<TableCellText title="Session">{session.clientType}</TableCellText>
<TableCellText title="Location">{session.countryName}</TableCellText>
<TableCellText title="IP">{session.ip}</TableCellText>
<TableCell>
<Button
text
icon
ariaLabel="Delete item"
on:click={() => {
selectedSessionId = session.$id;
showDelete = true;
}}>
<span class="icon-trash" aria-hidden="true"></span>
</Button>
</TableCell>
</TableRow>
{/each}
</TableBody>
</TableScroll>
<Trim>
{session.clientName}
{session.clientVersion} on {session.osName}
{session.osVersion}
</Trim>
{#if session.current}
<Badge
type="success"
variant="secondary"
content="current session" />
{/if}
</Layout.Stack>
</Table.Cell>
<Table.Cell column="location" {root}>
{#if session.countryCode !== '--'}
{session.countryName}
{:else}
Unknown
{/if}
</Table.Cell>
<Table.Cell column="ip" {root}>
{session.ip}
</Table.Cell>
<Table.Cell column="actions" {root}>
<Button
size="xs"
secondary
on:click={() => {
selectedSessionId = session.$id;
showDelete = true;
}}>
Sign out
</Button>
</Table.Cell>
</Table.Row.Base>
{/each}
</Table.Root>
{:else}
<EmptySearch>
<div class="u-flex u-flex-vertical u-cross-center u-gap-24">
@@ -123,16 +123,12 @@
// Reactive statements
$: collections = collectionList?.collections?.filter((n) => n.$id !== $collection.$id) ?? [];
$: if (editing) {
way = data.twoWay ? 'two' : 'one';
} else {
if (way === 'two') {
data.twoWay = true;
if (!data.twoWayKey) {
data.twoWayKey = camelize($collection.name);
}
} else {
data.twoWay = false;
$: if (!editing && way) {
data.twoWay = way === 'two';
if (way === 'two' && !data.twoWayKey) {
data.twoWayKey = camelize($collection.name);
} else if (way === 'one') {
data.twoWayKey = undefined;
}
}
@@ -158,6 +154,7 @@
bind:group={way}
name="one"
value="one"
disabled={editing}
icon={IconArrowSmRight}>
One Relation attribute within this collection
</Card.Selector>
@@ -166,6 +163,7 @@
bind:group={way}
name="two"
value="two"
disabled={editing}
icon={IconSwitchHorizontal}>
One Relation attribute within this collection and another within the related collection
</Card.Selector>
@@ -178,6 +176,7 @@
placeholder="Select a collection"
bind:value={data.relatedCollection}
on:change={updateKeyName}
disabled={editing}
options={collections?.map((n) => ({ value: n.$id, label: `${n.name} (${n.$id})` })) ?? []} />
{#if data?.relatedCollection}
@@ -14,8 +14,6 @@
import { type Attributes, collection, indexes } from '../store';
import { Icon, Layout } from '@appwrite.io/pink-svelte';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import { flags } from '$lib/flags';
export let showCreateIndex = false;
export let externalAttribute: Attributes = null;
@@ -39,8 +37,6 @@
let attributeList = [{ value: '', order: '', length: null }];
const showLengths = flags.showIndexLengths(page.data);
function generateIndexKey() {
let indexKeys = $indexes.map((index) => index.key);
@@ -152,7 +148,7 @@
placeholder="Select Order" />
<Layout.Stack direction="row" alignItems="flex-end" gap="xs">
{#if selectedType === IndexType.Key && showLengths}
{#if selectedType === IndexType.Key}
<InputNumber
id={`length-${i}`}
label={i === 0 ? 'Length' : undefined}
@@ -3,13 +3,9 @@
import { InputText } from '$lib/elements/forms';
import { Layout } from '@appwrite.io/pink-svelte';
import type { Models } from '@appwrite.io/console';
import { page } from '$app/state';
import { flags } from '$lib/flags';
export let showOverview = false;
export let selectedIndex: Models.Index = null;
const showLengths = flags.showIndexLengths(page.data);
</script>
<Modal title="Overview" bind:show={showOverview}>
@@ -43,14 +39,12 @@
id={`value-${selectedIndex.orders[i]}`}
value={selectedIndex.orders[i]}
readonly />
{#if showLengths}
<InputText
required
label={i === 0 ? 'Length' : ''}
id={`value-${selectedIndex.lengths[i]}`}
value={selectedIndex.lengths[i]?.toString() ?? null}
readonly />
{/if}
<InputText
required
label={i === 0 ? 'Length' : ''}
id={`value-${selectedIndex.lengths[i]}`}
value={selectedIndex.lengths[i]?.toString() ?? null}
readonly />
</Layout.Stack>
{/each}
{/if}
@@ -3,6 +3,13 @@ import { writable } from 'svelte/store';
export const columns = writable<Column[]>([
{ id: '$id', title: 'Execution ID', type: 'string', width: { min: 200, max: 250 } },
{
id: 'deploymentId',
title: 'Deployment ID',
type: 'string',
width: { min: 210 },
hide: true
},
{
id: 'requestPath',
title: 'Path',
@@ -10,7 +17,6 @@ export const columns = writable<Column[]>([
width: { min: 200 },
format: 'string'
},
{
id: 'trigger',
title: 'Trigger',
@@ -84,6 +84,8 @@
{#key column.id}
<Id value={log.$id}>{log.$id}</Id>
{/key}
{:else if column.id === 'deploymentId'}
<Id value={log.deploymentId}>{log.deploymentId}</Id>
{:else if column.id === '$createdAt'}
<DualTimeView time={log.$createdAt} />
{:else if column.id === 'requestPath'}
@@ -37,7 +37,8 @@
func.providerRepositoryId || undefined,
func.providerBranch || undefined,
func.providerSilentMode || undefined,
func.providerRootDirectory || undefined
func.providerRootDirectory || undefined,
func.specification || undefined
);
await invalidate(Dependencies.FUNCTION);
addNotification({
@@ -227,7 +227,7 @@
<FloatingActionBar>
<svelte:fragment slot="start">
<Badge content={selectedRows.length.toString()} />
<span>
<span style="white-space: nowrap">
{selectedRows.length > 1 ? 'deployments' : 'deployment'}
selected
</span>
@@ -49,6 +49,10 @@
height: var(--icon-size);
color: var(--icon-color);
}
& :global(img) {
inline-size: var(--icon-size) !important;
}
}
:global(.theme-dark) {
@@ -24,7 +24,7 @@
import ConnectionLine from './components/ConnectionLine.svelte';
import OnboardingPlatformCard from './components/OnboardingPlatformCard.svelte';
import { PlatformType } from '@appwrite.io/console';
import { isCloud } from '$lib/system';
import { project } from '../../store';
let showExitModal = false;
let isPlatformCreated = false;
@@ -35,11 +35,9 @@
const gitCloneCode =
'\ngit clone https://github.com/appwrite/starter-for-android\ncd starter-for-android\n';
const updateConfigCode = isCloud
? `const val APPWRITE_PROJECT_ID = "${projectId}"`
: `const val APPWRITE_PROJECT_ID = "${projectId}"
const val APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.region, page.params.project).client.config.endpoint}"
`;
const configCode = `const val APPWRITE_PROJECT_ID = "${projectId}"
const val APPWRITE_PROJECT_NAME = "${$project.name}"
const val APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.region, page.params.project).client.config.endpoint}"`;
async function createAndroidPlatform() {
try {
@@ -57,6 +55,12 @@ const val APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.region, page.
trackEvent(Submit.PlatformCreate, {
type: 'android'
});
addNotification({
type: 'success',
message: 'Platform created.'
});
invalidate(Dependencies.PROJECT);
invalidate(Dependencies.PLATFORMS);
} catch (error) {
@@ -158,7 +162,7 @@ const val APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.region, page.
<!-- Step Two -->
{#if isPlatformCreated}
<Fieldset legend="Clone starter">
<Fieldset legend="Clone starter" badge="Optional">
<Layout.Stack gap="l">
<Typography.Text variant="m-500">
1. If you're starting a new project, you can clone our starter kit from
@@ -171,14 +175,12 @@ const val APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.region, page.
</div>
<Typography.Text variant="m-500"
>2. Open the file <InlineCode
size="s"
code="data/repository/AppwriteRepository.kt" /> and update the configuration
settings.</Typography.Text>
>2. Open the file <InlineCode size="s" code="constants/AppwriteConfig.kt" />
and update the configuration settings.</Typography.Text>
<!-- Temporary fix: Remove this div once Code splitting issue with stack spacing is resolved -->
<div class="pink2-code-margin-fix">
<Code lang="kotlin" lineNumbers code={updateConfigCode} />
<Code lang="kotlin" lineNumbers code={configCode} />
</div>
<Typography.Text variant="m-500"
@@ -241,7 +243,7 @@ const val APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.region, page.
secondary
disabled={isCreatingPlatform}
href={location.pathname}>
Go to dashboard
Skip, go to dashboard
</Button>
{/if}
</svelte:fragment>
@@ -25,8 +25,8 @@
import ConnectionLine from './components/ConnectionLine.svelte';
import OnboardingPlatformCard from './components/OnboardingPlatformCard.svelte';
import { PlatformType } from '@appwrite.io/console';
import { isCloud } from '$lib/system';
import { app } from '$lib/stores/app';
import { project } from '../../store';
let showExitModal = false;
let isPlatformCreated = false;
@@ -37,11 +37,9 @@
const gitCloneCode =
'\ngit clone https://github.com/appwrite/starter-for-ios\ncd starter-for-ios\n';
const updateConfigCode = isCloud
? `APPWRITE_PROJECT_ID: "${projectId}"`
: `APPWRITE_PROJECT_ID: "${projectId}"
APPWRITE_PUBLIC_ENDPOINT: "${sdk.forProject(page.params.region, page.params.project).client.config.endpoint}"
`;
const configCode = `APPWRITE_PROJECT_ID: "${projectId}"
APPWRITE_PROJECT_NAME: "${$project.name}"
APPWRITE_PUBLIC_ENDPOINT: "${sdk.forProject(page.params.region, page.params.project).client.config.endpoint}"`;
export let platform: PlatformType = PlatformType.Appleios;
@@ -68,6 +66,12 @@ APPWRITE_PUBLIC_ENDPOINT: "${sdk.forProject(page.params.region, page.params.proj
trackEvent(Submit.PlatformCreate, {
type: platform
});
addNotification({
type: 'success',
message: 'Platform created.'
});
invalidate(Dependencies.PROJECT);
invalidate(Dependencies.PLATFORMS);
} catch (error) {
@@ -188,7 +192,7 @@ APPWRITE_PUBLIC_ENDPOINT: "${sdk.forProject(page.params.region, page.params.proj
<!-- Step Three -->
{#if isPlatformCreated}
<Fieldset legend="Clone starter">
<Fieldset legend="Clone starter" badge="Optional">
<Layout.Stack gap="l">
<Typography.Text variant="m-500">
1. If you're starting a new project, you can clone our starter kit from
@@ -206,7 +210,7 @@ APPWRITE_PUBLIC_ENDPOINT: "${sdk.forProject(page.params.region, page.params.proj
<!-- Temporary fix: Remove this div once Code splitting issue with stack spacing is resolved -->
<div class="pink2-code-margin-fix">
<Code lang="plaintext" lineNumbers code={updateConfigCode} />
<Code lang="plaintext" lineNumbers code={configCode} />
</div>
<Typography.Text variant="m-500"
@@ -268,7 +272,7 @@ APPWRITE_PUBLIC_ENDPOINT: "${sdk.forProject(page.params.region, page.params.proj
secondary
disabled={isCreatingPlatform}
href={location.pathname}>
Go to dashboard
Skip, go to dashboard
</Button>
{/if}
</svelte:fragment>
@@ -25,7 +25,7 @@
import ConnectionLine from './components/ConnectionLine.svelte';
import OnboardingPlatformCard from './components/OnboardingPlatformCard.svelte';
import { PlatformType } from '@appwrite.io/console';
import { isCloud } from '$lib/system';
import { project } from '../../store';
let showExitModal = false;
let isPlatformCreated = false;
@@ -36,11 +36,11 @@
const gitCloneCode =
'\ngit clone https://github.com/appwrite/starter-for-flutter\ncd starter-for-flutter\n';
const updateConfigCode = isCloud
? `static const String APPWRITE_PROJECT_ID = "${projectId}"`
: `static const String APPWRITE_PROJECT_ID = "${projectId}"
static const String APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.region, page.params.project).client.config.endpoint}"
`;
const configCode = `class Environment {
static const String appwriteProjectId = '${projectId}';
static const String appwriteProjectName = '${$project.name}';
static const String appwritePublicEndpoint = '${sdk.forProject(page.params.region, page.params.project).client.config.endpoint}';
}`;
export let platform: PlatformType = PlatformType.Flutterandroid;
@@ -127,6 +127,11 @@ static const String APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.reg
type: platform
});
addNotification({
type: 'success',
message: 'Platform created.'
});
invalidate(Dependencies.PROJECT);
invalidate(Dependencies.PLATFORMS);
} catch (error) {
@@ -263,7 +268,7 @@ static const String APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.reg
<!-- Step Three -->
{#if isPlatformCreated}
<Fieldset legend="Clone starter">
<Fieldset legend="Clone starter" badge="Optional">
<Layout.Stack gap="l">
<Typography.Text variant="m-500">
1. If you're starting a new project, you can clone our starter kit from
@@ -276,16 +281,18 @@ static const String APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.reg
</div>
<Typography.Text variant="m-500"
>2. Open the file <InlineCode size="s" code="lib/constants/appwrite.dart" />
and update the configuration settings.</Typography.Text>
>2. Replace <InlineCode size="s" code="lib/config/environment.dart" />
to reflect the values below:</Typography.Text>
<!-- Temporary fix: Remove this div once Code splitting issue with stack spacing is resolved -->
<div class="pink2-code-margin-fix">
<Code lang="dart" lineNumbers code={updateConfigCode} />
<Code lang="dart" lineNumbers code={configCode} />
</div>
<Typography.Text variant="m-500"
>3. Run the app on a connected device or simulator, then click the <InlineCode
>3. Run the app on a connected device or simulator using <InlineCode
size="s"
code="flutter run -d [device_name]" />, then click the <InlineCode
size="s"
code="Send a ping" /> button to verify the setup.</Typography.Text>
</Layout.Stack>
@@ -343,7 +350,7 @@ static const String APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.reg
secondary
disabled={isCreatingPlatform}
href={location.pathname}>
Go to dashboard
Skip, go to dashboard
</Button>
{/if}
</svelte:fragment>
@@ -25,6 +25,7 @@
import ConnectionLine from './components/ConnectionLine.svelte';
import OnboardingPlatformCard from './components/OnboardingPlatformCard.svelte';
import { PlatformType } from '@appwrite.io/console';
import { project } from '../../store';
let showExitModal = false;
let isPlatformCreated = false;
@@ -36,6 +37,7 @@
'\ngit clone https://github.com/appwrite/starter-for-react-native\ncd starter-for-react-native\n';
const updateConfigCode = `EXPO_PUBLIC_APPWRITE_PROJECT_ID=${projectId}
EXPO_PUBLIC_APPWRITE_PROJECT_NAME="${$project.name}"
EXPO_PUBLIC_APPWRITE_ENDPOINT=${sdk.forProject(page.params.region, page.params.project).client.config.endpoint}`;
export let platform: PlatformType = PlatformType.Reactnativeandroid;
@@ -91,6 +93,11 @@ EXPO_PUBLIC_APPWRITE_ENDPOINT=${sdk.forProject(page.params.region, page.params.p
type: platform
});
addNotification({
type: 'success',
message: 'Platform created.'
});
invalidate(Dependencies.PROJECT);
invalidate(Dependencies.PLATFORMS);
} catch (error) {
@@ -210,7 +217,7 @@ EXPO_PUBLIC_APPWRITE_ENDPOINT=${sdk.forProject(page.params.region, page.params.p
<!-- Step Three -->
{#if isPlatformCreated}
<Fieldset legend="Clone starter">
<Fieldset legend="Clone starter" badge="Optional">
<Layout.Stack gap="l">
<Typography.Text variant="m-500">
1. If you're starting a new project, you can clone our starter kit from
@@ -235,7 +242,13 @@ EXPO_PUBLIC_APPWRITE_ENDPOINT=${sdk.forProject(page.params.region, page.params.p
</div>
<Typography.Text variant="m-500"
>3. Run the app on a connected device or simulator, then click the <InlineCode
>3. Run the app on a connected device or simulator using <InlineCode
size="s"
code="npm install" /> followed by <InlineCode
size="s"
code={platform === PlatformType.Reactnativeios
? 'npm run ios'
: 'npm run android'} />, then click the <InlineCode
size="s"
code="Send a ping" /> button to verify the setup.</Typography.Text>
</Layout.Stack>
@@ -290,7 +303,7 @@ EXPO_PUBLIC_APPWRITE_ENDPOINT=${sdk.forProject(page.params.region, page.params.p
secondary
disabled={isCreatingPlatform}
href={location.pathname}>
Go to dashboard
Skip, go to dashboard
</Button>
{/if}
</svelte:fragment>
@@ -128,7 +128,15 @@ ${prefix}APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.p
smallIcon: IconAngular,
portNumber: 4200,
runCommand: 'npm run start',
updateConfigCode: `appwriteEndpoint: '${sdk.forProject(page.params.region, page.params.project).client.config.endpoint}',\nappwriteProjectId: '${projectId}',\nappwriteProjectName: '${$project.name}'`
updateConfigCode: `export const environment: {
appwriteEndpoint: string;
appwriteProjectId: string;
appwriteProjectName: string;
} = {
appwriteEndpoint: '${sdk.forProject(page.params.region, page.params.project).client.config.endpoint}',
appwriteProjectId: '${projectId}',
appwriteProjectName: '${$project.name}'
};`
},
{
key: 'js',
@@ -167,6 +175,11 @@ ${prefix}APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.p
type: platform
});
addNotification({
type: 'success',
message: 'Platform created.'
});
invalidate(Dependencies.PROJECT);
invalidate(Dependencies.PLATFORMS);
} catch (error) {
@@ -273,7 +286,7 @@ ${prefix}APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.p
<!-- Step Three -->
{#if isPlatformCreated && !isChangingFramework}
<Fieldset legend="Clone starter">
<Fieldset legend="Clone starter" badge="Optional">
<Layout.Stack gap="l">
<Typography.Text variant="m-500">
1. If you're starting a new project, you can clone our starter kit from
@@ -290,7 +303,7 @@ ${prefix}APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.p
{#if selectedFramework.key === 'angular'}
<Typography.Text variant="m-500"
>2. Change <InlineCode
>2. Replace <InlineCode
size="s"
code="src/environments/environment.ts" />
to reflect the values below:</Typography.Text>
@@ -303,7 +316,10 @@ ${prefix}APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.p
<!-- Temporary fix: Remove this div once Code splitting issue with stack spacing is resolved -->
<div class="pink2-code-margin-fix">
<Code lang="dotenv" lineNumbers code={selectedFramework.updateConfigCode} />
<Code
lang={selectedFramework.key === 'angular' ? 'ts' : 'dotenv'}
lineNumbers
code={selectedFramework.updateConfigCode} />
</div>
<Typography.Text variant="m-500"
@@ -391,7 +407,7 @@ ${prefix}APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.p
secondary
fullWidthMobile
href={location.pathname}
disabled={isCreatingPlatform}>Go to dashboard</Button>
disabled={isCreatingPlatform}>Skip, go to dashboard</Button>
{/if}
</svelte:fragment>
</Wizard>
@@ -123,7 +123,7 @@
single
allowCreate={$canWriteSites}
href="https://appwrite.io/docs/products/sites"
description="Deploy and manage your web your web applications with Sites. "
description="Deploy and manage your web applications with Sites. "
target="site"
src={$app.themeInUse === 'dark' ? EmptyDark : EmptyLight}
on:click={() => (show = true)}>
@@ -62,6 +62,7 @@
</Typography.Text>
</Layout.Stack>
</Layout.Stack>
<slot name="framework-actions" />
{/if}
{#if domain && showAfter}
@@ -18,23 +18,25 @@
let deployment = $state(data.deployment);
onMount(() => {
return sdk.forConsole.client.subscribe(
'console',
async (response: RealtimeResponseEvent<Models.Deployment>) => {
if (
response.events.includes(
`sites.${data.site.$id}.deployments.${data.deployment.$id}.update`
)
) {
deployment = response.payload;
if (response.payload.status === 'ready') {
goto(
`${base}/project-${page.params.region}-${page.params.project}/sites/create-site/finish?site=${data.site.$id}`
);
return sdk
.forConsoleIn(page.params.region)
.client.subscribe(
'console',
async (response: RealtimeResponseEvent<Models.Deployment>) => {
if (
response.events.includes(
`sites.${data.site.$id}.deployments.${data.deployment.$id}.update`
)
) {
deployment = response.payload;
if (response.payload.status === 'ready') {
goto(
`${base}/project-${page.params.region}-${page.params.project}/sites/create-site/finish?site=${data.site.$id}`
);
}
}
}
}
);
);
});
</script>
@@ -37,6 +37,7 @@
import { connectGitHub } from '$lib/stores/git';
import { getFrameworkIcon } from '$lib/stores/sites';
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
import { getTemplateSourceUrl } from '$lib/helpers/templateSource';
export let data;
@@ -351,6 +352,16 @@
alt={data.template.name}
ratio="16/9" />
</Layout.Stack>
<svelte:fragment slot="framework-actions">
{@const sourceUrl = getTemplateSourceUrl(data.template)}
{#if sourceUrl}
<Button secondary size="s" external href={sourceUrl}>
View source
<Icon icon={IconExternalLink} slot="end" size="s" />
</Button>
{/if}
</svelte:fragment>
</Aside>
</svelte:fragment>
@@ -30,18 +30,20 @@
let showCancel = $state(false);
onMount(() => {
return sdk.forConsole.client.subscribe(
'console',
async (response: RealtimeResponseEvent<Models.Deployment>) => {
if (
response.events.includes(
`sites.${page.params.site}.deployments.${page.params.deployment}.update`
)
) {
await invalidate(Dependencies.DEPLOYMENT);
return sdk
.forConsoleIn(page.params.region)
.client.subscribe(
'console',
async (response: RealtimeResponseEvent<Models.Deployment>) => {
if (
response.events.includes(
`sites.${page.params.site}.deployments.${page.params.deployment}.update`
)
) {
await invalidate(Dependencies.DEPLOYMENT);
}
}
}
);
);
});
</script>
@@ -155,7 +155,7 @@
<FloatingActionBar>
<svelte:fragment slot="start">
<Badge content={selectedRows.length.toString()} />
<span>
<span style="white-space: nowrap">
{selectedRows.length > 1 ? 'deployments' : 'deployment'}
selected
</span>
@@ -3,6 +3,13 @@ import { writable } from 'svelte/store';
export const columns = writable<Column[]>([
{ id: '$id', title: 'Log ID', type: 'string', width: { min: 200, max: 250 } },
{
id: 'deploymentId',
title: 'Deployment ID',
type: 'string',
width: { min: 210 },
hide: true
},
{
id: 'requestPath',
title: 'Path',
@@ -10,7 +17,6 @@ export const columns = writable<Column[]>([
width: { min: 480 },
format: 'string'
},
{
id: 'requestMethod',
title: 'Method',
@@ -74,6 +74,8 @@
{#key column.id}
<Id value={log.$id}>{log.$id}</Id>
{/key}
{:else if column.id === 'deploymentId'}
<Id value={log.deploymentId}>{log.deploymentId}</Id>
{:else if column.id === 'requestMethod'}
<Typography.Code size="m">
{log.requestMethod}
+1 -1
View File
@@ -25,7 +25,7 @@
await sdk.forConsole.teams.updateMembershipStatus(teamId, membershipId, userId, secret);
addNotification({
type: 'success',
message: 'Successfully logged in.'
message: 'Successfully joined the organization.'
});
await goto(`${base}/organization-${teamId}`);
trackEvent(Submit.MembershipUpdateStatus);
+9 -9
View File
@@ -1,16 +1,16 @@
<script>
import { base } from '$app/paths';
import { page } from '$app/state';
import { Button } from '$lib/elements/forms';
import { Unauthenticated } from '$lib/layout';
import { Typography } from '@appwrite.io/pink-svelte';
import { Button } from '$lib/elements/forms';
import { Badge, Typography, Layout } from '@appwrite.io/pink-svelte';
</script>
<Unauthenticated>
<Typography.Title size="xl"
>{'status' in page.error
? page.error.status || 'Invalid Argument'
: 'Invalid Argument'}</Typography.Title>
<Typography.Title>{page.error.message}</Typography.Title>
<Button href={base}>Back to the console</Button>
<Layout.Stack gap="l" alignItems="center">
<Badge variant="secondary" content="404 Page not found" />
<Typography.Title size="l" align="center">
The page you're looking for doesn't exist.
</Typography.Title>
<Button href={base}>Back to console</Button>
</Layout.Stack>
</Unauthenticated>
+1 -1
View File
@@ -38,7 +38,7 @@ body {
visibility: hidden;
position: absolute;
max-width: 100%;
bottom: 60px;
bottom: 20px;
left: 50%;
transform: translate(-50%, -50%);
}