mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'feat-project-changes-2' into feat-SER-205-new-pricing-component
This commit is contained in:
@@ -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
@@ -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",
|
||||
|
||||
Generated
+16
-15
@@ -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[];
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) => ({
|
||||
|
||||
@@ -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 |
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 '';
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+17
-3
@@ -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 />
|
||||
|
||||
+74
-78
@@ -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">
|
||||
|
||||
+9
-10
@@ -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}
|
||||
|
||||
+1
-5
@@ -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}
|
||||
|
||||
+6
-12
@@ -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}
|
||||
|
||||
+7
-1
@@ -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',
|
||||
|
||||
+2
@@ -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'}
|
||||
|
||||
+2
-1
@@ -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({
|
||||
|
||||
+1
-1
@@ -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>
|
||||
|
||||
+4
@@ -49,6 +49,10 @@
|
||||
height: var(--icon-size);
|
||||
color: var(--icon-color);
|
||||
}
|
||||
|
||||
& :global(img) {
|
||||
inline-size: var(--icon-size) !important;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.theme-dark) {
|
||||
|
||||
+15
-13
@@ -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>
|
||||
|
||||
+13
-9
@@ -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>
|
||||
|
||||
+19
-12
@@ -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>
|
||||
|
||||
+16
-3
@@ -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>
|
||||
|
||||
+21
-5
@@ -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}
|
||||
|
||||
+17
-15
@@ -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>
|
||||
|
||||
|
||||
+11
@@ -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>
|
||||
|
||||
|
||||
+13
-11
@@ -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>
|
||||
|
||||
|
||||
+1
-1
@@ -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}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -38,7 +38,7 @@ body {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
max-width: 100%;
|
||||
bottom: 60px;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user