- {#each [...$importItems.entries()] as [key, value] (key)}
+ {#each [...importItems.entries()] as [key, value] (key)}
@@ -222,6 +247,25 @@
class:is-danger={value.status === 'failed'}
style="--graph-size:{graphSize(value.status)}%">
+ {#if value.status === 'failed'}
+
+
+
+ There was an import issue.
+ openDetails(value.errors)}
+ >View details
+
+
+ {/if}
@@ -232,6 +276,18 @@
{/if}
+
+
+
+
+
+
+
+
diff --git a/src/lib/components/sidebar.svelte b/src/lib/components/sidebar.svelte
index 6a694d6e9..4ed4b7920 100644
--- a/src/lib/components/sidebar.svelte
+++ b/src/lib/components/sidebar.svelte
@@ -12,8 +12,7 @@
Button,
Layout,
Avatar,
- Typography,
- Badge
+ Typography
} from '@appwrite.io/pink-svelte';
import {
@@ -80,13 +79,7 @@
{ name: 'Functions', icon: IconLightningBolt, slug: 'functions', category: 'build' },
{ name: 'Messaging', icon: IconChatBubble, slug: 'messaging', category: 'build' },
{ name: 'Storage', icon: IconFolder, slug: 'storage', category: 'build' },
- {
- name: 'Sites',
- icon: IconGlobeAlt,
- slug: 'sites',
- category: 'deploy',
- badge: 'Early access'
- }
+ { name: 'Sites', icon: IconGlobeAlt, slug: 'sites', category: 'deploy' }
];
const isSelected = (service: string): boolean => {
@@ -239,12 +232,6 @@
class:has-text={state === 'open'}
class="link-text">
{projectOption.name}
- {#if projectOption?.badge}
-
- {/if}
{projectOption.name}
diff --git a/src/lib/elements/link.svelte b/src/lib/elements/link.svelte
index 6e75e98a5..444a94d18 100644
--- a/src/lib/elements/link.svelte
+++ b/src/lib/elements/link.svelte
@@ -29,6 +29,7 @@
{#if href}
{:else}
-
+
{/if}
diff --git a/src/lib/stores/billing.ts b/src/lib/stores/billing.ts
index d23fa3346..46134dde3 100644
--- a/src/lib/stores/billing.ts
+++ b/src/lib/stores/billing.ts
@@ -488,6 +488,9 @@ export async function paymentExpired(org: Organization) {
const nots = get(notifications);
const expiredNotification = nots.some((n) => n.message === expiredMessage);
const expiringNotification = nots.some((n) => n.message === expiringMessage);
+ const cardExpiry = new Date(payment.expiryYear, payment.expiryMonth, 1);
+ const nextMonth = new Date(year, month + 1, 1);
+ const isExpiringNextMonth = cardExpiry.getTime() === nextMonth.getTime();
if (payment.expired && !expiredNotification) {
addNotification({
type: 'error',
@@ -503,7 +506,7 @@ export async function paymentExpired(org: Organization) {
}
]
});
- } else if (!expiringNotification && payment.expiryYear <= year && payment.expiryMonth < month) {
+ } else if (!expiringNotification && !payment.expired && isExpiringNextMonth) {
addNotification({
type: 'warning',
isHtml: true,
diff --git a/src/lib/stores/sdk.ts b/src/lib/stores/sdk.ts
index 8fd1b477b..92c5a53cf 100644
--- a/src/lib/stores/sdk.ts
+++ b/src/lib/stores/sdk.ts
@@ -42,7 +42,6 @@ import {
SUBDOMAIN_TOR
} from '$lib/constants';
import { building } from '$app/environment';
-import { getProjectId } from '$lib/helpers/project';
export function getApiEndpoint(region?: string): string {
if (building) return '';
@@ -141,12 +140,32 @@ const sdkForProject = {
};
export const realtime = {
- forProject(region: string, _projectId: string) {
+ forProject(
+ region: string,
+ channels: string | string[],
+ callback: AppwriteRealtimeResponseEvent
+ ) {
const endpoint = getApiEndpoint(region);
if (endpoint !== clientRealtime.config.endpoint) {
clientRealtime.setEndpoint(endpoint);
}
- return clientRealtime;
+
+ // because uses a different client!
+ const realtime = new Realtime(clientRealtime);
+
+ return createRealtimeSubscription(realtime, channels, callback);
+ },
+
+ forConsole(
+ region: string,
+ channels: string | string[],
+ callback: AppwriteRealtimeResponseEvent
+ ): () => void {
+ const realtimeInstance = region
+ ? sdk.forConsoleIn(region).realtime
+ : sdk.forConsole.realtime;
+
+ return createRealtimeSubscription(realtimeInstance, channels, callback);
}
};
@@ -176,8 +195,8 @@ export const sdk = {
};
export enum RuleType {
- DEPLOYMENT = 'deployment',
API = 'api',
+ DEPLOYMENT = 'deployment',
REDIRECT = 'redirect'
}
@@ -191,11 +210,24 @@ export enum RuleTrigger {
MANUAL = 'manual'
}
-/**
- * Some type imports are broken on the SDK, this works correctly for the time being!
- */
-export type AppwriteRealtimeSubscription = Awaited>;
-
-export const createAdminClient = () => {
- return new Client().setEndpoint(getApiEndpoint()).setMode('admin').setProject(getProjectId());
+export type RealtimeResponse = {
+ events: string[];
+ channels: string[];
+ timestamp: string;
+ payload: unknown;
};
+
+export type AppwriteRealtimeResponseEvent = (response: RealtimeResponse) => void;
+
+function createRealtimeSubscription(
+ realtimeInstance: Realtime,
+ channels: string | string[],
+ callback: AppwriteRealtimeResponseEvent
+): () => void {
+ const channelsArray = Array.isArray(channels) ? channels : [channels];
+ const subscriptionPromise = realtimeInstance.subscribe(channelsArray, callback);
+
+ return () => {
+ subscriptionPromise.then((sub) => sub.close());
+ };
+}
diff --git a/src/routes/(console)/project-[region]-[project]/+layout.svelte b/src/routes/(console)/project-[region]-[project]/+layout.svelte
index f233956c0..426821259 100644
--- a/src/routes/(console)/project-[region]-[project]/+layout.svelte
+++ b/src/routes/(console)/project-[region]-[project]/+layout.svelte
@@ -27,15 +27,13 @@
import CsvImportBox from '$lib/components/csvImportBox.svelte';
onMount(() => {
- return realtime
- .forProject(page.params.region, page.params.project)
- .subscribe(['project', 'console'], (response) => {
- if (response.events.includes('stats.connections')) {
- for (const [projectId, value] of Object.entries(response.payload)) {
- stats.add(projectId, [new Date(response.timestamp).toISOString(), value]);
- }
+ return realtime.forProject(page.params.region, ['project', 'console'], (response) => {
+ if (response.events.includes('stats.connections')) {
+ for (const [projectId, value] of Object.entries(response.payload)) {
+ stats.add(projectId, [new Date(response.timestamp).toISOString(), value]);
}
- });
+ }
+ });
});
$: $registerCommands([
diff --git a/src/routes/(console)/project-[region]-[project]/auth/+page.svelte b/src/routes/(console)/project-[region]-[project]/auth/+page.svelte
index 80f6ed5cc..391274650 100644
--- a/src/routes/(console)/project-[region]-[project]/auth/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/auth/+page.svelte
@@ -1,4 +1,4 @@
-
@@ -9,8 +9,10 @@
import {
AvatarInitials,
Copy,
+ type DeleteOperationState,
Empty,
EmptySearch,
+ MultiSelectionTable,
PaginationWithLimit,
SearchQuery
} from '$lib/components';
@@ -20,14 +22,7 @@
import type { Models } from '@appwrite.io/console';
import { writable } from 'svelte/store';
import Create from './createUser.svelte';
- import {
- Badge,
- Icon,
- Table,
- Layout,
- Typography,
- FloatingActionBar
- } from '@appwrite.io/pink-svelte';
+ import { Badge, Icon, Table, Layout, Typography } from '@appwrite.io/pink-svelte';
import { Tag } from '@appwrite.io/pink-svelte';
import { IconDuplicate, IconPlus } from '@appwrite.io/pink-icons-svelte';
import { canWriteUsers } from '$lib/stores/roles';
@@ -37,11 +32,11 @@
import { sdk } from '$lib/stores/sdk';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { Dependencies } from '$lib/constants';
- import { addNotification } from '$lib/stores/notifications';
import { invalidate } from '$app/navigation';
- import Confirm from '$lib/components/confirm.svelte';
- export let data;
+ import type { PageProps } from './$types';
+
+ let { data }: PageProps = $props();
const columns = writable([
{ id: '$id', title: 'User ID', type: 'string', width: 200 },
@@ -59,44 +54,25 @@
}
]);
- let selectedUsers: string[] = [];
- let showDelete = false;
- let deleting = false;
-
async function userCreated(event: CustomEvent>>) {
await goto(
`${base}/project-${page.params.region}-${page.params.project}/auth/user-${event.detail.$id}`
);
}
- async function handleDelete() {
- showDelete = false;
- deleting = true;
-
- const promises = selectedUsers.map((userId) =>
- sdk.forProject(page.params.region, page.params.project).users.delete(userId)
- );
+ async function handleDelete(selectedRows: string[]): Promise {
+ const promises = selectedRows.map((userId) => {
+ return sdk.forProject(page.params.region, page.params.project).users.delete({ userId });
+ });
try {
await Promise.all(promises);
- trackEvent(Submit.UserDelete, {
- total: selectedUsers.length
- });
- addNotification({
- type: 'success',
- message: `${selectedUsers.length} user${selectedUsers.length > 1 ? 's' : ''} deleted`
- });
- invalidate(Dependencies.USERS);
+ trackEvent(Submit.UserDelete, { total: selectedRows.length });
} catch (error) {
- addNotification({
- type: 'error',
- message: error.message
- });
trackError(error, Submit.UserDelete);
+ return error;
} finally {
- selectedUsers = [];
- showDelete = false;
- deleting = false;
+ await invalidate(Dependencies.USERS);
}
}
@@ -116,101 +92,111 @@
{#if data.users.total}
-
-
+ onDelete={handleDelete}
+ allowSelection={$canWriteUsers}>
+ {#snippet header(root)}
{#each $columns as { id, title } (id)}
{title}
{/each}
-
- {#each data.users.users as user}
-
- {#each $columns as { id } (id)}
-
- {#if id === '$id'}
-
-
-
- {user.$id}
-
-
- {:else if id === 'name'}
-
- {#if user.email || user.phone}
- {#if user.name}
-
+ {/snippet}
+
+ {#snippet children(root)}
+ {#each data.users.users as user}
+
+ {#each $columns as { id } (id)}
+
+ {#if id === '$id'}
+
+
+
+ {user.$id}
+
+
+ {:else if id === 'name'}
+
+ {#if user.email || user.phone}
+ {#if user.name}
+
+
+ {user.name}
+
+ {:else}
+
+
+
+ {/if}
+ {:else}
+
+
+
{user.name}
- {:else}
-
-
-
{/if}
+
+ {:else if id === 'identifiers'}
+
+ {user.email && user.phone
+ ? [user.email, user.phone].join(',')
+ : user.email || user.phone}
+
+ {:else if id === 'status'}
+ {#if user.status}
+ {@const success =
+ user.emailVerification || user.phoneVerification}
+
{:else}
-
-
-
-
- {user.name}
-
+
+ {/if}
+ {:else if id === 'labels'}
+
+ {user.labels.join(', ')}
+
+ {:else if id === 'joined'}
+
+ {:else if id === 'lastActivity'}
+ {#if user.accessedAt}
+
+ {:else}
+ never
{/if}
-
- {:else if id === 'identifiers'}
-
- {user.email && user.phone
- ? [user.email, user.phone].join(',')
- : user.email || user.phone}
-
- {:else if id === 'status'}
- {#if user.status}
- {@const success =
- user.emailVerification || user.phoneVerification}
-
{:else}
-
+ {user[id]}
{/if}
- {:else if id === 'labels'}
-
- {user.labels.join(', ')}
-
- {:else if id === 'joined'}
-
- {:else if id === 'lastActivity'}
- {#if user.accessedAt}
-
- {:else}
- never
- {/if}
- {:else}
- {user[id]}
- {/if}
-
- {/each}
-
- {/each}
-
+
+ {/each}
+
+ {/each}
+ {/snippet}
+
+ {#snippet deleteContentNotice()}
+ This action is irreversible and will permanently remove the selected users and all
+ their data.
+ {/snippet}
+
Clear Search
+ secondary
+ href={`${base}/project-${page.params.region}-${page.params.project}/auth`}
+ >Clear Search
{:else}
showCreateUser.set(true)} />
{/if}
-
- {#if selectedUsers.length > 0}
-
-
-
-
- {selectedUsers.length > 1 ? 'users' : 'user'}
- selected
-
-
-
- (selectedUsers = [])}>Cancel
- (showDelete = true)}>Delete
-
-
- {/if}
-
-
-
- Are you sure you want to delete {selectedUsers.length}
- {selectedUsers.length > 1 ? 'users' : 'user'}?
-
-
- This action is irreversible and will permanently remove the selected users and all their
- data.
-
-
diff --git a/src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte b/src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte
index 3a64c2500..18b2eb2d9 100644
--- a/src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte
@@ -1,4 +1,4 @@
-
@@ -10,7 +10,9 @@
EmptySearch,
AvatarInitials,
SearchQuery,
- PaginationWithLimit
+ PaginationWithLimit,
+ type DeleteOperationState,
+ MultiSelectionTable
} from '$lib/components';
import Create from '../createTeam.svelte';
import { goto } from '$app/navigation';
@@ -19,31 +21,16 @@
import type { Models } from '@appwrite.io/console';
import { writable } from 'svelte/store';
import { canWriteTeams } from '$lib/stores/roles';
- import {
- Icon,
- Layout,
- Table,
- FloatingActionBar,
- Badge,
- Typography
- } from '@appwrite.io/pink-svelte';
+ import { Icon, Layout, Table } from '@appwrite.io/pink-svelte';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import DualTimeView from '$lib/components/dualTimeView.svelte';
import { sdk } from '$lib/stores/sdk';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { Dependencies } from '$lib/constants';
- import { addNotification } from '$lib/stores/notifications';
import { invalidate } from '$app/navigation';
- import Confirm from '$lib/components/confirm.svelte';
+ import type { PageProps } from './$types';
- export let data;
-
- const region = page.params.region;
- const project = page.params.project;
-
- let selectedTeams: string[] = [];
- let showDelete = false;
- let deleting = false;
+ let { data }: PageProps = $props();
const columns = writable([
{ id: 'name', title: 'Name', type: 'string', width: { min: 200, max: 300 } },
@@ -52,37 +39,24 @@
]);
const teamCreated = async (event: CustomEvent>>) => {
- await goto(`${base}/project-${region}-${project}/auth/teams/team-${event.detail.$id}`);
+ await goto(
+ `${base}/project-${page.params.region}-${page.params.project}/auth/teams/team-${event.detail.$id}`
+ );
};
- async function handleDelete() {
- showDelete = false;
- deleting = true;
-
- const promises = selectedTeams.map((teamId) =>
- sdk.forProject(page.params.region, page.params.project).teams.delete(teamId)
- );
+ async function handleDelete(selectedRows: string[]): Promise {
+ const promises = selectedRows.map((teamId) => {
+ return sdk.forProject(page.params.region, page.params.project).teams.delete({ teamId });
+ });
try {
await Promise.all(promises);
- trackEvent(Submit.TeamDelete, {
- total: selectedTeams.length
- });
- addNotification({
- type: 'success',
- message: `${selectedTeams.length} team${selectedTeams.length > 1 ? 's' : ''} deleted`
- });
- invalidate(Dependencies.TEAMS);
+ trackEvent(Submit.TeamDelete, { total: selectedRows.length });
} catch (error) {
- addNotification({
- type: 'error',
- message: error.message
- });
trackError(error, Submit.TeamDelete);
+ return error;
} finally {
- selectedTeams = [];
- showDelete = false;
- deleting = false;
+ await invalidate(Dependencies.TEAMS);
}
}
@@ -101,54 +75,47 @@
{#if data.teams.total}
-
-
+ onDelete={handleDelete}
+ allowSelection={$canWriteTeams}>
+ {#snippet header(root)}
{#each $columns as { id, title }}
{title}
{/each}
-
- {#each data.teams.teams as team (team.$id)}
-
- {#each $columns as column}
-
- {#if column.id === 'name'}
-
-
- {team.name}
-
- {:else if column.id === 'members'}
- {team.total} members
- {:else if column.id === 'created'}
-
- {/if}
-
- {/each}
-
- {/each}
-
+ {/snippet}
- {#if selectedTeams.length > 0}
-
-
-
-
- {selectedTeams.length > 1 ? 'teams' : 'team'}
- selected
-
-
-
- (selectedTeams = [])}>Cancel
- (showDelete = true)}>Delete
-
-
- {/if}
+ {#snippet children(root)}
+ {@const TableRowComponent = $canWriteTeams ? Table.Row.Link : Table.Row.Base}
+ {#each data.teams.teams as team (team.$id)}
+ {@const href = $canWriteTeams
+ ? `${base}/project-${page.params.region}-${page.params.project}/auth/teams/team-${team.$id}`
+ : undefined}
+
+ {#each $columns as column}
+
+ {#if column.id === 'name'}
+
+
+ {team.name}
+
+ {:else if column.id === 'members'}
+ {team.total} members
+ {:else if column.id === 'created'}
+
+ {/if}
+
+ {/each}
+
+ {/each}
+ {/snippet}
+
+ {#snippet deleteContentNotice()}
+ This action is irreversible and will permanently remove the selected teams and all
+ their memberships.
+ {/snippet}
+
{:else if data.search}
-
+
Clear Search
@@ -172,14 +142,3 @@
-
-
-
- Are you sure you want to delete {selectedTeams.length}
- {selectedTeams.length > 1 ? 'teams' : 'team'}?
-
-
- This action is irreversible and will permanently remove the selected teams and all their
- memberships.
-
-
diff --git a/src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte b/src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte
index 629c5bdda..98999a305 100644
--- a/src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte
@@ -1,77 +1,50 @@
@@ -85,71 +58,62 @@
{#if data.memberships.total}
-
-
+ {#snippet header(root)}
Name
Roles
Joined
-
- {#each data.memberships.memberships as membership (membership.$id)}
- {@const username = membership.userName ? membership.userName : '-'}
-
-
-
-
- {username}
-
-
-
- {membership.roles}
-
-
-
-
-
- {
- selectedMembership = membership;
- showDelete = true;
- trackEvent(Click.MembershipDeleteClick);
- }}>
-
-
-
-
- {/each}
-
+ {/snippet}
- {#if selectedMemberships.length > 0}
-
-
-
-
- {selectedMemberships.length > 1 ? 'memberships' : 'membership'}
- selected
-
-
-
- (selectedMemberships = [])}>Cancel
- (showBulkDelete = true)}>Delete
-
-
- {/if}
+ {#snippet children(root)}
+ {#each data.memberships.memberships as membership (membership.$id)}
+ {@const username = membership.userName ? membership.userName : '-'}
+
+
+
+
+ {username}
+
+
+
+ {membership.roles}
+
+
+
+
+
+ {
+ event.preventDefault();
+ showDelete = true;
+ selectedMembership = membership;
+ trackEvent(Click.MembershipDeleteClick);
+ }}>
+
+
+
+
+ {/each}
+ {/snippet}
+
+ {#snippet deleteContentNotice()}
+ This action is irreversible and will remove the selected members from this team.
+ {/snippet}
+
-
- invalidate(Dependencies.MEMBERSHIPS)} />
+ invalidate(Dependencies.MEMBERSHIPS)} />
-
-
- Are you sure you want to delete {selectedMemberships.length}
- {selectedMemberships.length > 1 ? 'memberships' : 'membership'}?
-
-
- This action is irreversible and will remove the selected members from this team.
-
-
+ invalidate(Dependencies.MEMBERSHIPS)} />
diff --git a/src/routes/(console)/project-[region]-[project]/auth/user-[user]/identities/table.svelte b/src/routes/(console)/project-[region]-[project]/auth/user-[user]/identities/table.svelte
index 13e55efa8..5d9e956af 100644
--- a/src/routes/(console)/project-[region]-[project]/auth/user-[user]/identities/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/auth/user-[user]/identities/table.svelte
@@ -1,122 +1,88 @@
-
-
+
+ {#snippet header(root)}
{#each columns as { id, title }}
{title}
{/each}
-
- {#each data.identities.identities as identity (identity.$id)}
-
- {#each columns as column}
-
- {#if column.id === '$id'}
- {#key columns}
-
- {identity[column.id]}
-
- {/key}
- {:else if column.id === 'provider'}
- {@const provider = oAuthProviders[identity[column.id]]}
-
-
-
+ {/snippet}
+
+ {#snippet children(root)}
+ {#each data.identities.identities as identity (identity.$id)}
+
+ {#each columns as column}
+
+ {#if column.id === '$id'}
+ {#key columns}
+
+ {identity[column.id]}
+
+ {/key}
+ {:else if column.id === 'provider'}
+ {@const provider = oAuthProviders[identity[column.id]]}
+
+
+
+
+ {provider.name}
- {provider.name}
-
- {:else if column.type === 'datetime'}
- {#if !identity[column.id]}
- -
+ {:else if column.type === 'datetime'}
+ {#if !identity[column.id]}
+ -
+ {:else}
+
+ {/if}
{:else}
-
+ {identity[column.id]}
{/if}
- {:else}
- {identity[column.id]}
- {/if}
-
- {/each}
-
- {/each}
-
-
-{#if selectedIds.length > 0}
-
-
-
-
- {selectedIds.length > 1 ? 'identities' : 'identity'}
- selected
-
-
-
- (selectedIds = [])}>Cancel
- (showDelete = true)}>Delete
-
-
-{/if}
-
-
-
- Are you sure you want to delete {selectedIds.length}
- {selectedIds.length > 1 ? 'identities' : 'identity'}?
-
-
+
+ {/each}
+
+ {/each}
+ {/snippet}
+
diff --git a/src/routes/(console)/project-[region]-[project]/auth/user-[user]/memberships/+page.svelte b/src/routes/(console)/project-[region]-[project]/auth/user-[user]/memberships/+page.svelte
index 0deaed8da..0c0b5525b 100644
--- a/src/routes/(console)/project-[region]-[project]/auth/user-[user]/memberships/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/auth/user-[user]/memberships/+page.svelte
@@ -1,50 +1,36 @@
{#if data.memberships.total}
-
-
+ {#snippet header(root)}
Name
Roles
Joined
-
- {#each data.memberships.memberships as membership}
-
-
-
-
- {membership.teamName ? membership.teamName : 'n/a'}
-
-
-
- {membership.roles}
-
-
-
-
-
- {
- selectedMembership = membership;
- showDelete = true;
- trackEvent('click_delete_membership');
- }}>
-
-
-
-
- {/each}
-
+ {/snippet}
+
+ {#snippet children(root)}
+ {#each data.memberships.memberships as membership}
+
+
+
+
+ {membership.teamName ? membership.teamName : 'n/a'}
+
+
+
+ {membership.roles}
+
+
+
+
+
+ {
+ event.preventDefault();
+ selectedMembership = membership;
+ showDelete = true;
+ trackEvent('click_delete_membership');
+ }}>
+
+
+
+
+ {/each}
+ {/snippet}
+
+ {#snippet deleteContentNotice()}
+ This action is irreversible and will remove the user from the selected teams.
+ {/snippet}
+
{:else}
{/if}
-
- {#if selectedMemberships.length > 0}
-
-
-
-
- {selectedMemberships.length > 1 ? 'memberships' : 'membership'}
- selected
-
-
-
- (selectedMemberships = [])}>Cancel
- (showBulkDelete = true)}>Delete
-
-
- {/if}
-
-
-
- Are you sure you want to delete {selectedMemberships.length}
- {selectedMemberships.length > 1 ? 'memberships' : 'membership'}?
-
-
- This action is irreversible and will remove the user from the selected teams.
-
-
diff --git a/src/routes/(console)/project-[region]-[project]/auth/user-[user]/targets/table.svelte b/src/routes/(console)/project-[region]-[project]/auth/user-[user]/targets/table.svelte
index 09aa63619..e71859a43 100644
--- a/src/routes/(console)/project-[region]-[project]/auth/user-[user]/targets/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/auth/user-[user]/targets/table.svelte
@@ -1,6 +1,5 @@
-
-
+
+ {#snippet header(root)}
{#each $columns as { id, title }}
{title}
{/each}
-
- {#each data.targets.targets as target (target.$id)}
- {@const provider = data.providersById[target.providerId]}
-
- {#each $columns as column}
-
- {#if column.id === '$id'}
- {#key $columns}
-
- {target[column.id]}
-
- {/key}
- {:else if column.id === 'target'}
- {#if target.providerType === MessagingProviderType.Push}
- {target.name}
+ {/snippet}
+
+ {#snippet children(root)}
+ {#each data.targets.targets as target (target.$id)}
+ {@const provider = data.providersById[target.providerId]}
+
+ {#each $columns as column}
+
+ {#if column.id === '$id'}
+ {#key $columns}
+
+ {target[column.id]}
+
+ {/key}
+ {:else if column.id === 'target'}
+ {#if target.providerType === MessagingProviderType.Push}
+ {target.name}
+ {:else}
+ {target.identifier}
+ {/if}
+ {:else if column.id === 'providerType'}
+
+ {:else if column.id === 'provider'}
+ {#if provider}
+
+ {/if}
+ {:else if column.id === '$createdAt'}
+
{:else}
- {target.identifier}
+ {target[column.id]}
{/if}
- {:else if column.id === 'providerType'}
-
- {:else if column.id === 'provider'}
- {#if provider}
-
- {/if}
- {:else if column.id === '$createdAt'}
-
- {:else}
- {target[column.id]}
- {/if}
-
- {/each}
-
- {/each}
-
-
-{#if selectedIds.length > 0}
-
-
-
-
- {selectedIds.length > 1 ? 'targets' : 'target'}
- selected
-
-
-
- (selectedIds = [])}>Cancel
- (showDelete = true)}>Delete
-
-
-{/if}
-
-
-
- Are you sure you want to delete {selectedIds.length}
- {selectedIds.length > 1 ? 'targets' : 'target'}?
-
-
+
+ {/each}
+
+ {/each}
+ {/snippet}
+
diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/+page.svelte
index 6c6a8de18..99521078d 100644
--- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/+page.svelte
@@ -69,11 +69,11 @@
await sdk
.forProject(page.params.region, page.params.project)
.backups.createArchive(['databases'], data.database.$id);
+ await invalidate(Dependencies.BACKUPS);
addNotification({
type: 'success',
message: 'Database backup has started'
});
- invalidate(Dependencies.BACKUPS);
trackEvent('click_manual_submit');
showFeedbackNotification();
} catch (error) {
@@ -86,7 +86,7 @@
}
};
- const trackEvents = (policies) => {
+ const trackEvents = (policies: UserBackupPolicy[]) => {
policies.forEach((policy) => {
let actualDay = null;
const monthlyBackupFrequency = policy.monthlyBackupFrequency;
@@ -139,7 +139,6 @@
? `Backup policies have been created`
: `
${totalPolicies[0].label} policy has been created`;
- // TODO: html isn't yet supported on Toast.
addNotification({
isHtml: true,
type: 'success',
@@ -148,7 +147,7 @@
trackEvents(totalPolicies);
- invalidate(Dependencies.BACKUPS);
+ await invalidate(Dependencies.BACKUPS);
showFeedbackNotification();
} catch (err) {
addNotification({
@@ -162,19 +161,14 @@
};
onMount(() => {
- return realtime
- .forProject(page.params.region, page.params.project)
- .subscribe(['project', 'console'], (response) => {
- // fast path return.
- if (!response.channels.includes(`projects.${getProjectId()}`)) return;
+ return realtime.forProject(page.params.region, ['project', 'console'], (response) => {
+ // fast path return.
+ if (!response.channels.includes(`projects.${getProjectId()}`)) return;
- if (
- response.events.includes('archives.*') ||
- response.events.includes('policies.*')
- ) {
- invalidate(Dependencies.BACKUPS);
- }
- });
+ if (response.events.includes('archives.*') || response.events.includes('policies.*')) {
+ invalidate(Dependencies.BACKUPS);
+ }
+ });
});
diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/table.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/table.svelte
index 689afe74f..d33593160 100644
--- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/table.svelte
@@ -1,5 +1,11 @@
-
-
+
+ {#snippet header(root)}
{#each $columns as column}
{column.title}
{/each}
-
+ {/snippet}
- {#each data.backups.archives as backup, index}
- {@const policy = policyDetails(backup.policyId)}
- {@const retainedUntil = new Date(
- new Date(policy?.$createdAt).getTime() + policy?.retention * 24 * 60 * 60 * 1000
- )}
- {@const formattedRetainedUntil = `${retainedUntil.getDate()} ${retainedUntil.toLocaleString('en-US', { month: 'short' })}, ${retainedUntil.getFullYear()} ${retainedUntil.toLocaleTimeString('en-US', { hour12: false })}`}
-
-
-
- {cleanBackupName(backup)}
-
-
-
- {#if backup.status === 'completed'}
- {calculateSize(backup.size)}
- {:else}
- -
- {/if}
-
-
- {@const backupStatus = getBackupStatus(backup)}
-
-
-
-
-
-
-
-
-
- {policy?.name || 'Manual'}
-
- {policy
- ? `Retained until: ${formattedRetainedUntil}`
- : `Retained forever`}
-
-
-
-
-
-
-
-
-
-
-
- {#if backup.status === 'completed'}
+ {#snippet children(root)}
+ {#each data.backups.archives as backup, index}
+ {@const policy = getPolicyDetails(backup.policyId)}
+ {@const retainedUntil = new Date(
+ new Date(policy?.$createdAt).getTime() + policy?.retention * 24 * 60 * 60 * 1000
+ )}
+ {@const formattedRetainedUntil = `${retainedUntil.getDate()} ${retainedUntil.toLocaleString('en-US', { month: 'short' })}, ${retainedUntil.getFullYear()} ${retainedUntil.toLocaleTimeString('en-US', { hour12: false })}`}
+
+
+
+ {getCleanBackupName(backup)}
+
+
+
+ {#if backup.status === 'completed'}
+ {calculateSize(backup.size)}
+ {:else}
+ -
+ {/if}
+
+
+ {@const backupStatus = getBackupStatus(backup)}
+
+
+
+
+
+
+
+
+
+ {policy?.name || 'Manual'}
+
+ {policy
+ ? `Retained until: ${formattedRetainedUntil}`
+ : `Retained forever`}
+
+
+
+
+
+
+
+
+
+
+
+ {#if backup.status === 'completed'}
+ {
+ toggle(e);
+ showRestore = true;
+ selectedBackup = backup;
+ showDropdown[index] = false;
+ trackEvent(Click.BackupRestoreClick);
+ }}>
+ Restore
+
+ {/if}
{
toggle(e);
- showRestore = true;
+ copy(backup.$id);
+ showDropdown[index] = false;
+ trackEvent(Click.BackupCopyIdClick);
+ }}>
+ Copy ID
+
+ {
+ toggle(e);
+ showDelete = true;
selectedBackup = backup;
showDropdown[index] = false;
- trackEvent(Click.BackupRestoreClick);
+ trackEvent(Click.BackupDeleteClick);
}}>
- Restore
+ Delete
- {/if}
- {
- toggle(e);
- copy(backup.$id);
- showDropdown[index] = false;
- trackEvent(Click.BackupCopyIdClick);
- }}>
- Copy ID
-
- {
- toggle(e);
- showDelete = true;
- selectedBackup = backup;
- showDropdown[index] = false;
- trackEvent(Click.BackupDeleteClick);
- }}>
- Delete
-
-
-
-
-
-
-
- {/each}
-
-
-{#if selectedBackups.length > 0}
-
-
-
-
- {selectedBackups.length > 1 ? 'backups' : 'backup'}
- selected
-
-
-
- (selectedBackups = [])}>Cancel
- (showDelete = true)}>Delete
-
-
-{/if}
+
+
+
+
+
+
+ {/each}
+ {/snippet}
+
+
+ onSubmit={async () => {
+ if (!selectedBackup) return;
+ await deleteBackups([selectedBackup.$id]);
+ }}>
- Are you sure you want to delete
- {#if selectedBackups.length}
- {selectedBackups.length} {selectedBackups.length > 1 ? 'backups' : 'backup'}?
- {:else}
- the {cleanBackupName(selectedBackup)} backup?
- {/if}
- This action is irreversible.
+ Are you sure you want to delete the {getCleanBackupName(selectedBackup)} backup?
+
+ This action is irreversible.
- {cleanBackupName(selectedBackup)}
+ {getCleanBackupName(selectedBackup)}
@@ -374,6 +382,6 @@
(showRestore = false)}>Cancel
- Restore
+ Restore
diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte
index 93034a2d5..f5908ae50 100644
--- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte
+++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte
@@ -20,7 +20,7 @@
@@ -456,7 +455,6 @@
-
+ {#key currentRowId}
+
+ {/key}
{
if (row) {
work = initWork();
- focusFirstInput();
+ requestAnimationFrame(() => focusFirstInput());
} else {
work = null;
}
diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/sheetOptions.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/sheetOptions.svelte
index 761178f79..3fdf50d14 100644
--- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/sheetOptions.svelte
+++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/sheetOptions.svelte
@@ -117,15 +117,20 @@
if (type === 'header') {
if (isSequence) {
- return ['sort-asc', 'sort-desc'].includes(item.action ?? '');
+ return ['sort-asc', 'sort-desc'].includes(item?.action);
}
- if (['delete', 'update', 'duplicate-header'].includes(item.action) && isSystemColumn) {
+ if (['delete', 'update', 'duplicate-header'].includes(item?.action) && isSystemColumn) {
+ return false;
+ }
+
+ // hide column-left and create-index for $id (first column, already indexed)
+ if (columnId === '$id' && ['column-left', 'create-index'].includes(item?.action)) {
return false;
}
// hide sort options for relationship columns
- if (isRelationship(column) && ['sort-asc', 'sort-desc'].includes(item.action ?? '')) {
+ if (isRelationship(column) && ['sort-asc', 'sort-desc'].includes(item?.action)) {
return false;
}
}
diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/spreadsheet.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/spreadsheet.svelte
index 324a1e087..ecf308d49 100644
--- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/spreadsheet.svelte
+++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/spreadsheet.svelte
@@ -53,6 +53,8 @@
Button,
Layout,
Link,
+ Popover,
+ Keyboard,
Badge,
FloatingActionBar,
Icon,
@@ -78,7 +80,8 @@
IconSwitchHorizontal,
IconText,
IconToggle,
- IconViewList
+ IconViewList,
+ IconArrowExpand
} from '@appwrite.io/pink-icons-svelte';
import type { HeaderCellAction, RowCellAction } from './sheetOptions.svelte';
import SheetOptions from './sheetOptions.svelte';
@@ -120,7 +123,9 @@
: 24;
let selectedRows = [];
+ let showExpandIconForId: number | null = null;
let spreadsheetContainer: SpreadsheetContainer;
+ let previouslyFocusedElement: Element | null = null;
let currentPage = 1;
let jumpToPageReactive = 0;
@@ -170,8 +175,8 @@
{
id: '$id',
title: '$id',
- width: getColumnWidth('$id', 225),
- minimumWidth: 225,
+ width: getColumnWidth('$id', 250),
+ minimumWidth: 250,
draggable: false,
type: 'string',
icon: IconFingerPrint,
@@ -537,9 +542,14 @@
}
} else if (type === 'row') {
if (action === 'update') {
- $databaseRowSheetOptions.show = true;
- $databaseRowSheetOptions.row = row;
- $databaseRowSheetOptions.title = 'Update row';
+ databaseRowSheetOptions.update((opts) => {
+ return {
+ ...opts,
+ row,
+ show: true,
+ title: 'Update row'
+ };
+ });
}
if (action === 'duplicate-row') {
@@ -747,6 +757,13 @@
$: totalPages = Math.ceil($rows.total / SPREADSHEET_PAGE_LIMIT) || 1;
$: rowSelection = !$spreadsheetLoading && !$paginatedRowsLoading ? true : ('disabled' as const);
+
+ $: if (!$databaseRowSheetOptions.show && previouslyFocusedElement) {
+ requestAnimationFrame(() => {
+ (previouslyFocusedElement as HTMLElement)?.focus();
+ previouslyFocusedElement = null;
+ });
+ }
@@ -776,6 +793,14 @@
bottomActionTooltip={{
text: 'Create row',
placement: 'top-end'
+ }}
+ expandKbdShortcut="Cmd+Enter"
+ on:expandKbdShortcut={({ detail }) => {
+ const focusedRowId = detail.rowId;
+ const focusedRow = $rows.rows.find((row) => row.$id === focusedRowId);
+
+ previouslyFocusedElement = document.activeElement;
+ onSelectSheetOption('update', null, 'row', focusedRow);
}}>
{#each $tableColumns as column (column.id)}
@@ -867,56 +892,133 @@
valueWithoutHover={row.$sequence}>
{#each $tableColumns as { id: columnId, isEditable } (columnId)}
{@const rowColumn = $columns.find((col) => col.key === columnId)}
-
- {#if columnId === '$id'}
- {row.$id}
- {:else if columnId === '$createdAt' || columnId === '$updatedAt'}
-
- {:else if columnId === 'actions'}
-
- onSelectSheetOption(option, null, 'row', row)}
- onVisibilityChanged={(visible) => {
- canShowDatetimePopover = !visible;
- }}>
- {#snippet children(toggle)}
-
-
-
- {/snippet}
-
- {:else if isRelationship(rowColumn)}
- {@const args = getDisplayNamesForTable(row[columnId])}
- {#if !isRelationshipToMany(rowColumn)}
- {#if row[columnId]}
- {@const displayValue = args
- .map((arg) => row[columnId]?.[arg])
- .filter(Boolean)
- .join(' | ')}
+ {#if columnId === '$id'}
+ {
+ showExpandIconForId = index;
+ }}
+ on:mouseleave={() => {
+ showExpandIconForId = null;
+ }}>
+
+
+ {row.$id}
- {#if displayValue}
- {
- $databaseRelatedRowSheetOptions.tableId =
- row[columnId]?.['$tableId'];
- $databaseRelatedRowSheetOptions.rows =
- row[columnId]?.['$id'];
- $databaseRelatedRowSheetOptions.show = true;
- }}>
- {displayValue}
-
+
+ {@const opacityValue =
+ showExpandIconForId === index ? '1' : '0'}
+
+ {
+ hide();
+ previouslyFocusedElement =
+ document.activeElement;
+ onSelectSheetOption(
+ 'update',
+ null,
+ 'row',
+ row
+ );
+ }}>
+
+
+
+
+
+
+ Expand row
+
+
+
+
+
+
+
+
+
+
+
+ {:else}
+
+ {#if columnId === '$createdAt' || columnId === '$updatedAt'}
+
+ {:else if columnId === 'actions'}
+
+ onSelectSheetOption(option, null, 'row', row)}
+ onVisibilityChanged={(visible) => {
+ canShowDatetimePopover = !visible;
+ }}>
+ {#snippet children(toggle)}
+
+
+
+ {/snippet}
+
+ {:else if isRelationship(rowColumn)}
+ {@const args = getDisplayNamesForTable(row[columnId])}
+ {#if !isRelationshipToMany(rowColumn)}
+ {#if row[columnId]}
+ {@const displayValue = args
+ .map((arg) => row[columnId]?.[arg])
+ .filter(Boolean)
+ .join(' | ')}
+
+ {#if displayValue}
+ {
+ $databaseRelatedRowSheetOptions.tableId =
+ row[columnId]?.['$tableId'];
+ $databaseRelatedRowSheetOptions.rows =
+ row[columnId]?.['$id'];
+ $databaseRelatedRowSheetOptions.show = true;
+ }}>
+ {displayValue}
+
+ {:else}
+
+ {/if}
{:else}
{/if}
{:else}
-
+ {@const itemsNum = row[columnId]?.length}
+ Items
{/if}
+ {:else if isSpatialType(rowColumn) && row[columnId] !== null}
+
+ {JSON.stringify(row[columnId])}
+
{:else}
- {@const itemsNum = row[columnId]?.length}
- Items
- {/if}
- {:else if isSpatialType(rowColumn) && row[columnId] !== null}
-
- {JSON.stringify(row[columnId])}
-
- {:else}
- {@const value = row[columnId]}
- {@const formatted = formatColumn(row[columnId])}
- {@const isEmptyArray = formatted === 'Empty'}
- {@const isDatetimeAttribute = rowColumn.type === 'datetime'}
- {@const isEncryptedAttribute =
- isString(rowColumn) && rowColumn.encrypt}
- {#if isDatetimeAttribute}
-
- Timestamp
- {toLocaleDateTime(value, true)}
-
- {:else if isEncryptedAttribute}
- e.preventDefault()}>
-
-
- {:else if formatted.length > 20}
-
+ {@const value = row[columnId]}
+ {@const formatted = formatColumn(row[columnId])}
+ {@const isEmptyArray = formatted === 'Empty'}
+ {@const isDatetimeAttribute = rowColumn.type === 'datetime'}
+ {@const isEncryptedAttribute =
+ isString(rowColumn) && rowColumn.encrypt}
+ {#if isDatetimeAttribute}
+
+ Timestamp
+ {toLocaleDateTime(value, true)}
+
+ {:else if isEncryptedAttribute}
+ e.preventDefault()}>
+
+
+ {:else if formatted.length > 20}
+
+
+ {formatted}
+
+
+ {formatted}
+
+
+ {:else if formatted === 'null'}
+
+ {:else if isEmptyArray}
+
+ {:else}
{formatted}
-
- {formatted}
-
-
- {:else if formatted === 'null'}
-
- {:else if isEmptyArray}
-
- {:else}
-
- {formatted}
-
+ {/if}
{/if}
- {/if}
-
- {@const isRelatedToMany = isRelationshipToMany(rowColumn)}
- {@const hasItems = isRelatedToMany
- ? row[columnId]?.length
- : false}
+
+ {@const isRelatedToMany = isRelationshipToMany(rowColumn)}
+ {@const hasItems = isRelatedToMany
+ ? row[columnId]?.length
+ : false}
- paginatedRows.update(index, row)}
- onRevert={(row) => paginatedRows.update(index, row)}
- openSideSheet={() => {
- close(); /* closes the editor */
+ paginatedRows.update(index, row)}
+ onRevert={(row) => paginatedRows.update(index, row)}
+ openSideSheet={() => {
+ close(); /* closes the editor */
- if (isRelationshipToMany(rowColumn)) {
- openSideSheetForRelationsToMany(
- row[columnId],
- rowColumn
- );
- } else {
- onSelectSheetOption('update', null, 'row', row);
- }
- }} />
-
-
+ if (isRelationshipToMany(rowColumn)) {
+ openSideSheetForRelationsToMany(
+ row[columnId],
+ rowColumn
+ );
+ } else {
+ onSelectSheetOption('update', null, 'row', row);
+ }
+ }} />
+
+
+ {/if}
{/each}
{/if}
diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table.svelte
index 28e5c2362..fa19cde37 100644
--- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table.svelte
@@ -1,110 +1,85 @@
-
-
+ {onDelete}>
+ {#snippet header(root)}
{#each $tableViewColumns as { id, title }}
{title}
{/each}
-
- {#each data.tables.tables as table (table.$id)}
-
- {#each $tableViewColumns as column}
-
- {#if column.id === '$id'}
- {#key $tableViewColumns}
- {table.$id}
- {/key}
- {:else if column.id === 'name'}
- {table.name}
- {:else}
-
- {/if}
-
- {/each}
-
- {/each}
-
+ {/snippet}
-{#if selectedTables.length > 0}
-
-
-
-
- {selectedTables.length > 1 ? 'tables' : 'table'}
- selected
-
-
-
- (selectedTables = [])}>Cancel
- (showDelete = true)}>Delete
-
-
-{/if}
-
-
-
- Are you sure you want to delete {selectedTables.length}
- {selectedTables.length > 1 ? 'tables' : 'table'}?
-
-
+ {#snippet children(root)}
+ {#each data.tables.tables as table (table.$id)}
+
+ {#each $tableViewColumns as column}
+
+ {#if column.id === '$id'}
+ {#key $tableViewColumns}
+ {table.$id}
+ {/key}
+ {:else if column.id === 'name'}
+ {table.name}
+ {:else}
+
+ {/if}
+
+ {/each}
+
+ {/each}
+ {/snippet}
+
diff --git a/src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte b/src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte
index 40c38c541..f84db9f7e 100644
--- a/src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte
@@ -185,27 +185,24 @@
on:invalid={handleInvalid}>
-
-
+
+
Drag and drop file here or click to upload
-
-
-
+ justifyContent="center">
+
+
+ Only .tar.gz files allowed
+
- Only .tar.gz files allowed
-
+
{#if maxSize > 0}
- Max file size: {readableMaxSize.value +
readableMaxSize.unit}
{/if}
diff --git a/src/routes/(console)/project-[region]-[project]/functions/create-function/template-[template]/configuration.svelte b/src/routes/(console)/project-[region]-[project]/functions/create-function/template-[template]/configuration.svelte
index 34f815e6e..cd21e5838 100644
--- a/src/routes/(console)/project-[region]-[project]/functions/create-function/template-[template]/configuration.svelte
+++ b/src/routes/(console)/project-[region]-[project]/functions/create-function/template-[template]/configuration.svelte
@@ -41,8 +41,8 @@
variables.map((variable) => {
if (variable.value === '{apiEndpoint}') {
- variable.value = getApiEndpoint();
- variable.placeholder = getApiEndpoint();
+ variable.value = getApiEndpoint(page.params.region);
+ variable.placeholder = getApiEndpoint(page.params.region);
} else if (variable.value === '{projectId}') {
variable.value = page.params.project;
variable.placeholder = page.params.project;
diff --git a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/+layout.svelte b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/+layout.svelte
index 4f8ad5851..aca7e790e 100644
--- a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/+layout.svelte
+++ b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/+layout.svelte
@@ -21,30 +21,29 @@
onMount(() => {
let previousStatus = null;
- return realtime
- .forProject(page.params.region, page.params.project)
- .subscribe('console', (message) => {
- if (
- message.payload.status !== 'ready' &&
- previousStatus === message.payload.status
- ) {
- return;
- }
- previousStatus = message.payload.status;
- if (message.events.includes('functions.*.deployments.*.create')) {
- invalidate(Dependencies.DEPLOYMENTS);
- return;
- }
- if (message.events.includes('functions.*.deployments.*.update')) {
- invalidate(Dependencies.DEPLOYMENTS);
- invalidate(Dependencies.FUNCTION);
- return;
- }
- if (message.events.includes('functions.*.deployments.*.delete')) {
- invalidate(Dependencies.DEPLOYMENTS);
- return;
- }
- });
+ return realtime.forProject(page.params.region, 'console', (response) => {
+ const payload = response.payload as Models.Deployment;
+ if (payload.status !== 'ready' && previousStatus === payload.status) {
+ return;
+ }
+
+ previousStatus = payload.status;
+ if (response.events.includes('functions.*.deployments.*.create')) {
+ invalidate(Dependencies.DEPLOYMENTS);
+ return;
+ }
+
+ if (response.events.includes('functions.*.deployments.*.update')) {
+ invalidate(Dependencies.DEPLOYMENTS);
+ invalidate(Dependencies.FUNCTION);
+ return;
+ }
+
+ if (response.events.includes('functions.*.deployments.*.delete')) {
+ invalidate(Dependencies.DEPLOYMENTS);
+ return;
+ }
+ });
});
$: $registerCommands([
diff --git a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/deployment-[deployment]/+page.svelte b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/deployment-[deployment]/+page.svelte
index 83247cdb2..52adc05e9 100644
--- a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/deployment-[deployment]/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/deployment-[deployment]/+page.svelte
@@ -1,7 +1,7 @@
diff --git a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/+page.svelte b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/+page.svelte
index 7c4280fc1..55a275d5b 100644
--- a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/+page.svelte
@@ -4,7 +4,7 @@
import { Dependencies } from '$lib/constants';
import { Button } from '$lib/elements/forms';
import { Container, ResponsiveContainerHeader } from '$lib/layout';
- import { sdk } from '$lib/stores/sdk';
+ import { realtime } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { project } from '$routes/(console)/project-[region]-[project]/store';
import { base } from '$app/paths';
@@ -12,11 +12,13 @@
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import Table from './table.svelte';
import { columns } from './store';
+ import type { PageProps } from './$types';
+ import { page } from '$app/state';
- export let data;
+ let { data }: PageProps = $props();
onMount(() => {
- return sdk.forConsole.realtime.subscribe('console', (response) => {
+ return realtime.forConsole(page.params.region, 'console', (response) => {
if (response.events.includes('functions.*.executions.*')) {
invalidate(Dependencies.EXECUTIONS);
}
diff --git a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte
index 8f0785ed7..f681c04ef 100644
--- a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte
@@ -1,17 +1,10 @@
-
-
+
+ {#snippet header(root)}
{#each columns as { id, title }}
{title}
{/each}
-
- {#each executions.executions as log (log.$id)}
- {
- e.stopPropagation();
- open = true;
- selectedLogId = log.$id;
- }}>
- {#each columns as column}
-
- {#if column.id === '$id'}
- {#key column.id}
- {log.$id}
- {/key}
- {:else if column.id === 'deploymentId'}
- {log.deploymentId}
- {:else if column.id === '$createdAt'}
-
- {:else if column.id === 'requestPath'}
-
- {log.requestPath}
-
- {:else if column.id === 'responseStatusCode'}
-
- {:else if column.id === 'requestMethod'}
-
- {log.requestMethod}
-
- {:else if column.id === 'trigger'}
- {capitalize(log.trigger)}
- {:else if column.id === 'status'}
- {@const status = log.status}
-
-
-
-
-
-
- {`Scheduled to execute on ${toLocaleDateTime(log.scheduledAt)}`}
-
-
- {:else if column.id === 'duration'}
- {#if ['processing', 'waiting'].includes(log.status)}
-
- {:else}
- {calculateTime(log.duration)}
+ {/snippet}
+
+ {#snippet children(root)}
+ {#each executions.executions as log (log.$id)}
+ {
+ e.stopPropagation();
+ open = true;
+ selectedLogId = log.$id;
+ }}>
+ {#each columns as column}
+
+ {#if column.id === '$id'}
+ {#key column.id}
+ {log.$id}
+ {/key}
+ {:else if column.id === 'deploymentId'}
+ {log.deploymentId}
+ {:else if column.id === '$createdAt'}
+
+ {:else if column.id === 'requestPath'}
+
+ {log.requestPath}
+
+ {:else if column.id === 'responseStatusCode'}
+
+ {:else if column.id === 'requestMethod'}
+
+ {log.requestMethod}
+
+ {:else if column.id === 'trigger'}
+ {capitalize(log.trigger)}
+ {:else if column.id === 'status'}
+ {@const status = log.status}
+
+
+
+
+
+
+ {`Scheduled to execute on ${toLocaleDateTime(log.scheduledAt)}`}
+
+
+ {:else if column.id === 'duration'}
+ {#if ['processing', 'waiting'].includes(log.status)}
+
+ {:else}
+ {calculateTime(log.duration)}
+ {/if}
{/if}
- {/if}
-
- {/each}
-
- {/each}
-
+
+ {/each}
+
+ {/each}
+ {/snippet}
+
-
-{#if selectedRows.length > 0}
-
-
-
-
- {selectedRows.length > 1 ? 'executions' : 'execution'}
- selected
-
-
-
- (selectedRows = [])}>Cancel
- (showBatchDeletion = true)}>Delete
-
-
-{/if}
-
-
-
- Are you sure you want to delete {selectedRows.length}
- {selectedRows.length > 1 ? 'executions' : 'execution'}?
-
-
- This action is irreversible.
-
diff --git a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte
index eecafbd5a..c4c1d1743 100644
--- a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte
@@ -1,5 +1,5 @@
-
-
+ {#snippet header(root)}
{#each columns as { id, title }}
{title}
{/each}
-
- {#each data.deploymentList.deployments as deployment (deployment.$id)}
-
- {#each columns as column}
-
- {#if column.id === '$id'}
- {#key column.id}
- {deployment.$id}
- {/key}
- {:else if column.id === 'status'}
- {@const status = deployment.status}
+ {/snippet}
- {#if data?.activeDeployment?.$id === deployment?.$id}
-
- {:else}
-
- {/if}
- {:else if column.id === 'type'}
-
- {:else if column.id === '$updatedAt'}
-
- {:else if column.id === 'buildDuration'}
- {#if ['waiting'].includes(deployment.status)}
- -
- {:else if ['processing', 'building'].includes(deployment.status)}
-
- {:else}
- {formatTimeDetailed(deployment.buildDuration)}
- {/if}
- {:else if column.id === 'totalSize'}
- {calculateSize(deployment.totalSize)}
- {:else if column.id === 'sourceSize'}
- {calculateSize(deployment.sourceSize)}
- {:else if column.id === 'buildSize'}
- {calculateSize(deployment.buildSize)}
- {/if}
-
- {/each}
-
-
-
-
-
+ {#snippet children(root)}
+ {#each data.deploymentList.deployments as deployment (deployment.$id)}
+
+ {#each columns as column}
+
+ {#if column.id === '$id'}
+ {#key column.id}
+ {deployment.$id}
+ {/key}
+ {:else if column.id === 'status'}
+ {@const status = deployment.status}
-
-
-
-
+ {#if data?.activeDeployment?.$id === deployment?.$id}
+
+ {:else}
+
+ {/if}
+ {:else if column.id === 'type'}
+
+ {:else if column.id === '$updatedAt'}
+
+ {:else if column.id === 'buildDuration'}
+ {#if ['waiting'].includes(deployment.status)}
+ -
+ {:else if ['processing', 'building'].includes(deployment.status)}
+
+ {:else}
+ {formatTimeDetailed(deployment.buildDuration)}
+ {/if}
+ {:else if column.id === 'totalSize'}
+ {calculateSize(deployment.totalSize)}
+ {:else if column.id === 'sourceSize'}
+ {calculateSize(deployment.sourceSize)}
+ {:else if column.id === 'buildSize'}
+ {calculateSize(deployment.buildSize)}
+ {/if}
+
+ {/each}
+
+
+
+
+
+
+
+
+
+
+
{
+ selectedDeployment = deployment;
+ showRedeploy = true;
+ toggle();
+ trackEvent(Click.FunctionsRedeployClick);
+ }}
+ style="width: 100%">
+ Redeploy
+
+
+ Source is empty
+
+ {#if deployment.status === 'ready' && deployment.$id !== $func.deploymentId}
{
selectedDeployment = deployment;
- showRedeploy = true;
+ showActivate = true;
toggle();
- trackEvent(Click.FunctionsRedeployClick);
- }}
- style="width: 100%">
- Redeploy
+ }}>
+ Activate
-
- Source is empty
-
- {#if deployment.status === 'ready' && deployment.$id !== $func.deploymentId}
- {
- selectedDeployment = deployment;
- showActivate = true;
- toggle();
- }}>
- Activate
-
- {/if}
+ {/if}
-
+
- {#if deployment.status === 'processing' || deployment.status === 'building' || deployment.status === 'waiting'}
- {
- selectedDeployment = deployment;
- toggle();
+ {#if deployment.status === 'processing' || deployment.status === 'building' || deployment.status === 'waiting'}
+ {
+ selectedDeployment = deployment;
+ toggle();
- showCancel = true;
- trackEvent(Click.FunctionsDeploymentCancelClick);
- }}>
- Cancel
-
- {/if}
- {#if deployment.status !== 'building' && deployment.status !== 'processing' && deployment.status !== 'waiting'}
- {
- selectedDeployment = deployment;
- toggle();
+ showCancel = true;
+ trackEvent(Click.FunctionsDeploymentCancelClick);
+ }}>
+ Cancel
+
+ {/if}
+ {#if deployment.status !== 'building' && deployment.status !== 'processing' && deployment.status !== 'waiting'}
+ {
+ selectedDeployment = deployment;
+ toggle();
- showDelete = true;
- trackEvent(Click.FunctionsDeploymentDeleteClick);
- }}>
- Delete
-
- {/if}
-
-
-
-
-
- {/each}
-
+ showDelete = true;
+ trackEvent(Click.FunctionsDeploymentDeleteClick);
+ }}>
+ Delete
+
+ {/if}
+
+
+
+
+
+ {/each}
+ {/snippet}
+
+ {#snippet deleteContent(count)}
+
+ Are you sure you want to delete {count}
+ {count > 1 ? 'deployments' : 'deployment'} from your function -
+ {page.data.function.name} ?
+
+ {/snippet}
+
{#if selectedDeployment}
-
+
{/if}
-
-{#if selectedRows.length > 0}
-
-
-
-
- {selectedRows.length > 1 ? 'deployments' : 'deployment'}
- selected
-
-
-
- (selectedRows = [])}>Cancel
- (showBatchDeletion = true)}>Delete
-
-
-{/if}
-
-
-
- Are you sure you want to delete {selectedRows.length}
- {selectedRows.length > 1 ? 'deployments' : 'deployment'} from your function -
- {$func.name} ?
-
-
- This action is irreversible.
-
diff --git a/src/routes/(console)/project-[region]-[project]/messaging/+page.svelte b/src/routes/(console)/project-[region]-[project]/messaging/+page.svelte
index 6f009719b..b096f2561 100644
--- a/src/routes/(console)/project-[region]-[project]/messaging/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/messaging/+page.svelte
@@ -1,7 +1,15 @@
-
-
+ onDelete={handleDelete}
+ allowSelection={$canWriteProviders}>
+ {#snippet header(root)}
{#each $columns as { id, title }}
{title}
{/each}
-
- {#each data.providers.providers as provider (provider.$id)}
-
- {#each $columns as column}
-
- {#if column.id === '$id'}
- {#key $columns}
- {provider.$id}
- {/key}
- {:else if column.id === 'provider'}
-
- {:else if column.id === 'type'}
-
- {:else if column.id === 'enabled'}
-
-
- {#if provider.enabled}
-
- {/if}
-
-
- {:else}
- {provider[column.id]}
- {/if}
-
- {/each}
-
- {/each}
-
-
-{#if selectedIds.length > 0}
-
-
-
-
- {selectedIds.length > 1 ? 'providers' : 'provider'}
- selected
-
-
-
- (selectedIds = [])}>Cancel
- (showDelete = true)}>Delete
-
-
-{/if}
-
-
-
- Are you sure you want to delete {selectedIds.length}
- {selectedIds.length > 1 ? 'providers' : 'provider'}?
-
-
+
+ {#each $columns as column}
+
+ {#if column.id === '$id'}
+ {#key $columns}
+ {provider.$id}
+ {/key}
+ {:else if column.id === 'provider'}
+
+ {:else if column.id === 'type'}
+
+ {:else if column.id === 'enabled'}
+
+
+ {#if provider.enabled}
+
+ {/if}
+
+
+ {:else}
+ {provider[column.id]}
+ {/if}
+
+ {/each}
+
+ {/each}
+ {/snippet}
+
diff --git a/src/routes/(console)/project-[region]-[project]/messaging/topics/table.svelte b/src/routes/(console)/project-[region]-[project]/messaging/topics/table.svelte
index b4d653169..f5525754e 100644
--- a/src/routes/(console)/project-[region]-[project]/messaging/topics/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/messaging/topics/table.svelte
@@ -1,9 +1,7 @@
-
-
+
+ {#snippet header(root)}
{#each columns as { id, title }}
{title}
{/each}
-
- {#each data.topics.topics as topic (topic.$id)}
-
- {#each columns as column (column.id)}
-
- {#if column.id === '$id'}
- {#key column.id}
- {topic.$id}
- {/key}
- {:else if column.type === 'datetime'}
- {#if topic[column.id]}
-
- {:else}-{/if}
- {:else if column.id === 'total'}
- {topic.emailTotal + topic.smsTotal + topic.pushTotal}
- {:else}
- {topic[column.id]}
- {/if}
-
- {/each}
-
- {/each}
-
+ {/snippet}
-{#if selectedIds.length > 0}
-
-
-
-
- {selectedIds.length > 1 ? 'topics' : 'topic'}
- selected
-
-
-
- (selectedIds = [])}>Cancel
- (showDelete = true)}>Delete
-
-
-{/if}
-
-
-
- Are you sure you want to delete {selectedIds.length}
- {selectedIds.length > 1 ? 'topics' : 'topic'}?
-
-
+ {#snippet children(root)}
+ {#each data.topics.topics as topic (topic.$id)}
+
+ {#each columns as column (column.id)}
+
+ {#if column.id === '$id'}
+ {#key column.id}
+ {topic.$id}
+ {/key}
+ {:else if column.type === 'datetime'}
+ {#if topic[column.id]}
+
+ {:else}-{/if}
+ {:else if column.id === 'total'}
+ {topic.emailTotal + topic.smsTotal + topic.pushTotal}
+ {:else}
+ {topic[column.id]}
+ {/if}
+
+ {/each}
+
+ {/each}
+ {/snippet}
+
diff --git a/src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte b/src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte
index a42fcfd07..c9a64e9af 100644
--- a/src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte
@@ -2,10 +2,8 @@
import { invalidate } from '$app/navigation';
import { base } from '$app/paths';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
- import { Id } from '$lib/components';
+ import { type DeleteOperationState, Id, MultiSelectionTable } from '$lib/components';
import { Dependencies } from '$lib/constants';
- import { Button } from '$lib/elements/forms';
- import { addNotification } from '$lib/stores/notifications';
import type { PageData } from './$types';
import ProviderType from '../../providerType.svelte';
import DualTimeView from '$lib/components/dualTimeView.svelte';
@@ -15,21 +13,26 @@
import { targetsById } from '../../store';
import { MessagingProviderType, type Models } from '@appwrite.io/console';
import type { Column } from '$lib/helpers/types';
- import { Badge, FloatingActionBar, Table, Typography } from '@appwrite.io/pink-svelte';
- import Confirm from '$lib/components/confirm.svelte';
+ import { Table } from '@appwrite.io/pink-svelte';
- export let columns: Column[];
- export let data: PageData;
+ let {
+ data,
+ columns
+ }: {
+ data: PageData;
+ columns: Column[];
+ } = $props();
- let subscribers: Record = {};
- let selectedIds: string[] = [];
- let selected: Record = {};
- let showDelete = false;
- let deleting = false;
+ const subscribers = $derived.by(() => {
+ const record: Record = {};
+ for (const subscriber of data.subscribers.subscribers) {
+ record[subscriber.$id] = subscriber;
+ }
- async function handleDelete() {
- showDelete = false;
+ return record;
+ });
+ async function handleDelete(selectedRows: string[]): Promise {
async function deleteSubscriber(subscriberId: string) {
await sdk
.forProject(page.params.region, page.params.project)
@@ -37,111 +40,68 @@
topicId: page.params.topic,
subscriberId
});
+
const { target } = subscribers[subscriberId];
const { [target.$id]: _, ...rest } = $targetsById;
$targetsById = rest;
}
- const promises = selectedIds.map((id) => deleteSubscriber(id));
+ const promises = selectedRows.map((id) => deleteSubscriber(id));
try {
await Promise.all(promises);
- trackEvent(Submit.MessagingTopicSubscriberDelete, {
- total: selectedIds.length
- });
- addNotification({
- type: 'success',
- message: `${selectedIds.length} subscriber${
- selectedIds.length > 1 ? 's' : ''
- } deleted`
- });
- invalidate(Dependencies.MESSAGING_TOPIC_SUBSCRIBERS);
+ trackEvent(Submit.MessagingTopicSubscriberDelete, { total: selectedRows.length });
} catch (error) {
- addNotification({
- type: 'error',
- message: error.message
- });
trackError(error, Submit.MessagingTopicSubscriberDelete);
+ return error;
} finally {
- selectedIds = [];
- showDelete = false;
+ await invalidate(Dependencies.MESSAGING_TOPIC_SUBSCRIBERS);
}
}
-
- $: data.subscribers.subscribers.forEach((s) => {
- subscribers[s.$id] = s;
- });
- $: selectedIds.forEach((id) => {
- selected[id] = subscribers[id];
- });
-
-
+
+ {#snippet header(root)}
{#each columns as { id, title }}
{title}
{/each}
-
- {#each data.subscribers.subscribers as subscriber (subscriber.$id)}
- {@const target = subscriber.target}
-
- {#each columns as column}
-
- {#if column.id === '$id'}
- {#key column.id}
-
- {subscriber.$id}
+ {/snippet}
+
+ {#snippet children(root)}
+ {#each data.subscribers.subscribers as subscriber (subscriber.$id)}
+ {@const target = subscriber.target}
+
+ {#each columns as column}
+
+ {#if column.id === '$id'}
+ {#key column.id}
+
+ {subscriber.$id}
+
+ {/key}
+ {:else if column.id === 'targetId'}
+
+ {subscriber[column.id]}
- {/key}
- {:else if column.id === 'targetId'}
-
- {subscriber[column.id]}
-
- {:else if column.id === 'target'}
- {#if target.providerType === MessagingProviderType.Push}
- {target.name}
+ {:else if column.id === 'target'}
+ {#if target.providerType === MessagingProviderType.Push}
+ {target.name}
+ {:else}
+ {target.identifier}
+ {/if}
+ {:else if column.id === 'type'}
+
+ {:else if column.id === '$createdAt'}
+
{:else}
- {target.identifier}
+ {subscriber[column.id]}
{/if}
- {:else if column.id === 'type'}
-
- {:else if column.id === '$createdAt'}
-
- {:else}
- {subscriber[column.id]}
- {/if}
-
- {/each}
-
- {/each}
-
-
-{#if selectedIds.length > 0}
-
-
-
-
- {selectedIds.length > 1 ? 'subscribers' : 'subscriber'}
- selected
-
-
-
- (selectedIds = [])}>Cancel
- (showDelete = true)}>Delete
-
-
-{/if}
-
-
-
- Are you sure you want to delete {selectedIds.length}
- {selectedIds.length > 1 ? 'subscribers' : 'subscriber'}?
-
-
+
+ {/each}
+
+ {/each}
+ {/snippet}
+
diff --git a/src/routes/(console)/project-[region]-[project]/overview/(components)/create.svelte b/src/routes/(console)/project-[region]-[project]/overview/(components)/create.svelte
index 0df14759c..84e6a7809 100644
--- a/src/routes/(console)/project-[region]-[project]/overview/(components)/create.svelte
+++ b/src/routes/(console)/project-[region]-[project]/overview/(components)/create.svelte
@@ -15,6 +15,7 @@
import { writable } from 'svelte/store';
import Scopes from '../api-keys/scopes.svelte';
import { page } from '$app/state';
+ import { copy } from '$lib/helpers/copy';
const projectId = page.params.project;
@@ -28,7 +29,7 @@
async function create() {
try {
- const { $id } = await sdk.forConsole.projects.createKey({
+ const { $id, secret } = await sdk.forConsole.projects.createKey({
projectId,
name,
scopes,
@@ -45,7 +46,21 @@
);
addNotification({
message: `API key has been created`,
- type: 'success'
+ type: 'success',
+ buttons: [
+ {
+ name: 'Copy API key',
+ method: async () => {
+ await copy(secret);
+ }
+ },
+ {
+ name: 'Copy endpoint',
+ method: async () => {
+ await copy(sdk.forConsole.client.config.endpoint);
+ }
+ }
+ ]
});
} catch (error) {
addNotification({
diff --git a/src/routes/(console)/project-[region]-[project]/overview/(components)/deleteBatch.svelte b/src/routes/(console)/project-[region]-[project]/overview/(components)/deleteBatch.svelte
index ff694c3cd..3a742d94c 100644
--- a/src/routes/(console)/project-[region]-[project]/overview/(components)/deleteBatch.svelte
+++ b/src/routes/(console)/project-[region]-[project]/overview/(components)/deleteBatch.svelte
@@ -59,7 +59,12 @@
}
-
+ 1 ? 's' : ''}`}>
Are you sure you want to delete {keyIds.length}
{label} key{keyIds.length > 1 ? 's' : ''}?
diff --git a/src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte b/src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte
index 28bf1a389..9cfb56a83 100644
--- a/src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte
@@ -2,27 +2,32 @@
import { base } from '$app/paths';
import { page } from '$app/state';
import { goto } from '$app/navigation';
- import { Empty } from '$lib/components';
- import { Button } from '$lib/elements/forms';
+ import { Empty, MultiSelectionTable } from '$lib/components';
import { canWriteKeys } from '$lib/stores/roles';
import type { Models } from '@appwrite.io/console';
import { diffDays } from '$lib/helpers/date';
import DualTimeView from '$lib/components/dualTimeView.svelte';
import { devKeyColumns, keyColumns, showDevKeysCreateModal } from '../store';
- import { Badge, FloatingActionBar, Layout, Table } from '@appwrite.io/pink-svelte';
+ import { Badge, Layout, Table } from '@appwrite.io/pink-svelte';
import DeleteBatch from './deleteBatch.svelte';
import { capitalize } from '$lib/helpers/string';
import { getEffectiveScopes } from '../api-keys/scopes.svelte';
- export let keyType: 'api' | 'dev' = 'api';
- export let keys: Models.KeyList | Models.DevKeyList;
+ let {
+ keyType = 'api',
+ keys
+ }: {
+ keyType?: 'api' | 'dev';
+ keys: Models.KeyList | Models.DevKeyList;
+ } = $props();
- let showDeleteModal = false;
- let selectedRows: string[] = [];
+ let selectedKeys = $state([]);
+ let showDeleteModal = $state(false);
const isApiKey = keyType === 'api';
const label = isApiKey ? 'API' : 'dev';
const slug = isApiKey ? 'api-keys' : 'dev-keys';
+ const columns = isApiKey ? $keyColumns : $devKeyColumns;
function getApiKeyScopeCount(key: Models.Key | Models.DevKey) {
const apiKey = key as Models.Key;
@@ -53,55 +58,65 @@
else
return 'Dev keys allow bypassing rate limits and CORS errors in your development environment.';
}
-
- const columns = isApiKey ? $keyColumns : $devKeyColumns;
{#if keys.total}
-
-
+ {
+ showDeleteModal = true;
+ selectedKeys = selectedRows;
+ }}>
+ {#snippet header(root)}
{#each columns as column}
{column.title}
{/each}
-
- {#each getKeys() as key (key.$id)}
-
-
- {key.name}
-
-
- {#if key.accessedAt}
-
- {:else}
- never
- {/if}
-
-
- {@const expiration = getExpiryDetails(key)}
-
- {#if key.expire}
-
+ {/snippet}
+
+ {#snippet children(root)}
+ {#each getKeys() as key (key.$id)}
+
+
+ {key.name}
+
+
+ {#if key.accessedAt}
+
{:else}
never
{/if}
-
- {#if expiration.status}
-
- {/if}
-
-
- {#if isApiKey}
-
- {getApiKeyScopeCount(key)} Scopes
- {/if}
-
- {/each}
-
+
+ {@const expiration = getExpiryDetails(key)}
+
+ {#if key.expire}
+
+ {:else}
+ never
+ {/if}
+
+ {#if expiration.status}
+
+ {/if}
+
+
+ {#if isApiKey}
+
+ {getApiKeyScopeCount(key)} Scopes
+
+ {/if}
+
+ {/each}
+ {/snippet}
+
{:else}
{
+ on:click={async () => {
if (isApiKey) {
- goto(
+ await goto(
`${base}/project-${page.params.region}-${page.params.project}/overview/${slug}/create`
);
} else {
@@ -120,21 +135,4 @@
}} />
{/if}
-{#if selectedRows.length > 0}
-
-
-
-
- {capitalize(label)}
- {selectedRows.length > 1 ? 'keys' : 'key'}
- selected
-
-
-
- (selectedRows = [])}>Cancel
- (showDeleteModal = true)}>Delete
-
-
-{/if}
-
-
+
diff --git a/src/routes/(console)/project-[region]-[project]/overview/api-keys/[key]/header.svelte b/src/routes/(console)/project-[region]-[project]/overview/api-keys/[key]/header.svelte
index 0b4b527d6..984700097 100644
--- a/src/routes/(console)/project-[region]-[project]/overview/api-keys/[key]/header.svelte
+++ b/src/routes/(console)/project-[region]-[project]/overview/api-keys/[key]/header.svelte
@@ -3,6 +3,10 @@
import { page } from '$app/state';
import { Cover, CoverTitle } from '$lib/layout';
import { key } from './store';
+ import { RegionEndpoint, Copy } from '$lib/components';
+ import { Layout, Tag, Icon } from '@appwrite.io/pink-svelte';
+ import { IconDuplicate } from '@appwrite.io/pink-icons-svelte';
+ import { projectRegion } from '../../../store';
const projectId = page.params.project;
@@ -12,5 +16,26 @@
{$key?.name}
+
+ {#if $key?.secret}
+
+
+
+ API secret
+
+
+ {/if}
+ {#if $projectRegion}
+
+ {/if}
+
+
+
diff --git a/src/routes/(console)/project-[region]-[project]/overview/assets/platform-sdk-dark.png b/src/routes/(console)/project-[region]-[project]/overview/assets/platform-sdk-dark.png
new file mode 100644
index 000000000..e778c55b9
Binary files /dev/null and b/src/routes/(console)/project-[region]-[project]/overview/assets/platform-sdk-dark.png differ
diff --git a/src/routes/(console)/project-[region]-[project]/overview/assets/platform-sdk.jpg b/src/routes/(console)/project-[region]-[project]/overview/assets/platform-sdk.jpg
new file mode 100644
index 000000000..b891d6a12
Binary files /dev/null and b/src/routes/(console)/project-[region]-[project]/overview/assets/platform-sdk.jpg differ
diff --git a/src/routes/(console)/project-[region]-[project]/overview/header.svelte b/src/routes/(console)/project-[region]-[project]/overview/header.svelte
index 8a412281c..ff2107397 100644
--- a/src/routes/(console)/project-[region]-[project]/overview/header.svelte
+++ b/src/routes/(console)/project-[region]-[project]/overview/header.svelte
@@ -2,19 +2,24 @@
import { page } from '$app/state';
import { Id, RegionEndpoint } from '$lib/components';
import { Cover } from '$lib/layout';
- import { project, projectRegion } from '../store';
+ import { projectRegion } from '../store';
import { hasOnboardingDismissed, setHasOnboardingDismissed } from '$lib/helpers/onboarding';
import { goto } from '$app/navigation';
- import { base } from '$app/paths';
+ import { resolve } from '$app/paths';
import { Layout, Button, Typography } from '@appwrite.io/pink-svelte';
import { user } from '$lib/stores/user';
import { isSmallViewport } from '$lib/stores/viewport';
import { trackEvent } from '$lib/actions/analytics';
function dismissOnboarding() {
- setHasOnboardingDismissed($project.$id, $user);
+ setHasOnboardingDismissed(page.params.project, $user);
trackEvent('onboarding_hub_platform_dismiss');
- goto(`${base}/project-${$project.region}-${$project.$id}/overview/platforms`);
+ goto(
+ resolve('/(console)/project-[region]-[project]/overview/platforms', {
+ region: page.params.region,
+ project: page.params.project
+ })
+ );
}
@@ -24,12 +29,15 @@
- {$project?.name}
+ {page.data.project?.name}
- {$project.$id}
-
+ {page.params.project}
+ {#if $projectRegion}
+
+ {/if}
@@ -53,7 +61,7 @@
>Follow a few quick steps to get started with Appwrite
-
+
{
openPlatformWizard(0, platformMap.get('Web'));
@@ -341,17 +339,34 @@
-
- Or connect
- server side
-
-
+ or
+
+
+
+
+
+
+ Create API key
+ Connect your server or backend to Appwrite
+
+
+
+
+
+
+
+
{
- let subscription: AppwriteRealtimeSubscription;
- sdk.forConsole.realtime
- .subscribe('console', (response) => {
- if (response.events.includes(`projects.${projectId}.ping`)) {
- connectionSuccessful = true;
- invalidate(Dependencies.ORGANIZATION);
- invalidate(Dependencies.PROJECT);
- subscription?.close();
- }
- })
- .then((realtime) => (subscription = realtime));
+ const unsubscribe = realtime.forConsole(page.params.region, 'console', (response) => {
+ if (response.events.includes(`projects.${projectId}.ping`)) {
+ connectionSuccessful = true;
+ invalidate(Dependencies.ORGANIZATION);
+ invalidate(Dependencies.PROJECT);
+ unsubscribe();
+ }
+ });
return () => {
- subscription?.close();
+ unsubscribe();
resetPlatformStore();
};
});
diff --git a/src/routes/(console)/project-[region]-[project]/overview/platforms/createApple.svelte b/src/routes/(console)/project-[region]-[project]/overview/platforms/createApple.svelte
index 0f428a78e..a8fea18ca 100644
--- a/src/routes/(console)/project-[region]-[project]/overview/platforms/createApple.svelte
+++ b/src/routes/(console)/project-[region]-[project]/overview/platforms/createApple.svelte
@@ -18,7 +18,7 @@
import { Card } from '$lib/components';
import { page } from '$app/state';
import { onMount } from 'svelte';
- import { type AppwriteRealtimeSubscription, sdk } from '$lib/stores/sdk';
+ import { realtime, sdk } from '$lib/stores/sdk';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { addNotification } from '$lib/stores/notifications';
import { fade } from 'svelte/transition';
@@ -97,8 +97,10 @@ APPWRITE_PUBLIC_ENDPOINT: "${sdk.forProject(page.params.region, page.params.proj
message: 'Platform created.'
});
- invalidate(Dependencies.PROJECT);
- invalidate(Dependencies.PLATFORMS);
+ await Promise.all([
+ invalidate(Dependencies.PROJECT),
+ invalidate(Dependencies.PLATFORMS)
+ ]);
} catch (error) {
trackError(error, Submit.PlatformCreate);
addNotification({
@@ -115,20 +117,17 @@ APPWRITE_PUBLIC_ENDPOINT: "${sdk.forProject(page.params.region, page.params.proj
}
onMount(() => {
- let subscription: AppwriteRealtimeSubscription;
- sdk.forConsole.realtime
- .subscribe('console', (response) => {
- if (response.events.includes(`projects.${projectId}.ping`)) {
- connectionSuccessful = true;
- invalidate(Dependencies.ORGANIZATION);
- invalidate(Dependencies.PROJECT);
- subscription?.close();
- }
- })
- .then((realtime) => (subscription = realtime));
+ const unsubscribe = realtime.forConsole(page.params.region, 'console', (response) => {
+ if (response.events.includes(`projects.${projectId}.ping`)) {
+ connectionSuccessful = true;
+ invalidate(Dependencies.ORGANIZATION);
+ invalidate(Dependencies.PROJECT);
+ unsubscribe();
+ }
+ });
return () => {
- subscription?.close();
+ unsubscribe();
resetPlatformStore();
};
});
diff --git a/src/routes/(console)/project-[region]-[project]/overview/platforms/createFlutter.svelte b/src/routes/(console)/project-[region]-[project]/overview/platforms/createFlutter.svelte
index e0e662602..9f567ff28 100644
--- a/src/routes/(console)/project-[region]-[project]/overview/platforms/createFlutter.svelte
+++ b/src/routes/(console)/project-[region]-[project]/overview/platforms/createFlutter.svelte
@@ -18,7 +18,7 @@
import { Card } from '$lib/components';
import { page } from '$app/state';
import { onMount } from 'svelte';
- import { type AppwriteRealtimeSubscription, sdk } from '$lib/stores/sdk';
+ import { realtime, sdk } from '$lib/stores/sdk';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { addNotification } from '$lib/stores/notifications';
import { fade } from 'svelte/transition';
@@ -161,8 +161,10 @@ client.ping();
message: 'Platform created.'
});
- invalidate(Dependencies.PROJECT);
- invalidate(Dependencies.PLATFORMS);
+ await Promise.all([
+ invalidate(Dependencies.PROJECT),
+ invalidate(Dependencies.PLATFORMS)
+ ]);
} catch (error) {
trackError(error, Submit.PlatformCreate);
addNotification({
@@ -179,20 +181,17 @@ client.ping();
}
onMount(() => {
- let subscription: AppwriteRealtimeSubscription;
- sdk.forConsole.realtime
- .subscribe('console', (response) => {
- if (response.events.includes(`projects.${projectId}.ping`)) {
- connectionSuccessful = true;
- invalidate(Dependencies.ORGANIZATION);
- invalidate(Dependencies.PROJECT);
- subscription?.close();
- }
- })
- .then((realtime) => (subscription = realtime));
+ const unsubscribe = realtime.forConsole(page.params.region, 'console', (response) => {
+ if (response.events.includes(`projects.${projectId}.ping`)) {
+ connectionSuccessful = true;
+ invalidate(Dependencies.ORGANIZATION);
+ invalidate(Dependencies.PROJECT);
+ unsubscribe();
+ }
+ });
return () => {
- subscription?.close();
+ unsubscribe();
resetPlatformStore();
};
});
diff --git a/src/routes/(console)/project-[region]-[project]/overview/platforms/createReactNative.svelte b/src/routes/(console)/project-[region]-[project]/overview/platforms/createReactNative.svelte
index af345372e..0a55a52a9 100644
--- a/src/routes/(console)/project-[region]-[project]/overview/platforms/createReactNative.svelte
+++ b/src/routes/(console)/project-[region]-[project]/overview/platforms/createReactNative.svelte
@@ -18,7 +18,7 @@
import { Card } from '$lib/components';
import { page } from '$app/state';
import { onMount } from 'svelte';
- import { type AppwriteRealtimeSubscription, sdk } from '$lib/stores/sdk';
+ import { realtime, sdk } from '$lib/stores/sdk';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { addNotification } from '$lib/stores/notifications';
import { fade } from 'svelte/transition';
@@ -128,8 +128,10 @@ EXPO_PUBLIC_APPWRITE_ENDPOINT=${sdk.forProject(page.params.region, page.params.p
message: 'Platform created.'
});
- invalidate(Dependencies.PROJECT);
- invalidate(Dependencies.PLATFORMS);
+ await Promise.all([
+ invalidate(Dependencies.PROJECT),
+ invalidate(Dependencies.PLATFORMS)
+ ]);
} catch (error) {
trackError(error, Submit.PlatformCreate);
addNotification({
@@ -146,20 +148,17 @@ EXPO_PUBLIC_APPWRITE_ENDPOINT=${sdk.forProject(page.params.region, page.params.p
}
onMount(() => {
- let subscription: AppwriteRealtimeSubscription;
- sdk.forConsole.realtime
- .subscribe('console', (response) => {
- if (response.events.includes(`projects.${projectId}.ping`)) {
- connectionSuccessful = true;
- invalidate(Dependencies.ORGANIZATION);
- invalidate(Dependencies.PROJECT);
- subscription?.close();
- }
- })
- .then((realtime) => (subscription = realtime));
+ const unsubscribe = realtime.forConsole(page.params.region, 'console', (response) => {
+ if (response.events.includes(`projects.${projectId}.ping`)) {
+ connectionSuccessful = true;
+ invalidate(Dependencies.ORGANIZATION);
+ invalidate(Dependencies.PROJECT);
+ unsubscribe();
+ }
+ });
return () => {
- subscription?.close();
+ unsubscribe();
resetPlatformStore();
};
});
diff --git a/src/routes/(console)/project-[region]-[project]/overview/platforms/createWeb.svelte b/src/routes/(console)/project-[region]-[project]/overview/platforms/createWeb.svelte
index debaff0fc..8516bac0c 100644
--- a/src/routes/(console)/project-[region]-[project]/overview/platforms/createWeb.svelte
+++ b/src/routes/(console)/project-[region]-[project]/overview/platforms/createWeb.svelte
@@ -28,7 +28,7 @@
} from '@appwrite.io/pink-icons-svelte';
import { page } from '$app/state';
import { onMount } from 'svelte';
- import { type AppwriteRealtimeSubscription, sdk } from '$lib/stores/sdk';
+ import { realtime, sdk } from '$lib/stores/sdk';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { addNotification } from '$lib/stores/notifications';
import { fade } from 'svelte/transition';
@@ -241,8 +241,10 @@ APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.project).c
message: 'Platform created.'
});
- invalidate(Dependencies.PROJECT);
- invalidate(Dependencies.PLATFORMS);
+ await Promise.all([
+ invalidate(Dependencies.PROJECT),
+ invalidate(Dependencies.PLATFORMS)
+ ]);
} catch (error) {
trackError(error, Submit.PlatformCreate);
addNotification({
@@ -259,21 +261,18 @@ APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.project).c
}
onMount(() => {
- let subscription: AppwriteRealtimeSubscription;
- sdk.forConsole.realtime
- .subscribe('console', (response) => {
- if (response.events.includes(`projects.${projectId}.ping`)) {
- connectionSuccessful = true;
- invalidate(Dependencies.ORGANIZATION);
- invalidate(Dependencies.PROJECT);
- subscription?.close();
- }
- })
- .then((realtime) => (subscription = realtime));
+ const unsubscribe = realtime.forConsole(page.params.region, 'console', (response) => {
+ if (response.events.includes(`projects.${projectId}.ping`)) {
+ connectionSuccessful = true;
+ invalidate(Dependencies.ORGANIZATION);
+ invalidate(Dependencies.PROJECT);
+ unsubscribe();
+ }
+ });
return () => {
+ unsubscribe();
resetPlatformStore();
- subscription?.close();
};
});
diff --git a/src/routes/(console)/project-[region]-[project]/settings/migrations/+page.svelte b/src/routes/(console)/project-[region]-[project]/settings/migrations/+page.svelte
index b6f8083bc..cc82cb18c 100644
--- a/src/routes/(console)/project-[region]-[project]/settings/migrations/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/settings/migrations/+page.svelte
@@ -38,13 +38,11 @@
let migration: Models.Migration = null;
onMount(() => {
- return realtime
- .forProject(page.params.region, page.params.project)
- .subscribe(['project', 'console'], (response) => {
- if (response.events.includes('migrations.*')) {
- invalidate(Dependencies.MIGRATIONS);
- }
- });
+ return realtime.forProject(page.params.region, ['project', 'console'], (response) => {
+ if (response.events.includes('migrations.*')) {
+ invalidate(Dependencies.MIGRATIONS);
+ }
+ });
});
$: $registerCommands([
diff --git a/src/routes/(console)/project-[region]-[project]/sites/+page.svelte b/src/routes/(console)/project-[region]-[project]/sites/+page.svelte
index e1eb6bb66..3c4ee175a 100644
--- a/src/routes/(console)/project-[region]-[project]/sites/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/sites/+page.svelte
@@ -25,7 +25,8 @@
import { onMount } from 'svelte';
import { invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
- import { sdk } from '$lib/stores/sdk';
+ import { realtime } from '$lib/stores/sdk';
+ import { page } from '$app/state';
export let data;
@@ -49,7 +50,7 @@
$updateCommandGroupRanks({ sites: 1000 });
onMount(() => {
- return sdk.forConsole.realtime.subscribe('console', (response) => {
+ return realtime.forConsole(page.params.region, 'console', (response) => {
if (response.events.includes('sites.*')) {
invalidate(Dependencies.SITES);
}
diff --git a/src/routes/(console)/project-[region]-[project]/sites/create-site/deploying/+page.svelte b/src/routes/(console)/project-[region]-[project]/sites/create-site/deploying/+page.svelte
index bdb1741ac..3cb100543 100644
--- a/src/routes/(console)/project-[region]-[project]/sites/create-site/deploying/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/sites/create-site/deploying/+page.svelte
@@ -7,42 +7,36 @@
import Aside from '../aside.svelte';
import Logs from '../../(components)/logs.svelte';
import { Copy, SvgIcon } from '$lib/components';
- import { type AppwriteRealtimeSubscription, sdk } from '$lib/stores/sdk';
+ import { realtime } from '$lib/stores/sdk';
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
import { getFrameworkIcon } from '$lib/stores/sites';
+ import type { Models } from '@appwrite.io/console';
let { data } = $props();
let deployment = $state(data.deployment);
onMount(() => {
- let subscription: AppwriteRealtimeSubscription;
- sdk.forConsoleIn(page.params.region)
- .realtime.subscribe('console', async (response) => {
- if (
- response.events.includes(
- `sites.${data.site.$id}.deployments.${data.deployment.$id}.update`
- )
- ) {
- deployment = response.payload;
- if (response.payload.status === 'ready') {
- const resolvedUrl = resolve(
- '/(console)/project-[region]-[project]/sites/create-site/finish',
- {
- region: page.params.region,
- project: page.params.project
- }
- );
- await goto(`${resolvedUrl}?site=${data.site.$id}`);
- }
+ return realtime.forConsole(page.params.region, 'console', async (response) => {
+ if (
+ response.events.includes(
+ `sites.${data.site.$id}.deployments.${data.deployment.$id}.update`
+ )
+ ) {
+ deployment = response.payload as Models.Deployment;
+ if (deployment.status === 'ready') {
+ const resolvedUrl = resolve(
+ '/(console)/project-[region]-[project]/sites/create-site/finish',
+ {
+ region: page.params.region,
+ project: page.params.project
+ }
+ );
+ await goto(`${resolvedUrl}?site=${data.site.$id}`);
}
- })
- .then((realtime) => (subscription = realtime));
-
- return () => {
- subscription?.close();
- };
+ }
+ });
});
diff --git a/src/routes/(console)/project-[region]-[project]/sites/create-site/manual/+page.svelte b/src/routes/(console)/project-[region]-[project]/sites/create-site/manual/+page.svelte
index 281b68314..8cecb4acc 100644
--- a/src/routes/(console)/project-[region]-[project]/sites/create-site/manual/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/sites/create-site/manual/+page.svelte
@@ -174,24 +174,21 @@
on:invalid={handleInvalid}>
-
-
- Drag and drop file here or click to upload
-
-
+
+
+ Drag and drop file here or click to upload;
-
+ justifyContent="center">
+
+
+ Only .tar.gz files allowed
+
- Only .tar.gz files allowed
-
+
{#if maxSize > 0}
{
if (variable.value === '{apiEndpoint}') {
- variable.value = getApiEndpoint();
- variable.placeholder = getApiEndpoint();
+ variable.value = getApiEndpoint(page.params.region);
+ variable.placeholder = getApiEndpoint(page.params.region);
} else if (variable.value === '{projectId}') {
variable.value = page.params.project;
variable.placeholder = page.params.project;
diff --git a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/+page.svelte b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/+page.svelte
index adbf16091..9a3c98817 100644
--- a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/+page.svelte
@@ -7,19 +7,21 @@
import { Button } from '$lib/elements/forms';
import InstantRollbackDomain from './instantRollbackModal.svelte';
import { app } from '$lib/stores/app';
- import { sdk } from '$lib/stores/sdk';
+ import { realtime } from '$lib/stores/sdk';
import { invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
import { onMount } from 'svelte';
import { page } from '$app/state';
import { base } from '$app/paths';
+ import type { PageProps } from './$types';
import { regionalProtocol } from '$routes/(console)/project-[region]-[project]/store';
- export let data;
- let showRollback = false;
+ let { data }: PageProps = $props();
+
+ let showRollback = $state(false);
onMount(() => {
- return sdk.forConsole.realtime.subscribe('console', (response) => {
+ return realtime.forConsole(page.params.region, 'console', (response) => {
if (response.events.includes(`sites.${page.params.site}.deployments.*`)) {
invalidate(Dependencies.SITE);
}
diff --git a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/+layout.svelte b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/+layout.svelte
index 8ff3fe4e8..64a7454fa 100644
--- a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/+layout.svelte
+++ b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/+layout.svelte
@@ -1,7 +1,7 @@
diff --git a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/table.svelte b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/table.svelte
index 9bb700a5a..8f75512aa 100644
--- a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/table.svelte
@@ -1,5 +1,5 @@
-
-
+ {#snippet header(root)}
{#each $columns as { id, title }}
{title}
{/each}
-
- {#each data.deploymentList.deployments as deployment (deployment.$id)}
-
- {#each $columns as column}
-
- {#if column.id === '$id'}
- {#key column.id}
- {deployment.$id}
- {/key}
- {:else if column.id === 'status'}
- {@const status = deployment.status}
- {#if data?.activeDeployment?.$id === deployment?.$id}
-
- {:else}
-
+ {/snippet}
+
+ {#snippet children(root)}
+ {#each data.deploymentList.deployments as deployment (deployment.$id)}
+
+ {#each $columns as column}
+
+ {#if column.id === '$id'}
+ {#key column.id}
+ {deployment.$id}
+ {/key}
+ {:else if column.id === 'status'}
+ {@const status = deployment.status}
+ {#if data?.activeDeployment?.$id === deployment?.$id}
+
+ {:else}
+
+ {/if}
+ {:else if column.id === 'type'}
+
+ {:else if column.id === '$updatedAt'}
+
+ {:else if column.id === 'buildDuration'}
+ {#if ['waiting'].includes(deployment.status)}
+ -
+ {:else if ['processing', 'building'].includes(deployment.status)}
+
+ {:else}
+ {formatTimeDetailed(deployment.buildDuration)}
+ {/if}
+ {:else if column.id === 'totalSize'}
+ {calculateSize(deployment?.totalSize ?? 0)}
+ {:else if column.id === 'sourceSize'}
+ {calculateSize(deployment.sourceSize)}
+ {:else if column.id === 'buildSize'}
+ {calculateSize(deployment.buildSize)}
{/if}
- {:else if column.id === 'type'}
-
- {:else if column.id === '$updatedAt'}
-
- {:else if column.id === 'buildDuration'}
- {#if ['waiting'].includes(deployment.status)}
- -
- {:else if ['processing', 'building'].includes(deployment.status)}
-
- {:else}
- {formatTimeDetailed(deployment.buildDuration)}
- {/if}
- {:else if column.id === 'totalSize'}
- {calculateSize(deployment?.totalSize ?? 0)}
- {:else if column.id === 'sourceSize'}
- {calculateSize(deployment.sourceSize)}
- {:else if column.id === 'buildSize'}
- {calculateSize(deployment.buildSize)}
- {/if}
+
+ {/each}
+
+
+
+
- {/each}
-
-
-
-
-
-
- {/each}
-
+
+ {/each}
+ {/snippet}
+
+ {#snippet deleteContent(count)}
+ Are you sure you want to delete {count}
+ {count > 1 ? 'deployments' : 'deployment'} from your site -
+ {page.data.site.name} ?
+ {/snippet}
+
{#if selectedDeployment}
@@ -154,33 +151,3 @@
selectedDeploymentId={selectedDeployment.$id}
bind:show={showActivate} />
{/if}
-
-{#if selectedRows.length > 0}
-
-
-
-
- {selectedRows.length > 1 ? 'deployments' : 'deployment'}
- selected
-
-
-
- (selectedRows = [])}>Cancel
- (showBatchDeletion = true)}>Delete
-
-
-{/if}
-
-
-
- Are you sure you want to delete {selectedRows.length}
- {selectedRows.length > 1 ? 'deployments' : 'deployment'} from your site -
- {$site.name} ?
-
-
- This action is irreversible.
-
diff --git a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/+page.svelte b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/+page.svelte
index 7d710cb40..3ad6b4645 100644
--- a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/+page.svelte
@@ -4,16 +4,17 @@
import { Dependencies } from '$lib/constants';
import { Button } from '$lib/elements/forms';
import { Container, ResponsiveContainerHeader } from '$lib/layout';
- import { sdk } from '$lib/stores/sdk';
+ import { realtime } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import Table from './table.svelte';
import { Card, Empty } from '@appwrite.io/pink-svelte';
import { columns } from './store';
+ import { page } from '$app/state';
export let data;
onMount(() => {
- return sdk.forConsole.realtime.subscribe('console', (response) => {
+ return realtime.forConsole(page.params.region, 'console', (response) => {
if (response.events.includes('sites.*.executions.*')) {
invalidate(Dependencies.EXECUTIONS);
}
diff --git a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte
index 9d74a095d..da4890035 100644
--- a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte
+++ b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte
@@ -1,137 +1,105 @@
-
-
+
+ {#snippet header(root)}
{#each filteredColumns as { id, title }}
{title}
{/each}
-
- {#each logs.executions as log (log.$id)}
- {
- e.stopPropagation();
- openSheet = true;
- selectedLogId = log.$id;
- }}>
- {#each filteredColumns as column}
-
- {#if column.id === '$id'}
- {#key column.id}
- {log.$id}
- {/key}
- {:else if column.id === 'deploymentId'}
- {log.deploymentId}
- {:else if column.id === 'requestMethod'}
-
- {log.requestMethod}
-
- {:else if column.id === 'duration'}
- {#if ['processing', 'waiting'].includes(log.status)}
-
- {:else}
- {calculateTime(log.duration)}
+ {/snippet}
+
+ {#snippet children(root)}
+ {#each logs.executions as log (log.$id)}
+ {
+ e.stopPropagation();
+ openSheet = true;
+ selectedLogId = log.$id;
+ }}>
+ {#each filteredColumns as column}
+
+ {#if column.id === '$id'}
+ {#key column.id}
+ {log.$id}
+ {/key}
+ {:else if column.id === 'deploymentId'}
+ {log.deploymentId}
+ {:else if column.id === 'requestMethod'}
+
+ {log.requestMethod}
+
+ {:else if column.id === 'duration'}
+ {#if ['processing', 'waiting'].includes(log.status)}
+
+ {:else}
+ {calculateTime(log.duration)}
+ {/if}
+ {:else if column.id === 'responseStatusCode'}
+
+
+
+ {:else if column.id === 'requestPath'}
+
+ {log.requestPath}
+
+ {:else if column.id === '$createdAt'}
+
{/if}
- {:else if column.id === 'responseStatusCode'}
-
-
-
- {:else if column.id === 'requestPath'}
-
- {log.requestPath}
-
- {:else if column.id === '$createdAt'}
-
- {/if}
-
- {/each}
-
- {/each}
-
+
+ {/each}
+
+ {/each}
+ {/snippet}
+
-
-{#if selectedRows.length > 0}
-
-
-
-
- {selectedRows.length > 1 ? 'logs' : 'log'}
- selected
-
-
-
- (selectedRows = [])}>Cancel
- (showBatchDeletion = true)}>Delete
-
-
-{/if}
-
-
-
- Are you sure you want to delete {selectedRows.length}
- {selectedRows.length > 1 ? 'logs' : 'log'}?
-
-
- This action is irreversible.
-
diff --git a/src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte b/src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte
index a4ae7b082..8caf3c05a 100644
--- a/src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte
+++ b/src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte
@@ -3,28 +3,25 @@
import { base } from '$app/paths';
import { page } from '$app/state';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
- import { Avatar, Empty, EmptySearch, PaginationWithLimit, SearchQuery } from '$lib/components';
+ import {
+ Avatar,
+ type DeleteOperationState,
+ Empty,
+ EmptySearch,
+ MultiSelectionTable,
+ PaginationWithLimit,
+ SearchQuery
+ } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Badge } from '@appwrite.io/pink-svelte';
import { Button } from '$lib/elements/forms';
import { calculateSize } from '$lib/helpers/sizeConvertion';
import { Container } from '$lib/layout';
import { ImageFormat, type Models } from '@appwrite.io/console';
- import { addNotification } from '$lib/stores/notifications';
import { uploader } from '$lib/stores/uploader';
import { sdk } from '$lib/stores/sdk.js';
import DeleteFile from './deleteFile.svelte';
- import { bucket } from './store';
- import Confirm from '$lib/components/confirm.svelte';
- import {
- Layout,
- Table,
- Icon,
- Popover,
- ActionMenu,
- FloatingActionBar,
- Typography
- } from '@appwrite.io/pink-svelte';
+ import { Layout, Table, Icon, Popover, ActionMenu } from '@appwrite.io/pink-svelte';
import { onMount } from 'svelte';
import DualTimeView from '$lib/components/dualTimeView.svelte';
import {
@@ -34,23 +31,20 @@
IconTrash
} from '@appwrite.io/pink-icons-svelte';
import { isSmallViewport } from '$lib/stores/viewport';
+ import type { PageProps } from './$types';
- export let data;
+ const { data }: PageProps = $props();
- let showDelete = false;
- let selectedFile: Models.File = null;
- let selectedFiles: string[] = [];
- let showBulkDelete = false;
- let deleting = false;
-
- const bucketId = page.params.bucket;
+ let showDelete = $state(false);
+ let isUploading = $state(false);
+ let selectedFile: Models.File | null = $state(null);
function getPreview(fileId: string) {
return (
sdk
.forProject(page.params.region, page.params.project)
.storage.getFilePreview({
- bucketId,
+ bucketId: page.params.bucket,
fileId,
height: 128,
width: 128,
@@ -71,42 +65,25 @@
showDelete = true;
}
- async function handleBulkDelete() {
- showBulkDelete = false;
- deleting = true;
-
- const promises = selectedFiles.map((fileId) =>
- sdk.forProject(page.params.region, page.params.project).storage.deleteFile({
- bucketId,
+ async function handleBulkDelete(selectedRows: string[]): Promise {
+ const promises = selectedRows.map((fileId) => {
+ return sdk.forProject(page.params.region, page.params.project).storage.deleteFile({
+ bucketId: page.params.bucket,
fileId
- })
- );
+ });
+ });
try {
await Promise.all(promises);
- trackEvent(Submit.FileDelete, {
- total: selectedFiles.length
- });
- addNotification({
- type: 'success',
- message: `${selectedFiles.length} file${selectedFiles.length > 1 ? 's' : ''} deleted`
- });
- invalidate(Dependencies.FILES);
+ trackEvent(Submit.FileDelete, { total: selectedRows.length });
} catch (error) {
- addNotification({
- type: 'error',
- message: error.message
- });
trackError(error, Submit.FileDelete);
+ return error;
} finally {
- selectedFiles = [];
- showBulkDelete = false;
- deleting = false;
+ await invalidate(Dependencies.FILES);
}
}
- let isUploading = false;
-
const beforeunload = (event: BeforeUnloadEvent) => {
// legacy browser **may** support showing a custom message.
const message = 'An upload is in progress. Are you sure you want to leave?';
@@ -147,10 +124,10 @@
{#if data.files.total}
-
-
+ {#snippet header(root)}
Filename
Type
Size
Created
-
- {#each data.files.files as file}
- {#if file.chunksTotal / file.chunksUploaded !== 1}
-
-
-
-
- {file.name}
-
-
+ {/snippet}
+
+ {#snippet children(root)}
+ {#each data.files.files as file}
+ {#if file.chunksTotal / file.chunksUploaded !== 1}
+
+
+
+
+ {file.name}
+
+
+
+
+
+ {file.mimeType}
+
+ {calculateSize(file.sizeOriginal)}
+
+
+
+
+
+
+ {
+ event.preventDefault();
+ deleteFile(file);
+ }}>
+
+
-
-
- {file.mimeType}
-
- {calculateSize(file.sizeOriginal)}
-
-
-
-
-
-
- deleteFile(file)}>
-
-
-
-
-
- {:else}
- {@const href = `${base}/project-${page.params.region}-${page.params.project}/storage/bucket-${bucketId}/file-${file.$id}`}
-
-
-
-
- {file.mimeType}
-
- {calculateSize(file.sizeOriginal)}
-
-
-
-
-
-
- {
- e.stopPropagation();
- e.preventDefault();
- toggle();
- }}>
-
-
-
-
- Update
-
-
+
+ {:else}
+ {@const href = `${base}/project-${page.params.region}-${page.params.project}/storage/bucket-${page.params.bucket}/file-${file.$id}`}
+
+
+
+
+ {file.mimeType}
+
+ {calculateSize(file.sizeOriginal)}
+
+
+
+
+
+
+ {
e.stopPropagation();
e.preventDefault();
- selectedFile = file;
- showDelete = true;
+ toggle();
}}>
- Delete
-
-
-
-
-
- {/if}
- {/each}
-
+
+
+
+
+ Update
+
+ {
+ e.stopPropagation();
+ e.preventDefault();
+ selectedFile = file;
+ showDelete = true;
+ }}>
+ Delete
+
+
+
+
+
+ {/if}
+ {/each}
+ {/snippet}
+
+ {#snippet deleteContentNotice()}
+ This action is irreversible and will permanently remove the selected files.
+ {/snippet}
+
+ href={`${base}/project-${page.params.region}-${page.params.project}/storage/bucket-${page.params.bucket}`}>
Clear Search
@@ -268,39 +258,9 @@
allowCreate
on:click={() =>
goto(
- `${base}/project-${page.params.region}-${page.params.project}/storage/bucket-${bucketId}/create`
+ `${base}/project-${page.params.region}-${page.params.project}/storage/bucket-${page.params.bucket}/create`
)} />
{/if}
-
- {#if selectedFiles.length > 0}
-
-
-
-
- {selectedFiles.length > 1 ? 'files' : 'file'}
- selected
-
-
-
- (selectedFiles = [])}>Cancel
- (showBulkDelete = true)}>Delete
-
-
- {/if}
-
-
-
- Are you sure you want to delete {selectedFiles.length}
- {selectedFiles.length > 1 ? 'files' : 'file'} from {$bucket?.name} ?
-
-
- This action is irreversible and will permanently remove the selected files.
-
-
diff --git a/static/icons/dark/color/resend.svg b/static/icons/dark/color/resend.svg
index 4371f60b3..0ded97051 100644
--- a/static/icons/dark/color/resend.svg
+++ b/static/icons/dark/color/resend.svg
@@ -1,3 +1,3 @@
-
+
diff --git a/static/icons/light/color/resend.svg b/static/icons/light/color/resend.svg
index 67af6bf6d..ad9911369 100644
--- a/static/icons/light/color/resend.svg
+++ b/static/icons/light/color/resend.svg
@@ -1,3 +1,3 @@
-
+