Merge branch 'main' into 'refactor-terminologies'.

This commit is contained in:
Darshan
2025-07-01 13:52:27 +05:30
54 changed files with 279 additions and 185 deletions
+5
View File
@@ -11,7 +11,12 @@ test('upgrade - free tier', async ({ page }) => {
await page.waitForURL(/\/organization-[^/]+\/change-plan/);
await page.locator('input[value="tier-1"]').click();
await page.getByRole('button', { name: 'add' }).first().click();
await enterCreditCard(page);
// wait for a second after adding a card to update the UI.
await page.waitForSelector('button#method[role="combobox"]');
// skip members
await page.getByRole('button', { name: 'change plan' }).click();
await page.waitForURL(/\/console\/project-(?:[a-z0-9]+-)?([^/]+)\/get-started/);
@@ -21,7 +21,7 @@
try {
const card = await submitStripeCard(name, page?.params?.organization ?? null);
modal.closeModal();
invalidate(Dependencies.PAYMENT_METHODS);
await invalidate(Dependencies.PAYMENT_METHODS);
dispatch('submit', card);
addNotification({
type: 'success',
@@ -20,8 +20,19 @@
async function cardSaved(event: CustomEvent<PaymentMethodData>) {
value = event.detail.$id;
invalidate(Dependencies.UPGRADE_PLAN);
invalidate(Dependencies.CREATE_ORGANIZATION);
if (value) {
methods = {
...methods,
total: methods.total + 1,
paymentMethods: [...methods.paymentMethods, event.detail]
};
}
await Promise.all([
invalidate(Dependencies.UPGRADE_PLAN),
invalidate(Dependencies.ORGANIZATION)
]);
}
onMount(() => {
@@ -58,6 +58,7 @@
placeholder="Promo code"
id="code"
label="Add promo code"
autofocus
bind:value={coupon} />
<svelte:fragment slot="footer">
+2 -1
View File
@@ -101,7 +101,8 @@
async function createProjectsBottomSheet(organization: Organization): Promise<SheetMenu> {
isLoadingProjects = true;
loadedProjects = await projects;
// null on non-org/project path like `onboarding`.
loadedProjects = (await projects) ?? loadedProjects;
isLoadingProjects = false;
const createProjectItem = {
+40 -27
View File
@@ -31,49 +31,62 @@
}
};
onMount(async () => {
const saveColumnPreferences = () => {
const shownColumns = $columns.filter((n) => n.hide !== true).map((n) => n.id);
if (isCustomCollection) {
const prefs = preferences.getCustomTableColumns(page.params.table);
columns.set(
$columns.map((column) => {
column.hide = prefs?.includes(column.id) ?? false;
preferences.setCustomCollectionColumns(page.params.collection, shownColumns);
} else {
preferences.setColumns(shownColumns);
}
};
onMount(() => {
if (isCustomCollection) {
const shownColumns = preferences.getCustomCollectionColumns(page.params.collection);
columns.update((columns) => {
return columns.map((column) => {
column.hide = !shownColumns.includes(column.id);
return column;
})
);
});
});
} else {
const prefs = preferences.get(page.route);
// Override the shown columns only if a preference was set
if (prefs?.columns) {
columns.set(
$columns.map((column) => {
column.hide = prefs.columns?.includes(column.id) ?? false;
if (prefs?.columns && prefs.columns.length > 0) {
columns.update((cols) => {
return cols.map((column) => {
column.hide = !prefs.columns.includes(column.id);
return column;
})
);
});
});
}
}
columns.subscribe((ctx) => {
const columns = ctx.filter((n) => n.hide === true).map((n) => n.id);
if (isCustomCollection) {
preferences.setCustomCollectionColumns(columns);
} else {
preferences.setColumns(columns);
}
});
calcMaxHeight();
});
let selectedColumnsNumber = $derived(
$columns.reduce((acc, column) => {
if (column.hide === true) return acc;
if (column.hide) return acc;
return ++acc;
}, 0)
);
function toggleColumn(column: Column) {
columns.update((cols) =>
cols.map((col) => {
if (col.id === column.id) {
col.hide = !column.hide;
}
return col;
})
);
saveColumnPreferences();
}
</script>
<svelte:window on:resize={calcMaxHeight} />
@@ -86,7 +99,7 @@
{#each $columns as column}
{#if !column?.exclude}
<ActionMenu.Item.Button
on:click={() => (column.hide = !column.hide)}
on:click={() => toggleColumn(column)}
disabled={allowNoColumns
? false
: selectedColumnsNumber <= 1 && column.hide !== true}>
@@ -94,7 +107,7 @@
<Selector.Checkbox
checked={!column.hide}
size="s"
on:click={() => (column.hide = !column.hide)} />
on:click={() => toggleColumn(column)} />
{column.title}
</Layout.Stack>
</ActionMenu.Item.Button>
@@ -10,7 +10,12 @@
style="padding-block: 0.13rem">
{#each Array(11) as _, i}
<li>
<Tag size="m" selected={value === i} on:click={() => (value = i)}>
<Tag
size="m"
selected={value === i}
autofocus={i === 0}
data-rating={i}
on:click={() => (value = i)}>
{i}
</Tag>
</li>
@@ -6,6 +6,7 @@
<InputTextarea
required
id="feedback"
autofocus
bind:value={$feedbackData.message}
label="Tell us more about your experience"
placeholder="Share your suggestions and feature requests..." />
+17 -15
View File
@@ -51,7 +51,7 @@
import { base } from '$app/paths';
import { logout } from '$lib/helpers/logout';
import { app } from '$lib/stores/app';
import { isTabletViewport } from '$lib/stores/viewport';
import { isTabletViewport, isSmallViewport } from '$lib/stores/viewport';
import { isCloud } from '$lib/system.js';
import { user } from '$lib/stores/user';
import { Click, trackEvent } from '$lib/actions/analytics';
@@ -166,20 +166,22 @@
>Upgrade</Button.Anchor>
{/if}
<DropList show={$feedback.show} class="extended-width">
<Button.Button
type="button"
variant="compact"
on:click={() => {
toggleFeedback();
trackEvent(Click.FeedbackSubmitClick, { source: 'top_nav' });
}}
>Feedback
</Button.Button>
<svelte:fragment slot="other">
<Feedback />
</svelte:fragment>
</DropList>
{#if !$isSmallViewport}
<DropList show={$feedback.show} class="extended-width">
<Button.Button
type="button"
variant="compact"
on:click={() => {
toggleFeedback();
trackEvent(Click.FeedbackSubmitClick, { source: 'top_nav' });
}}
>Feedback
</Button.Button>
<svelte:fragment slot="other">
<Feedback />
</svelte:fragment>
</DropList>
{/if}
<DropList
noArrow
scrollable
+28 -39
View File
@@ -35,7 +35,7 @@
import MobileSupportModal from '$routes/(console)/wizard/support/mobileSupportModal.svelte';
import MobileFeedbackModal from '$routes/(console)/wizard/feedback/mobileFeedbackModal.svelte';
import { getSidebarState, updateSidebarState } from '$lib/helpers/sidebar';
import { isTabletViewport } from '$lib/stores/viewport';
import { isTabletViewport, isSmallViewport } from '$lib/stores/viewport';
import { Click, trackEvent } from '$lib/actions/analytics';
import type { HTMLAttributes } from 'svelte/elements';
@@ -255,7 +255,7 @@
</Tooltip>
</div>
</Layout.Stack>
{:else}
{:else if $isSmallViewport}
<div class="action-buttons">
<Layout.Stack direction="column" gap="s">
<DropList show={$feedback.show} scrollable>
@@ -282,7 +282,6 @@
trackEvent(Click.SupportOpenClick, { source: 'side_nav' });
}}>
<span>Support</span>
<svelte:fragment slot="other">
<MobileSupportModal bind:show={$showSupportModal}
></MobileSupportModal>
@@ -314,43 +313,33 @@
</div>
{/if}
{#if project}
<div class="only-mobile">
<div class="action-buttons">
<Layout.Stack direction="column" gap="s">
<DropList show={$feedback.show} scrollable>
<Button.Button
variant="secondary"
size="s"
on:click={() => {
toggleFeedback();
trackEvent('click_menu_feedback', { source: 'side_nav' });
}}
><span>Feedback</span>
</Button.Button>
<svelte:fragment slot="other">
<MobileFeedbackModal />
</svelte:fragment>
</DropList>
{#if project && $isSmallViewport}
<div class="action-buttons">
<Layout.Stack direction="column" gap="s">
<DropList show={$feedback.show} scrollable>
<Button.Button
variant="secondary"
size="s"
on:click={() => {
toggleFeedback();
trackEvent('click_menu_feedback', { source: 'side_nav' });
}}
><span>Feedback</span>
</Button.Button>
</DropList>
<DropList show={$showSupportModal} scrollable>
<Button.Button
variant="secondary"
size="s"
on:click={() => {
$showSupportModal = true;
trackEvent(Click.SupportOpenClick, { source: 'side_nav' });
}}>
<span>Support</span>
<svelte:fragment slot="other">
<MobileSupportModal bind:show={$showSupportModal}
></MobileSupportModal>
</svelte:fragment>
</Button.Button>
</DropList>
</Layout.Stack>
</div>
<DropList show={$showSupportModal} scrollable>
<Button.Button
variant="secondary"
size="s"
on:click={() => {
$showSupportModal = true;
trackEvent(Click.SupportOpenClick, { source: 'side_nav' });
}}>
<span>Support</span>
</Button.Button>
</DropList>
</Layout.Stack>
</div>
{/if}
</div>
+1
View File
@@ -34,6 +34,7 @@
label="Name"
placeholder="Project name"
required
autofocus
bind:value={projectName} />
{#if !showCustomId}
<div>
+4 -2
View File
@@ -50,7 +50,7 @@
if (confirmExit) {
showExitModal = true;
} else {
goto(href);
goBack();
trackEvent('wizard_exit', {
from: 'escape'
});
@@ -58,6 +58,8 @@
}
}
const goBack = () => goto(href);
onMount(() => ($isNewWizardStatusOpen = true));
onDestroy(() => ($isNewWizardStatusOpen = false));
@@ -77,7 +79,7 @@
if (confirmExit) {
showExitModal = true;
} else {
goto(href);
goBack();
trackEvent('wizard_exit', {
from: 'button'
});
+7 -8
View File
@@ -29,7 +29,7 @@ type PreferencesStore = {
} & { hideAiDisclaimer?: boolean };
async function updateConsolePreferences(store: PreferencesStore): Promise<void> {
const currentPreferences = get(user).prefs ?? (await sdk.forConsole.account.getPrefs());
const currentPreferences = get(user)?.prefs ?? (await sdk.forConsole.account.getPrefs());
if (!currentPreferences?.console || Array.isArray(currentPreferences.console)) {
currentPreferences.console = {};
}
@@ -78,9 +78,9 @@ function createPreferences() {
let newPrefsSnapshot: PreferencesStore;
update((currentPrefs) => {
oldPrefsSnapshot = currentPrefs;
oldPrefsSnapshot = structuredClone(currentPrefs);
callback(currentPrefs);
newPrefsSnapshot = currentPrefs;
newPrefsSnapshot = structuredClone(currentPrefs);
return currentPrefs;
});
@@ -148,16 +148,14 @@ function createPreferences() {
return n;
}),
setCustomCollectionColumns: (columns: Preferences['columns']) =>
setCustomCollectionColumns: (tableId: string, columns: Preferences['columns']) =>
updateAndSync((n) => {
const table = page.params.table;
if (!n?.tables?.[table]) {
if (!n?.tables?.[tableId]) {
n ??= {};
n.tables ??= {};
}
n.tables[table] = columns;
n.tables[tableId] = Array.from(new Set(columns));
return n;
}),
loadTeamPrefs: async (id: string) => {
@@ -189,6 +187,7 @@ function createPreferences() {
return n;
});
await sdk.forConsole.teams.updatePrefs(orgId, teamPrefs);
}
};
+13 -9
View File
@@ -316,15 +316,19 @@
$: checkForUsageLimits($organization);
$: projects = sdk.forConsole.projects.list([
Query.equal(
'teamId',
// id from page params ?? id from store ?? id from preferences
page.params.organization ?? currentOrganizationId ?? data.currentOrgId
),
Query.limit(5),
Query.orderDesc('$updatedAt')
]);
$: isOnOnboarding = page.route?.id?.includes('/(console)/onboarding');
$: projects = isOnOnboarding
? null
: sdk.forConsole.projects.list([
Query.equal(
'teamId',
// id from page params ?? id from store ?? id from preferences
page.params.organization ?? currentOrganizationId ?? data.currentOrgId
),
Query.limit(5),
Query.orderDesc('$updatedAt')
]);
$: if ($requestedMigration) {
openMigrationWizard();
@@ -186,6 +186,7 @@
bind:value={name}
label="Organization name"
placeholder="Enter organization name"
autofocus
id="name"
required />
</Fieldset>
@@ -5,8 +5,9 @@ import type { Coupon } from '$lib/sdk/billing';
import type { Organization } from '$lib/stores/organization';
export const load: PageLoad = async ({ url, parent, depends }) => {
depends(Dependencies.CREATE_ORGANIZATION);
const { organizations } = await parent();
depends(Dependencies.CREATE_ORGANIZATION);
const [coupon, paymentMethods] = await Promise.all([
getCoupon(url),
sdk.forConsole.billing.listPaymentMethods()
@@ -2,14 +2,14 @@
import { isCloud } from '$lib/system';
import { sdk } from '$lib/stores/sdk';
import { ID } from '@appwrite.io/console';
import { BillingPlan } from '$lib/constants';
import { BillingPlan, Dependencies } from '$lib/constants';
import { tierToPlan } from '$lib/stores/billing';
import { addNotification } from '$lib/stores/notifications';
import { loadAvailableRegions } from '$routes/(console)/regions';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { Button, Card, Layout, Input, Typography, Spinner } from '@appwrite.io/pink-svelte';
import { Form } from '$lib/elements/forms/index.js';
import { goto } from '$app/navigation';
import { goto, invalidate } from '$app/navigation';
import { base } from '$app/paths';
let isLoading = false;
@@ -47,6 +47,10 @@
if (organization) {
loadAvailableRegions(organization?.$id).then();
await goto(`${base}/organization-${organization.$id}`);
// fixes an edge case where
// the org is not available for some reason!
await invalidate(Dependencies.CREATE_ORGANIZATION);
}
isLoading = false;
}
@@ -66,6 +70,7 @@
<Input.Text
required
autofocus
disabled={isLoading}
label="Organization name"
bind:value={organizationName}
@@ -102,7 +102,7 @@
on:submit={createProject}>
<svelte:fragment slot="submit">
<Layout.Stack direction="row" justifyContent="flex-end">
<Button.Button autofocus type="submit" variant="primary" size="s">
<Button.Button type="submit" variant="primary" size="s">
Create
</Button.Button>
</Layout.Stack>
@@ -271,11 +271,7 @@
<title>Change plan - Appwrite</title>
</svelte:head>
<Wizard
title="Change plan"
href={`${base}/organization-${page.params.organization}`}
bind:showExitModal
confirmExit>
<Wizard title="Change plan" href={previousPage} bind:showExitModal confirmExit>
<Form bind:this={formComponent} onSubmit={handleSubmit} bind:isSubmitting>
<Layout.Stack gap="xxl">
<Fieldset legend="Select plan">
@@ -338,12 +334,12 @@
</Layout.Stack>
</Layout.Stack>
</Fieldset>
{:then paymentMethods}
{:then}
<Fieldset legend="Payment">
<SelectPaymentMethod
methods={paymentMethods}
bind:taxId
bind:value={paymentMethodId}
bind:taxId>
bind:methods={paymentMethods}>
<svelte:fragment slot="actions">
{#if !selectedCoupon?.code}
{#if paymentMethodId}
@@ -70,7 +70,7 @@
id="email"
label="Email"
placeholder="Enter email"
autofocus={true}
autofocus
bind:value={email} />
<InputText id="member-name" label="Name" placeholder="Enter name" bind:value={name} />
{#if isCloud}
@@ -50,6 +50,7 @@
name="domain"
bind:value={domainName}
required
autofocus
placeholder="example.com" />
<Divider />
@@ -84,7 +84,13 @@
DNS records map domain names to IP addresses or other resources.
</span>
<Layout.Stack gap="l">
<InputText id="name" label="Name" placeholder="subdomain" bind:value={name} required />
<InputText
id="name"
label="Name"
placeholder="subdomain"
bind:value={name}
required
autofocus />
<Layout.Stack gap="xs">
<InputSelect options={recordTypes} bind:value={type} id="type" label="Type" required />
<Input.Helper state="default">
@@ -73,7 +73,7 @@
<ConsoleButton
size="s"
event="create_user"
on:mousedown={() => newMemberModal.set(true)}
on:click={() => newMemberModal.set(true)}
disabled={isCloud ? !$currentPlan?.addons?.seats?.supported : false}>
<Icon size="s" icon={IconPlus} slot="start" />
<span class="text">Invite</span>
@@ -115,7 +115,7 @@
{ id: 'reads', hide: !databaseOperationMetric },
{ id: 'writes', hide: !databaseOperationMetric },
{ id: 'metric', hide: !!databaseOperationMetric },
{ id: 'costs' }
{ id: 'costs', hide: true }
]}
let:root>
<svelte:fragment slot="header" let:root>
@@ -52,7 +52,7 @@
</Button>
</Layout.Stack>
</div>
<Paginator items={members.memberships} hideFooter={members?.total <= 5}>
<Paginator items={members?.memberships} hideFooter={members?.total <= 5}>
{#snippet children(paginatedItems: typeof members.memberships)}
<Table.Root columns={2} let:root>
<svelte:fragment slot="header" let:root>
@@ -34,7 +34,9 @@ export const load: LayoutLoad = async ({ params, depends, parent }) => {
// fetch if not available in `plansInfo`
includedInBasePlans
? plansInfo.get(organization.billingPlan)
: sdk.forConsole.billing.getOrganizationPlan(organization.$id),
: isCloud
? sdk.forConsole.billing.getOrganizationPlan(organization.$id)
: null,
loadAvailableRegions(project.teamId)
]);
@@ -60,7 +60,7 @@
</Layout.Stack>
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
<ViewSelector view={View.Table} {columns} hideView />
<Button on:mousedown={() => ($showCreateUser = true)} event="create_user" size="s">
<Button on:click={() => ($showCreateUser = true)} event="create_user" size="s">
<Icon size="s" icon={IconPlus} slot="start" />
<span class="text">Create user</span>
</Button>
@@ -173,7 +173,7 @@
href="https://appwrite.io/docs/references/cloud/server-nodejs/users"
target="user"
allowCreate={$canWriteUsers}
on:mousedown={() => showCreateUser.set(true)} />
on:click={() => showCreateUser.set(true)} />
{/if}
</Container>
@@ -56,7 +56,7 @@
</script>
<Modal title="Create user" {error} bind:show={showCreate} onSubmit={create}>
<InputText id="name" label="Name" placeholder="Enter name" autofocus={true} bind:value={name} />
<InputText autofocus id="name" label="Name" placeholder="Enter name" bind:value={name} />
<InputEmail id="email" label="Email" placeholder="Enter email" bind:value={mail} />
<InputPhone id="phone" label="Phone" placeholder="Enter phone" bind:value={phone} />
<InputPassword id="password" label="Password" placeholder="Enter password" bind:value={pass} />
@@ -7,11 +7,13 @@
import { createTimeUnitPair } from '$lib/helpers/unit';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { project } from '../../store';
import { project as projectStore } from '../../store';
import { Layout } from '@appwrite.io/pink-svelte';
import { page } from '$app/state';
const { value, unit, baseValue, units } = createTimeUnitPair($project?.authDuration);
const options = units.map((v) => ({ label: v.name, value: v.name }));
const project = $derived($projectStore ?? page.data?.project);
const { value, unit, baseValue, units } = $derived(createTimeUnitPair(project?.authDuration));
const options = $derived(units.map((v) => ({ label: v.name, value: v.name })));
async function updateSessionLength() {
try {
@@ -43,7 +45,7 @@
</Layout.Stack>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={$baseValue === $project.authDuration} on:click={updateSessionLength}>
<Button disabled={$baseValue === project.authDuration} on:click={updateSessionLength}>
Update
</Button>
</svelte:fragment>
@@ -52,7 +52,7 @@
label="Email"
placeholder="Enter email"
required={true}
autofocus={true}
autofocus
bind:value={email} />
<InputText id="name" label="Name" placeholder="Enter name" bind:value={name} />
<Alert.Inline status="info">
@@ -40,7 +40,7 @@
id: column.key,
title: column.key,
type: column.type as ColumnType,
show: selected?.includes(column.key) ?? true,
hide: !selected?.includes(column.key),
array: column?.array,
format: 'format' in column && column?.format === 'enum' ? column.format : null,
elements: 'elements' in column ? column.elements : null
@@ -51,7 +51,7 @@
...['$id', '$createdAt', '$updatedAt'].map((id) => ({
id,
title: id,
show: true,
hide: false,
type: (id === '$id' ? 'string' : 'datetime') as ColumnType
}))
]);
@@ -95,11 +95,11 @@
<Layout.Stack direction="row" justifyContent="space-between">
<Filters
query={data.query}
columns={tableColumns}
columns={filterColumns}
disabled={!(hasColumns && hasValidColumns)}
analyticsSource="database_rows" />
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
<ViewSelector view={data.view} columns={tableColumns} hideView />
<ViewSelector view={data.view} columns={tableColumns} hideView isCustomCollection />
{#if flags.showCsvImport(data)}
<Button
secondary
@@ -11,19 +11,34 @@
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
import { isRelationship } from '../row-[row]/columns/store';
import Confirm from '$lib/components/confirm.svelte';
import { preferences } from '$lib/stores/preferences';
export let showDelete = false;
export let selectedColumn: Columns;
const databaseId = page.params.database;
let checked = false;
let error: string;
let checked = false;
async function updateTableColumns() {
const selectedColumns = preferences
.getCustomTableColumns($table.$id)
.filter((column) => column != selectedColumn.key);
// todo: change method name to table
await preferences.setCustomCollectionColumns($table.$id, selectedColumns);
await invalidate(Dependencies.TABLE);
}
async function handleDelete() {
try {
await sdk
.forProject(page.params.region, page.params.project)
.tables.deleteColumn(databaseId, $table.$id, selectedColumn.key);
await invalidate(Dependencies.TABLE);
await updateTableColumns();
showDelete = false;
addNotification({
type: 'success',
@@ -29,14 +29,18 @@
};
let error: string;
async function updateTableColumns() {
const selectedColumns = preferences.getCustomTableColumns(tableId);
selectedColumns.push(key ?? data?.key);
await preferences.setCustomCollectionColumns(tableId, selectedColumns);
await invalidate(Dependencies.COLLECTION);
}
async function submit() {
try {
await $option.create(databaseId, tableId, key, data);
await updateTableColumns();
let selectedColumns = preferences.getCustomTableColumns(tableId);
selectedColumns.push(key ?? data?.key);
preferences.setCustomCollectionColumns(selectedColumns);
await invalidate(Dependencies.TABLE);
if (!page.url.pathname.includes('columns')) {
await goto(
`${base}/project-${page.params.region}-${page.params.project}/databases/database-${databaseId}/table-${tableId}/columns`
@@ -104,7 +104,7 @@
id: column.key,
title: column.key,
type: column.type as ColumnType,
show: selected?.includes(column.key) ?? true,
hide: !selected?.includes(column.key),
array: column?.array,
width: { min: 168 },
format: 'format' in column && column?.format === 'enum' ? column.format : null,
@@ -9,6 +9,12 @@
$: count = data.executions;
$: gbHoursTotal = data.executionsMbSecondsTotal / 1000 / 3600;
$: mbSecondsCount = data.executionsMbSeconds;
$: gbHoursCount = data.executionsMbSeconds
?.map((metric) => ({
...metric,
value: metric.value / 1000 / 3600
}))
.filter(({ value }) => value);
</script>
<Container>
@@ -33,7 +39,7 @@
title: 'Total GB hours'
}}
total={gbHoursTotal}
count={mbSecondsCount} />
count={gbHoursCount} />
{/if}
</Layout.Stack>
</Container>
@@ -7,6 +7,7 @@ export const load: PageLoad = async ({ params }) => {
const period = isValueOfStringEnum(FunctionUsageRange, params.period)
? params.period
: FunctionUsageRange.ThirtyDays;
return sdk
.forProject(params.region, params.project)
.functions.getUsage(params.function, period);
@@ -106,6 +106,7 @@
id="subject"
label="Subject"
required
autofocus={true}
placeholder="Enter subject"
bind:value={subject}>
</InputText>
@@ -116,6 +116,7 @@
id="title"
label="Title"
required
autofocus
placeholder="Enter title"
bind:value={title}>
</InputText>
@@ -96,6 +96,7 @@
id="message"
label="Message"
required
autofocus
maxlength={900}
placeholder="Type here..."
bind:value={content}>
@@ -35,9 +35,8 @@
const gitCloneCode =
'\ngit clone https://github.com/appwrite/starter-for-react-native\ncd starter-for-react-native\n';
const updateConfigCode = `const EXPO_PUBLIC_APPWRITE_PROJECT_ID = "${projectId}";
const EXPO_PUBLIC_APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.project).client.config.endpoint}";
`;
const updateConfigCode = `EXPO_PUBLIC_APPWRITE_PROJECT_ID=${projectId}
EXPO_PUBLIC_APPWRITE_ENDPOINT=${sdk.forProject(page.params.region, page.params.project).client.config.endpoint}`;
export let platform: PlatformType = PlatformType.Reactnativeandroid;
@@ -232,7 +231,7 @@ const EXPO_PUBLIC_APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page
<!-- Temporary fix: Remove this div once Code splitting issue with stack spacing is resolved -->
<div class="pink2-code-margin-fix">
<Code lang="javascript" lineNumbers code={updateConfigCode} />
<Code lang="dotenv" lineNumbers code={updateConfigCode} />
</div>
<Typography.Text variant="m-500"
@@ -235,6 +235,7 @@ ${prefix}APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.p
id="hostname"
label="Hostname"
placeholder="localhost"
autofocus
error={hostnameError && 'Please enter a valid hostname'}
bind:value={hostname}>
<Tooltip slot="info">
@@ -302,7 +303,7 @@ ${prefix}APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.p
<!-- Temporary fix: Remove this div once Code splitting issue with stack spacing is resolved -->
<div class="pink2-code-margin-fix">
<Code lang="bash" lineNumbers code={selectedFramework.updateConfigCode} />
<Code lang="dotenv" lineNumbers code={selectedFramework.updateConfigCode} />
</div>
<Typography.Text variant="m-500"
@@ -2,7 +2,7 @@
import { goto, invalidate } from '$app/navigation';
import { base } from '$app/paths';
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
import { BoxAvatar, Confirm, CardGrid } from '$lib/components';
import { BoxAvatar, CardGrid, Modal } from '$lib/components';
import { Button, InputText } from '$lib/elements/forms';
import { toLocaleDateTime } from '$lib/helpers/date';
import { addNotification } from '$lib/stores/notifications';
@@ -11,7 +11,6 @@
import { project, projectRegion } from '../store';
import { organization } from '$lib/stores/organization';
import { Dependencies } from '$lib/constants';
let error: string;
let showDelete = false;
let name: string = null;
@@ -62,16 +61,12 @@
</svelte:fragment>
</CardGrid>
<Confirm
disabled={name !== $project.name}
onSubmit={handleDelete}
title="Delete project"
bind:open={showDelete}
bind:error>
<p>
<b>This project will be deleted</b>, along with all of its metadata, stats, and other
resources. <b>This action is irreversible</b>.
</p>
<Modal size="s" bind:show={showDelete} title="Delete project" onSubmit={handleDelete} bind:error>
<svelte:fragment slot="description">
This project will be deleted along with all of its metadata, stats, and other resources.
<b>This action is irreversible.</b>
</svelte:fragment>
<InputText
label={`Enter "${$project.name}" to continue`}
placeholder="Enter name"
@@ -79,4 +74,9 @@
autofocus
required
bind:value={name} />
</Confirm>
<svelte:fragment slot="footer">
<Button text on:click={() => (showDelete = false)}>Cancel</Button>
<Button submissionLoader submit disabled={name !== $project.name}>Delete</Button>
</svelte:fragment>
</Modal>
@@ -77,6 +77,7 @@
id="domain"
bind:value={domainName}
required
autofocus
placeholder="appwrite.example.com" />
</Form>
@@ -10,14 +10,7 @@
</script>
<Layout.Stack direction="row" alignItems="center" inline>
{#if ['processing'].includes(status)}
<Typography.Code color="--fgcolor-neutral-secondary">
<Layout.Stack direction="row" alignItems="center" inline>
0s
<Spinner size="s" />
</Layout.Stack>
</Typography.Code>
{:else if ['building'].includes(status)}
{#if ['processing', 'building'].includes(status)}
<Typography.Code color="--fgcolor-neutral-secondary">
<Layout.Stack direction="row" alignItems="center" inline>
<p use:timer={{ start: deployment.$createdAt }}></p>
@@ -3,6 +3,7 @@
import { humanFileSize } from '$lib/helpers/sizeConvertion';
import { formatTimeDetailed } from '$lib/helpers/timeConversion';
import type { Models } from '@appwrite.io/console';
import { page } from '$app/state';
import {
Badge,
Divider,
@@ -45,7 +46,7 @@
}
function getFilePreview(fileId: string) {
return sdk.forConsole.storage.getFileView('screenshots', fileId);
return sdk.forConsoleIn(page.params.region).storage.getFileView('screenshots', fileId);
}
</script>
@@ -33,7 +33,7 @@
}
function getFilePreview(fileId: string) {
return sdk.forConsole.storage.getFileView('screenshots', fileId);
return sdk.forConsoleIn(page.params.region).storage.getFileView('screenshots', fileId);
}
</script>
@@ -88,7 +88,7 @@
<Layout.Stack>
<Layout.Stack gap="s">
<InputText id="domain" placeholder="my-domain" bind:value={domain}>
<InputText id="domain" placeholder="my-domain" bind:value={domain} autofocus>
<svelte:fragment slot="end">
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
.{$regionalConsoleVariables._APP_DOMAIN_SITES}
@@ -44,10 +44,7 @@
hideColumns={!data.buckets.total}
hideView={!data.buckets.total} />
{#if $canWriteBuckets}
<Button
on:mousedown={() => ($showCreateBucket = true)}
event="create_bucket"
size="s">
<Button on:click={() => ($showCreateBucket = true)} event="create_bucket" size="s">
<Icon icon={IconPlus} slot="start" size="s" />
Create bucket
</Button>
@@ -169,7 +169,15 @@
</Table.Cell>
<Table.Cell column="actions" {root}>
<Popover let:toggle placement="bottom-start" padding="none">
<Button text icon ariaLabel="more options" on:click={toggle}>
<Button
text
icon
ariaLabel="more options"
on:click={(e) => {
e.stopPropagation();
e.preventDefault();
toggle();
}}>
<Icon icon={IconDotsHorizontal} size="s" />
</Button>
<ActionMenu.Root slot="tooltip">
@@ -178,7 +186,9 @@
</ActionMenu.Item.Anchor>
<ActionMenu.Item.Button
leadingIcon={IconTrash}
on:click={() => {
on:click={(e) => {
e.stopPropagation();
e.preventDefault();
selectedFile = file;
showDelete = true;
}}>
@@ -32,5 +32,5 @@
</script>
<Confirm {onSubmit} title="Delete file" bind:open={showDelete} bind:error>
Are you sure you want to delete <b>{file.name}</b>?
<p>Are you sure you want to delete <b>{file.name}</b>?</p>
</Confirm>
@@ -32,7 +32,11 @@
}
if (data?.couponData?.code) {
trackEvent(Submit.AccountCreate, { campaign_name: data?.couponData?.code });
trackEvent(Submit.AccountCreate, {
campaign_name: data?.couponData?.code,
email: mail,
name: $user?.name
});
await goto(`${base}/apply-credit?code=${data?.couponData?.code}`);
return;
}
@@ -32,7 +32,11 @@
await sdk.forConsole.account.create(ID.unique(), mail, pass, name ?? '');
await sdk.forConsole.account.createEmailPasswordSession(mail, pass);
trackEvent(Submit.AccountCreate, { campaign_name: data?.couponData?.code });
trackEvent(Submit.AccountCreate, {
campaign_name: data?.couponData?.code,
email: mail,
name: name
});
if (data?.couponData?.code) {
await goto(`${base}/apply-credit?code=${data?.couponData?.code}`);
@@ -13,7 +13,7 @@
import { sdk } from '$lib/stores/sdk';
import { Unauthenticated } from '$lib/layout';
import { Dependencies } from '$lib/constants';
import { trackEvent } from '$lib/actions/analytics';
import { Submit, trackEvent } from '$lib/actions/analytics';
import { onMount } from 'svelte';
import { page } from '$app/state';
import LoginLight from '$lib/images/login/login-light-mode.svg';
@@ -74,10 +74,16 @@
await sdk.forConsole.account.createEmailPasswordSession(mail, pass);
const prefs = await sdk.forConsole.account.getPrefs();
const newPrefs = { ...prefs, code };
await sdk.forConsole.account.updatePrefs(newPrefs);
await invalidate(Dependencies.ACCOUNT);
await Promise.all([
sdk.forConsole.account.updatePrefs(newPrefs),
invalidate(Dependencies.ACCOUNT)
]);
await goto(base);
trackEvent('submit_account_create', { code: code });
trackEvent(Submit.AccountCreate, {
email: mail,
name: name,
code: code
});
}
} catch (error) {
addNotification({
+1
View File
@@ -16,6 +16,7 @@ export const ssr = false;
export const load: LayoutLoad = async ({ depends, url, route }) => {
depends(Dependencies.ACCOUNT);
depends(Dependencies.CREATE_ORGANIZATION);
const [account, error] = (await sdk.forConsole.account
.get()