Merge branch 'main' into 'supposed-improvements'.

This commit is contained in:
Darshan
2025-06-18 11:36:33 +05:30
31 changed files with 1078 additions and 915 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
PUBLIC_CONSOLE_MODE=self-hosted
PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import,attribute-encrypt
PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import,attribute-encrypt,index-lengths
PUBLIC_APPWRITE_MULTI_REGION=false
PUBLIC_APPWRITE_ENDPOINT=http://localhost/v1
PUBLIC_STRIPE_KEY=
+3 -3
View File
@@ -79,7 +79,7 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
build-args: |
"PUBLIC_CONSOLE_MODE=cloud"
"PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import,attribute-encrypt"
"PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import,attribute-encrypt,index-lengths"
"PUBLIC_APPWRITE_MULTI_REGION=true"
"PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}"
"PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY_STAGE }}"
@@ -118,7 +118,7 @@ jobs:
build-args: |
"PUBLIC_CONSOLE_MODE=self-hosted"
"PUBLIC_APPWRITE_MULTI_REGION=false"
"PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import,attribute-encrypt"
"PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import,attribute-encrypt,index-lengths"
"PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}"
publish-cloud-no-regions:
@@ -156,6 +156,6 @@ jobs:
build-args: |
"PUBLIC_CONSOLE_MODE=cloud"
"PUBLIC_APPWRITE_MULTI_REGION=false"
"PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import,attribute-encrypt"
"PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import,attribute-encrypt,index-lengths"
"PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY_STAGE }}"
"PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}"
+112 -106
View File
@@ -190,83 +190,107 @@
$currentPlan?.name.toLocaleLowerCase() !== selectedOrg?.tierName.toLocaleLowerCase()
? $currentPlan.name
: selectedOrg?.tierName; // fallback
$: derivedKey = `${selectedOrg?.$id}-${selectedProject?.$id}`;
</script>
<svelte:window on:resize={onResize} />
<div use:melt={$menubar}>
{#if !$isSmallViewport}
<span class="breadcrumb-separator">/</span>
<button
type="button"
class="trigger"
use:melt={$triggerOrganizations}
aria-label="Open organizations tab">
<span class="orgName">{selectedOrg?.name ?? 'Organization'}</span>
<span class="not-mobile"
>{#if correctPlanName}<Badge
variant="secondary"
content={correctPlanName} />{/if}</span>
<Icon icon={IconChevronDown} size="s" color="--fgcolor-neutral-secondary" />
</button>
{:else}
<button
type="button"
class="trigger"
on:click={() => {
organisationBottomSheetOpen = true;
}}
aria-label="Open organizations tab">
<span class="orgName" class:noProjects={!currentProject}
>{selectedOrg?.name ?? 'Organization'}</span>
<span class="not-mobile"
><Badge variant="secondary" content={correctPlanName ?? ''} /></span>
<Icon icon={IconChevronDown} size="s" color="--fgcolor-neutral-secondary" />
</button>
{/if}
<div class="menu" use:melt={$menuOrganizations}>
<Card.Base padding="xxxs" shadow={true}>
{#if selectedOrg}
<div use:melt={$itemOrganizations}>
<ActionMenu.Root>
<ActionMenu.Item.Anchor href={`${base}/organization-${selectedOrg?.$id}`}
>Organization overview</ActionMenu.Item.Anchor
></ActionMenu.Root>
</div>
{#if organizations.length > 1}
<div class="separator" use:melt={$separatorOrganizations}></div>
{#key derivedKey}
<div use:melt={$menubar}>
{#if !$isSmallViewport}
<span class="breadcrumb-separator">/</span>
<button
type="button"
class="trigger"
use:melt={$triggerOrganizations}
aria-label="Open organizations tab">
<span class="orgName">{selectedOrg?.name ?? 'Organization'}</span>
<span class="not-mobile"
>{#if correctPlanName}<Badge
variant="secondary"
content={correctPlanName} />{/if}</span>
<Icon icon={IconChevronDown} size="s" color="--fgcolor-neutral-secondary" />
</button>
{:else}
<button
type="button"
class="trigger"
on:click={() => {
organisationBottomSheetOpen = true;
}}
aria-label="Open organizations tab">
<span class="orgName" class:noProjects={!currentProject}
>{selectedOrg?.name ?? 'Organization'}</span>
<span class="not-mobile"
><Badge variant="secondary" content={correctPlanName ?? ''} /></span>
<Icon icon={IconChevronDown} size="s" color="--fgcolor-neutral-secondary" />
</button>
{/if}
<div use:melt={$subTriggerOrganizations}>
<div class="menu" use:melt={$menuOrganizations}>
<Card.Base padding="xxxs" shadow={true}>
{#if selectedOrg}
<div use:melt={$itemOrganizations}>
<ActionMenu.Root>
<ActionMenu.Item.Button trailingIcon={IconChevronRight}
>Switch organization</ActionMenu.Item.Button>
</ActionMenu.Root>
<ActionMenu.Item.Anchor
href={`${base}/organization-${selectedOrg?.$id}`}
>Organization overview</ActionMenu.Item.Anchor
></ActionMenu.Root>
</div>
<div class="menu subMenu" use:melt={$subMenuOrganizations}>
<Card.Base padding="xxxs" shadow={true}>
<div use:melt={$radioGroupOrganizations}>
{#each organizations as organization}
{#if organizations.length > 1}
<div class="separator" use:melt={$separatorOrganizations}></div>
<div use:melt={$subTriggerOrganizations}>
<ActionMenu.Root>
<ActionMenu.Item.Button trailingIcon={IconChevronRight}
>Switch organization</ActionMenu.Item.Button>
</ActionMenu.Root>
</div>
<div class="menu subMenu" use:melt={$subMenuOrganizations}>
<Card.Base padding="xxxs" shadow={true}>
<div use:melt={$radioGroupOrganizations}>
{#each organizations as organization}
<div use:melt={$itemOrganizations}>
<ActionMenu.Root>
<ActionMenu.Item.Anchor
href={`${base}/organization-${organization?.$id}`}
>{organization.name}</ActionMenu.Item.Anchor>
</ActionMenu.Root>
</div>
{/each}
<div class="separator" use:melt={$separatorOrganizations}></div>
<div use:melt={$itemOrganizations}>
<ActionMenu.Root>
<ActionMenu.Item.Anchor
href={`${base}/organization-${organization?.$id}`}
>{organization.name}</ActionMenu.Item.Anchor>
</ActionMenu.Root>
<ActionMenu.Item.Button
leadingIcon={IconPlusSm}
on:click={createOrg}
>Create organization</ActionMenu.Item.Button
></ActionMenu.Root>
</div>
{/each}
<div class="separator" use:melt={$separatorOrganizations}></div>
<div use:melt={$itemOrganizations}>
<ActionMenu.Root>
<ActionMenu.Item.Button
leadingIcon={IconPlusSm}
on:click={createOrg}
>Create organization</ActionMenu.Item.Button
></ActionMenu.Root>
</div>
</div>
</Card.Base>
</div>
</Card.Base>
</div>
{:else}
<div class="separator" use:melt={$separatorOrganizations}></div>
<div use:melt={$itemOrganizations}>
<ActionMenu.Root>
<ActionMenu.Item.Button
leadingIcon={IconPlusSm}
on:click={createOrg}>Create organization</ActionMenu.Item.Button
></ActionMenu.Root>
</div>
{/if}
{:else}
{#each organizations as organization}
<div use:melt={$itemOrganizations}>
<ActionMenu.Root>
<ActionMenu.Item.Anchor
href={`${base}/organization-${organization?.$id}`}
>{organization.name}</ActionMenu.Item.Anchor
></ActionMenu.Root>
</div>
{/each}
<div class="separator" use:melt={$separatorOrganizations}></div>
<div use:melt={$itemOrganizations}>
<ActionMenu.Root>
@@ -275,48 +299,30 @@
></ActionMenu.Root>
</div>
{/if}
{:else}
{#each organizations as organization}
<div use:melt={$itemOrganizations}>
<ActionMenu.Root>
<ActionMenu.Item.Anchor
href={`${base}/organization-${organization?.$id}`}
>{organization.name}</ActionMenu.Item.Anchor
></ActionMenu.Root>
</div>
{/each}
<div class="separator" use:melt={$separatorOrganizations}></div>
<div use:melt={$itemOrganizations}>
<ActionMenu.Root>
<ActionMenu.Item.Button leadingIcon={IconPlusSm} on:click={createOrg}
>Create organization</ActionMenu.Item.Button
></ActionMenu.Root>
</div>
{/if}
</Card.Base>
</div>
</Card.Base>
</div>
{#if selectedOrg && currentProject}
<span class="breadcrumb-separator">/</span>
{#if !$isSmallViewport}
<button
type="button"
class="trigger"
use:melt={$triggerProjects}
aria-label="Open projects tab">
<span class="projectName">{currentProject.name}</span>
<Icon icon={IconChevronDown} size="s" />
</button>
{:else}
<button
type="button"
class="trigger"
on:click={() => (projectsBottomSheetOpen = true)}
aria-label="Open projects tab">
<span class="projectName">{currentProject.name}</span>
<Icon icon={IconChevronDown} size="s" />
</button>
{/if}
{#if selectedOrg && currentProject}
<span class="breadcrumb-separator">/</span>
{#if !$isSmallViewport}
<button
type="button"
class="trigger"
use:melt={$triggerProjects}
aria-label="Open projects tab">
<span class="projectName">{currentProject.name}</span>
<Icon icon={IconChevronDown} size="s" />
</button>
{:else}
<button
type="button"
class="trigger"
on:click={() => (projectsBottomSheetOpen = true)}
aria-label="Open projects tab">
<span class="projectName">{currentProject.name}</span>
<Icon icon={IconChevronDown} size="s" />
</button>
{/if}
<div class="menu" use:melt={$menuProjects}>
<Card.Base padding="xxxs" shadow={true}>
+26 -21
View File
@@ -27,37 +27,42 @@
const importItems: Writable<ImportItemsMap> = writable(new Map());
async function showCompletionNotification(
databaseId: string,
collectionId: string,
importData: Payload
database: string,
collection: string,
payload: Payload
) {
await invalidate(Dependencies.DOCUMENTS);
const url = `${base}/project-${page.params.region}-${page.params.project}/databases/database-${databaseId}/collection-${collectionId}`;
const isSuccess = payload.status === 'completed';
const isError = !isSuccess && !!payload.errors;
// extract clean message from nested backend error.
const match = importData.errors.join('').match(/message: '(.*)' Message:/i);
const errorMessage = match?.[1];
if (!isSuccess && !isError) return;
const type = importData.status === 'completed' ? 'success' : 'error';
const message =
importData.status === 'completed'
? 'CSV import finished successfully.'
: `${errorMessage}`;
let errorMessage = 'Import failed. Check your CSV for correct fields and required values.';
if (isError && Array.isArray(payload.errors)) {
try {
// the `errors` is a list of json encoded string.
errorMessage = JSON.parse(payload.errors[0]).message;
} catch {
// do nothing, fallback to default message.
}
}
const type = isSuccess ? 'success' : 'error';
const message = isError ? errorMessage : 'CSV import finished successfully.';
const url = `${base}/project-${page.params.region}-${page.params.project}/databases/database-${database}/collection-${collection}`;
addNotification({
type,
message,
isHtml: true,
buttons:
collectionId === page.params.collection || type === 'error'
? undefined
: [
{
name: 'View documents',
method: () => goto(url)
}
]
isSuccess && collection !== page.params.collection
? [{ name: 'View documents', method: () => goto(url) }]
: undefined
});
if (isSuccess) {
await invalidate(Dependencies.DOCUMENTS);
}
}
async function updateOrAddItem(importData: Payload | Models.Migration) {
+13 -8
View File
@@ -23,7 +23,7 @@
import { page } from '$app/state';
import Card from '../card.svelte';
import SkeletonRepoList from './skeletonRepoList.svelte';
import { untrack } from 'svelte';
import { onMount, untrack } from 'svelte';
let {
action = $bindable('select'),
@@ -46,6 +46,11 @@
let search = $state('');
let selectedInstallation = $state(null);
let isLoadingRepositories = $state(null);
let installationsMap = $state(null);
onMount(() => {
loadInstallations();
});
async function loadInstallations() {
if (installationList) {
@@ -59,7 +64,7 @@
)
);
}
return installationList.installations;
installationsMap = installationList.installations;
} else {
const { installations } = await sdk
.forProject(page.params.region, page.params.project)
@@ -70,7 +75,7 @@
}
installation.set(installations.find((entry) => entry.$id === selectedInstallation));
}
return installations;
installationsMap = installations;
}
}
@@ -123,7 +128,7 @@
{#if hasInstallations}
<Layout.Stack>
<Layout.Stack gap="s">
{#await loadInstallations()}
{#if !installationsMap}
<Layout.Stack direction="row">
<InputSelect
disabled
@@ -137,12 +142,12 @@
value={null} />
<InputSearch placeholder="Search repositories" disabled />
</Layout.Stack>
{:then installations}
{:else}
<Layout.Stack direction={$isSmallViewport ? 'column' : 'row'}>
<InputSelect
id="installation"
options={[
...installations.map((entry) => {
...installationsMap.map((entry) => {
return {
label: entry.organization,
value: entry.$id
@@ -160,7 +165,7 @@
}
search = '';
installation.set(
installations.find((entry) => entry.$id === selectedInstallation)
installationsMap.find((entry) => entry.$id === selectedInstallation)
);
isLoadingRepositories = true;
@@ -174,7 +179,7 @@
bind:value={search}
disabled={!search && !$repositories?.repositories?.length} />
</Layout.Stack>
{/await}
{/if}
</Layout.Stack>
{#if selectedInstallation}
<!-- manual installation change -->
+2 -1
View File
@@ -21,5 +21,6 @@ function isFlagEnabled(name: string) {
export const flags = {
showSites: isFlagEnabled('sites'),
showCsvImport: isFlagEnabled('csv-import'),
showAttributeEncrypt: isFlagEnabled('attribute-encrypt')
showAttributeEncrypt: isFlagEnabled('attribute-encrypt'),
showIndexLengths: isFlagEnabled('index-lengths')
};
-1
View File
@@ -107,7 +107,6 @@ function createPreferences() {
}
);
},
getCustomCollectionColumns: (collectionId: string): Preferences['columns'] => {
return preferences?.collections?.[collectionId] ?? [];
},
+2 -1
View File
@@ -81,7 +81,8 @@ function createConsoleSdk(client: Client) {
billing: new Billing(client),
sources: new Sources(client),
sites: new Sites(client),
domains: new Domains(client)
domains: new Domains(client),
storage: new Storage(client)
};
}
@@ -63,7 +63,7 @@
let savedDefault = data.default;
let showEncrypt = flags.showAttributeEncrypt(page.data);
const showEncrypt = flags.showAttributeEncrypt(page.data);
function handleDefaultState(hideDefault: boolean) {
if (hideDefault) {
@@ -20,7 +20,6 @@
const databaseId = page.params.database;
const collectionId = page.params.collection;
const documentId = page.params.document;
const editing = true;
function initWork() {
const prohibitedKeys = [
@@ -124,7 +123,7 @@
<svelte:fragment slot="title">{label}</svelte:fragment>
<svelte:fragment slot="aside">
<AttributeItem {attribute} bind:formValues={$work} {label} {editing} />
<AttributeItem {attribute} bind:formValues={$work} {label} editing />
</svelte:fragment>
<svelte:fragment slot="actions">
@@ -1,24 +1,22 @@
<script lang="ts">
import { page } from '$app/state';
import { PaginationInline } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { Button, InputSelect } from '$lib/elements/forms';
import { preferences } from '$lib/stores/preferences';
import { sdk } from '$lib/stores/sdk';
import { Query, type Models } from '@appwrite.io/console';
import { onMount } from 'svelte';
import { doc } from '../store';
import { isRelationshipToMany } from './store';
import { IconPlus, IconX } from '@appwrite.io/pink-icons-svelte';
import { Icon, Layout, Typography } from '@appwrite.io/pink-svelte';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import InputSelect from '$lib/elements/forms/inputSelect.svelte';
export let id: string;
export let label: string;
export let editing = false;
export let value: string | string[];
export let attribute: Models.AttributeRelationship;
export let optionalText: string | undefined = undefined;
export let editing = false;
const databaseId = page.params.database;
@@ -28,6 +26,7 @@
let relatedList: string[] = [];
let singleRel: string;
let showInput = false;
let newItemValue: string = '';
let limit = 10;
let offset = 0;
@@ -39,6 +38,7 @@
singleRel = value as string;
}
}
documentList = await getDocuments();
if (editing && $doc?.[attribute.key]) {
@@ -59,20 +59,48 @@
});
async function getDocuments(search: string = null) {
if (search) {
const documents = await sdk
.forProject(page.params.region, page.params.project)
.databases.listDocuments(databaseId, attribute.relatedCollection, [
Query.startsWith('$id', search),
Query.orderDesc('')
]);
return documents;
const queries = search ? [Query.startsWith('$id', search), Query.orderDesc('')] : [];
return await sdk
.forProject(page.params.region, page.params.project)
.databases.listDocuments(databaseId, attribute.relatedCollection, queries);
}
function getAvailableOptions(excludeIndex?: number) {
return options?.filter((option) => {
const otherItems =
excludeIndex !== undefined
? relatedList.filter((_, idx) => idx !== excludeIndex)
: relatedList;
return !otherItems.includes(option.value);
});
}
function updateRelatedList() {
relatedList = relatedList;
value = relatedList;
}
function removeItem(index: number) {
if (relatedList.length === 1) {
relatedList[index] = '';
} else {
const documents = await sdk
.forProject(page.params.region, page.params.project)
.databases.listDocuments(databaseId, attribute.relatedCollection);
return documents;
relatedList.splice(index, 1);
}
updateRelatedList();
}
function addNewItem() {
if (newItemValue) {
relatedList = [...relatedList, newItemValue];
value = relatedList;
newItemValue = '';
showInput = false;
}
}
function cancelAddItem() {
newItemValue = '';
showInput = false;
}
//Reactive statements
@@ -84,106 +112,140 @@
.reverse()
.slice(offset, offset + limit)
: [];
$: total = relatedList?.length ?? 0;
$: totalCount = relatedList?.length ?? 0;
$: options =
documentList?.documents?.map((n) => {
const data = displayNames.filter((name) => name !== '$id').map((name) => n?.[name]);
return {
value: n.$id,
label: n.$id,
data
};
return { value: n.$id, label: n.$id, data };
}) ?? [];
$: hasItems = totalCount > 0;
$: showTopAddButton = !editing && totalCount === 0 && !showInput;
$: showBottomAddButton =
(!editing && hasItems && !showInput) ||
(editing && hasItems && relatedList.every((item) => item) && !showInput);
$: showEmptyInput = editing && totalCount === 0 && !showInput;
</script>
{#if isRelationshipToMany(attribute)}
<div class="u-width-full-line">
<Layout.Stack direction="row" alignContent="space-between">
<Layout.Stack gap="xxs" direction="row" alignItems="center">
<Typography.Text variant="m-500">{label}</Typography.Text>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
{optionalText}
</Typography.Text>
<Layout.Stack gap="xxl">
<Layout.Stack gap="m">
<Layout.Stack direction="row" alignContent="space-between">
<Layout.Stack gap="xxs" direction="row" alignItems="center">
<Typography.Text variant="m-500">{label}</Typography.Text>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
{optionalText}
</Typography.Text>
</Layout.Stack>
{#if showTopAddButton}
<Button secondary on:click={() => (showInput = true)}>
<Icon icon={IconPlus} slot="start" size="s" />
Add item
</Button>
{/if}
</Layout.Stack>
{#if editing || total === 0}
<Button
secondary
on:click={() => {
showInput = true;
}}>
<Icon icon={IconPlus} slot="start" size="s" />
Add item
</Button>
<Layout.Stack gap="m">
<!-- Empty input for editing mode when no items exist -->
{#if showEmptyInput}
<Layout.Stack direction="row">
<InputSelect
{id}
required
{options}
bind:value={relatedList[0]}
placeholder={`Select ${attribute.key}`}
on:change={() => {
if (!relatedList[0]) relatedList = [''];
updateRelatedList();
}} />
</Layout.Stack>
{/if}
<!-- Existing items in editing mode -->
{#if editing && hasItems}
{#each paginatedItems as _, paginatedIndex}
{@const actualIndex = offset + paginatedIndex}
<Layout.Stack direction="row">
<InputSelect
{id}
required
options={getAvailableOptions(actualIndex)}
bind:value={relatedList[actualIndex]}
placeholder={`Select ${attribute.key}`}
on:change={updateRelatedList} />
{#if relatedList[actualIndex]}
<div style:padding-block-start="0.5rem">
<Button
icon
extraCompact
on:click={() => removeItem(actualIndex)}>
<Icon icon={IconX} size="s" />
</Button>
</div>
{/if}
</Layout.Stack>
{/each}
{/if}
<!-- Existing items in creation mode -->
{#if !editing && hasItems}
{#each relatedList as item, i}
<Layout.Stack direction="row">
<InputSelect
{id}
required
options={getAvailableOptions(i)}
bind:value={item}
on:change={updateRelatedList} />
<div style:padding-block-start="0.5rem">
<Button
icon
extraCompact
ariaLabel={`Delete item ${i}`}
on:click={() => {
relatedList.splice(i, 1);
updateRelatedList();
}}>
<Icon icon={IconX} size="s" />
</Button>
</div>
</Layout.Stack>
{/each}
{/if}
<!-- Input for adding new items -->
{#if showInput}
<Layout.Stack direction="row">
<InputSelect
{id}
required
placeholder={`Select ${attribute.key}`}
bind:value={newItemValue}
options={getAvailableOptions()}
on:change={addNewItem} />
<div style:padding-block-start="0.5rem">
<Button icon extraCompact on:click={cancelAddItem}>
<Icon icon={IconX} size="s" />
</Button>
</div>
</Layout.Stack>
{/if}
</Layout.Stack>
{#if showBottomAddButton}
<Layout.Stack direction="row" alignContent="flex-start">
<Button extraCompact on:click={() => (showInput = true)}>
<Icon icon={IconPlus} slot="start" size="s" />
Add item
</Button>
</Layout.Stack>
{/if}
</Layout.Stack>
<ul class="u-flex-vertical u-gap-4 u-margin-block-start-4">
{#if !editing && relatedList?.length}
{#each relatedList as item, i}
<InputSelect {id} required bind:value={item} {options} />
<Button
extraCompact
ariaLabel={`Delete item ${i}`}
on:click={() => {
relatedList.splice(i, 1);
relatedList = relatedList;
value = relatedList;
}}>
<span class="icon-x" aria-hidden="true"></span>
</Button>
{/each}
{/if}
{#if showInput}
<InputSelect
{id}
label="Rel"
required
placeholder={`Select ${attribute.key}`}
bind:value={relatedList[total]}
options={options?.filter((n) => !relatedList.includes(n.value))}
on:change={() => {
value = relatedList;
showInput = false;
}} />
<Button extraCompact ariaLabel={`Hide input`} on:click={() => (showInput = false)}>
<span class="icon-x" aria-hidden="true"></span>
</Button>
{/if}
{#if paginatedItems && editing}
{#each paginatedItems as item, i}
<InputSelect {id} label="Rel" required bind:value={item} {options} />
<Button
extraCompact
ariaLabel={`Delete item ${i}`}
on:click={() => {
relatedList.splice(i, 1);
relatedList = relatedList;
value = relatedList;
}}>
<span class="icon-x" aria-hidden="true"></span>
</Button>
{/each}
{/if}
</ul>
{#if editing}
<div class="u-flex u-margin-block-start-32 u-main-space-between">
<p class="text">Total results: {total}</p>
<PaginationInline {limit} bind:offset {total} hidePages />
</div>
{/if}
{#if total > 0 && !editing}
<Button
extraCompact
disabled={showInput}
on:click={() => {
showInput = true;
}}>
<Icon icon={IconPlus} slot="start" size="s" />
Add item
</Button>
{/if}
</div>
</Layout.Stack>
{:else}
<InputSelect
{id}
@@ -14,6 +14,7 @@
import { type Attributes, collection, indexes } from '../store';
import { Icon, Layout } from '@appwrite.io/pink-svelte';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import { flags } from '$lib/flags';
export let showCreateIndex = false;
export let externalAttribute: Attributes = null;
@@ -38,6 +39,8 @@
let attributeList = [{ value: '', order: '', length: null }];
const showLengths = flags.showIndexLengths(page.data);
function generateIndexKey() {
let indexKeys = $indexes.map((index) => index.key);
@@ -149,7 +152,7 @@
placeholder="Select Order" />
<Layout.Stack direction="row" alignItems="flex-end" gap="xs">
{#if selectedType === IndexType.Key}
{#if selectedType === IndexType.Key && showLengths}
<InputNumber
id={`length-${i}`}
label={i === 0 ? 'Length' : undefined}
@@ -3,9 +3,13 @@
import { InputText } from '$lib/elements/forms';
import { Layout } from '@appwrite.io/pink-svelte';
import type { Models } from '@appwrite.io/console';
import { page } from '$app/state';
import { flags } from '$lib/flags';
export let showOverview = false;
export let selectedIndex: Models.Index = null;
const showLengths = flags.showIndexLengths(page.data);
</script>
<Modal title="Overview" bind:show={showOverview}>
@@ -39,12 +43,14 @@
id={`value-${selectedIndex.orders[i]}`}
value={selectedIndex.orders[i]}
readonly />
<InputText
required
label={i === 0 ? 'Length' : ''}
id={`value-${selectedIndex.lengths[i]}`}
value={selectedIndex.lengths[i]?.toString() ?? null}
readonly />
{#if showLengths}
<InputText
required
label={i === 0 ? 'Length' : ''}
id={`value-${selectedIndex.lengths[i]}`}
value={selectedIndex.lengths[i]?.toString() ?? null}
readonly />
{/if}
</Layout.Stack>
{/each}
{/if}
@@ -33,6 +33,7 @@
import { toLocaleDateTime } from '$lib/helpers/date';
import DualTimeView from '$lib/components/dualTimeView.svelte';
import { flags } from '$lib/flags';
export let data: PageData;
const databaseId = page.params.database;
@@ -169,7 +170,7 @@
let checked = false;
let showEncrypt = flags.showAttributeEncrypt(data);
const showEncrypt = flags.showAttributeEncrypt(data);
</script>
<Table.Root
@@ -205,85 +206,89 @@
{#each $columns as { id } (id)}
{@const attr = $attributes.find((n) => n.key === id)}
<Table.Cell column={id} {root}>
{#if isRelationship(attr)}
{@const args = displayNames?.[attr.relatedCollection] ?? ['$id']}
{#if !isRelationshipToMany(attr)}
{#if document[id]}
{@const related = document[id]}
<Link.Button
variant="muted"
on:click={(e) => {
e.preventDefault();
e.stopPropagation();
goto(
`${base}/project-${page.params.region}-${page.params.project}/databases/database-${databaseId}/collection-${attr.relatedCollection}/document-${related.$id}`
);
}}>
{#each args as arg, i}
{#if arg !== undefined}
{#if i}
&nbsp;|
{#if attr}
<Table.Cell column={id} {root}>
{#if isRelationship(attr)}
{@const args = displayNames?.[attr.relatedCollection] ?? ['$id']}
{#if !isRelationshipToMany(attr)}
{#if document[id]}
{@const related = document[id]}
<Link.Button
variant="muted"
on:click={(e) => {
e.preventDefault();
e.stopPropagation();
goto(
`${base}/project-${page.params.region}-${page.params.project}/databases/database-${databaseId}/collection-${attr.relatedCollection}/document-${related.$id}`
);
}}>
{#each args as arg, i}
{#if arg !== undefined}
{#if i}
&nbsp;|
{/if}
<span class="text" data-private>
{related?.[arg]}
</span>
{/if}
<span class="text" data-private>
{related?.[arg]}
</span>
{/if}
{/each}
</Link.Button>
{/each}
</Link.Button>
{:else}
<span class="text">n/a</span>
{/if}
{:else}
<span class="text">n/a</span>
{@const itemsNum = document[id]?.length}
<Button.Button
variant="extra-compact"
disabled={!itemsNum}
badge={itemsNum ?? 0}
on:click={(e) => {
e.stopPropagation();
e.preventDefault();
relationshipData = document[id];
showRelationships = true;
selectedRelationship = attr;
}}>
Items
</Button.Button>
{/if}
{:else}
{@const itemsNum = document[id]?.length}
<Button.Button
variant="extra-compact"
disabled={!itemsNum}
badge={itemsNum ?? 0}
on:click={(e) => {
e.stopPropagation();
e.preventDefault();
relationshipData = document[id];
showRelationships = true;
selectedRelationship = attr;
}}>
Items
</Button.Button>
{/if}
{:else}
{@const formatted = formatColumn(document[id])}
{@const isDatetimeAttribute = attr.type === 'datetime'}
{#if isDatetimeAttribute}
<DualTimeView time={formatted.whole}>
<span slot="title">Timestamp</span>
{toLocaleDateTime(formatted.whole, true, 'UTC')}
</DualTimeView>
{:else if isString(attr) && attr.encrypt && showEncrypt}
<button on:click={(e) => e.preventDefault()}>
<InteractiveText
copy={false}
variant="secret"
isVisible={false}
text={formatted.value} />
</button>
{:else if formatted.truncated}
<Tooltip placement="bottom" disabled={!formatted.truncated}>
{@const datetime = document[id]}
{@const formatted = formatColumn(document[id])}
{@const isDatetimeAttribute = attr.type === 'datetime'}
{@const isEncryptedAttribute = isString(attr) && attr.encrypt}
{#if isDatetimeAttribute}
<DualTimeView time={datetime}>
<span slot="title">Timestamp</span>
{toLocaleDateTime(datetime, true)}
</DualTimeView>
{:else if isEncryptedAttribute && showEncrypt}
<button on:click={(e) => e.preventDefault()}>
<InteractiveText
copy={false}
variant="secret"
isVisible={false}
text={formatted.value} />
</button>
{:else if formatted.truncated}
<Tooltip placement="bottom" disabled={!formatted.truncated}>
<Typography.Text truncate>{formatted.value}</Typography.Text>
<span
let:showing
slot="tooltip"
style:white-space="pre-wrap"
style:word-break="break-all">
{#if showing}
{formatted.whole}
{/if}
</span>
</Tooltip>
{:else}
<Typography.Text truncate>{formatted.value}</Typography.Text>
<span
let:showing
slot="tooltip"
style:white-space="pre-wrap"
style:word-break="break-all">
{#if showing}
{formatted.whole}
{/if}
</span>
</Tooltip>
{:else}
<Typography.Text truncate>{formatted.value}</Typography.Text>
{/if}
{/if}
{/if}
</Table.Cell>
</Table.Cell>
{/if}
{/each}
<Table.Cell column="$created" {root}>
<DualTimeView time={document.$createdAt} />
@@ -1,6 +1,6 @@
<script lang="ts">
import { Card, SvgIcon } from '$lib/components';
import { Icon, Layout, Typography } from '@appwrite.io/pink-svelte';
import { Icon, Layout, Skeleton, Typography } from '@appwrite.io/pink-svelte';
import { IconGithub, IconGitBranch } from '@appwrite.io/pink-icons-svelte';
import type { Models, Runtime } from '@appwrite.io/console';
@@ -10,6 +10,7 @@
export let rootDir: string = undefined;
export let showGitData = true;
export let runtimes: Models.RuntimeList;
export let loading = false;
$: selectedRuntime = runtimes?.runtimes.find((r) => r.$id === runtime);
</script>
@@ -18,7 +19,7 @@
<Layout.Stack gap="xl">
<slot />
<Layout.Stack gap="l">
{#if selectedRuntime?.name}
{#if selectedRuntime?.name && !loading}
<Layout.Stack gap="xxxs">
<Typography.Caption variant="400">Runtime</Typography.Caption>
<Layout.Stack gap="xxs" alignItems="center" direction="row">
@@ -30,6 +31,11 @@
</Typography.Text>
</Layout.Stack>
</Layout.Stack>
{:else if loading}
<Layout.Stack gap="xs">
<Skeleton variant="line" width={100} height={16} />
<Skeleton variant="line" width="100%" height={20} />
</Layout.Stack>
{/if}
{#if showGitData}
{#if repositoryName}
@@ -7,7 +7,7 @@
import { organization } from '$lib/stores/organization';
import { isCloud } from '$lib/system';
import { IconPencil } from '@appwrite.io/pink-icons-svelte';
import { Fieldset, Icon, Input, Layout, Tag } from '@appwrite.io/pink-svelte';
import { Fieldset, Icon, Input, Layout, Skeleton, Tag } from '@appwrite.io/pink-svelte';
import type { ComponentType } from 'svelte';
export let name: string;
@@ -25,6 +25,7 @@
value: string;
label: string;
}[] = [];
export let loading = false;
let showCustomId = false;
</script>
@@ -51,13 +52,20 @@
{/if}
</Layout.Stack>
{#key runtime}
<InputSelect
id="runtime"
label="Runtime"
placeholder="Select runtime"
bind:value={runtime}
required
{options} />
{#if loading}
<Layout.Stack gap="xs">
<Skeleton variant="line" width={100} height={20} />
<Skeleton variant="line" width="100%" height={32} />
</Layout.Stack>
{:else}
<InputSelect
id="runtime"
label="Runtime"
placeholder="Select runtime"
bind:value={runtime}
required
{options} />
{/if}
{/key}
{#if isCloud}
@@ -79,12 +87,19 @@
</Layout.Stack>
{/if}
{#if showEntrypoint}
<InputText
label="Entrypoint"
id="entrypoint"
bind:value={entrypoint}
required
placeholder="Enter entrypoint" />
{#if loading}
<Layout.Stack gap="xs">
<Skeleton variant="line" width={100} height={20} />
<Skeleton variant="line" width="100%" height={32} />
</Layout.Stack>
{:else}
<InputText
label="Entrypoint"
id="entrypoint"
bind:value={entrypoint}
required
placeholder="Enter entrypoint" />
{/if}
{/if}
</Layout.Stack>
</Fieldset>
@@ -10,7 +10,7 @@
import { installation, repository } from '$lib/stores/vcs';
import { Layout } from '@appwrite.io/pink-svelte';
import { writable } from 'svelte/store';
import { ID, Runtime, VCSDeploymentType } from '@appwrite.io/console';
import { ID, Runtime, VCSDeploymentType, VCSDetectionType } from '@appwrite.io/console';
import type { Models } from '@appwrite.io/console';
import { onMount } from 'svelte';
import Details from '../(components)/details.svelte';
@@ -57,12 +57,40 @@
let silentMode = false;
let specification = specificationOptions[0].value;
let detectingRuntime = true;
onMount(async () => {
installation.set(data.installation);
repository.set(data.repository);
name = data.repository.name;
await detectRuntime();
});
async function detectRuntime() {
try {
detectingRuntime = true;
const detections = (await sdk
.forProject(page.params.regionn, page.params.project)
.vcs.createRepositoryDetection(
data.installation.$id,
page.params.repository,
VCSDetectionType.Runtime
)) as unknown as Models.DetectionRuntime; /* SDK return type is wrong atm */
entrypoint = detections.entrypoint;
buildCommand = detections.commands;
runtime = detections.runtime as Runtime;
trackEvent(Submit.FrameworkDetect, { runtime, source: 'repository' });
} catch (error) {
trackError(error, Submit.FrameworkDetect);
} finally {
detectingRuntime = false;
}
}
async function create() {
try {
const func = await sdk
@@ -154,7 +182,8 @@
bind:specification
{specificationOptions}
options={runtimeOptions}
showEntrypoint />
showEntrypoint
loading={detectingRuntime} />
<ProductionBranchFieldset
bind:branch
@@ -173,7 +202,8 @@
runtimes={data.runtimesList}
repositoryName={data.repository.name}
{branch}
{rootDir} />
{rootDir}
loading={detectingRuntime} />
</svelte:fragment>
<svelte:fragment slot="footer">
@@ -3,17 +3,17 @@ import { sdk } from '$lib/stores/sdk';
export const load = async ({ parent, params, url }) => {
const { installations, runtimesList, specificationsList } = await parent();
const [repository] = await Promise.all([
sdk
.forProject(params.region, params.project)
.vcs.getRepository(url.searchParams.get('installation'), params.repository)
]);
const repository = await sdk
.forProject(params.region, params.project)
.vcs.getRepository(url.searchParams.get('installation'), params.repository);
const installation = installations.installations.find(
(installation) => installation.$id === url.searchParams.get('installation')
);
return {
installation,
installations,
installation: installations.installations.find(
(installation) => installation.$id === url.searchParams.get('installation')
),
repository,
runtimesList,
specificationsList
@@ -98,7 +98,7 @@
<div class="u-flex u-gap-16">
<div class="u-cross-child-center u-line-height-1-5">
<h6 class="u-bold">{$platform.name}</h6>
<p>{$platform.hostname}</p>
<p>{$platform.hostname || $platform.key}</p>
</div>
</div>
</Box>
@@ -91,11 +91,11 @@ const val APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.region, page.
});
</script>
<Wizard title="Add Android platform" bind:showExitModal confirmExit>
<Form onSubmit={createAndroidPlatform}>
<Layout.Stack gap="xxl">
<!-- Step One -->
{#if !isPlatformCreated}
<Wizard title="Add Android platform" bind:showExitModal confirmExit={!isPlatformCreated}>
<Layout.Stack gap="xxl">
<!-- Step One -->
{#if !isPlatformCreated}
<Form onSubmit={createAndroidPlatform}>
<Fieldset legend="Details">
<Layout.Stack gap="l" alignItems="flex-end">
<Layout.Stack gap="s">
@@ -136,59 +136,60 @@ const val APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.region, page.
</Button>
</Layout.Stack>
</Fieldset>
{:else}
<Layout.Stack gap="xxl">
<Card padding="s" radius="s">
<Layout.Stack
direction="row"
justifyContent="space-between"
alignItems="center"
gap="xs">
<Layout.Stack direction="row" alignItems="center" gap="s">
<Icon size="m" icon={IconAndroid} />
<Typography.Text variant="m-400" color="--fgcolor-neutral-primary">
{$createPlatform.name} ({$createPlatform.key})
</Typography.Text>
</Layout.Stack>
</Form>
{:else}
<Layout.Stack gap="xxl">
<Card padding="s" radius="s">
<Layout.Stack
direction="row"
justifyContent="space-between"
alignItems="center"
gap="xs">
<Layout.Stack direction="row" alignItems="center" gap="s">
<Icon size="m" icon={IconAndroid} />
<Typography.Text variant="m-400" color="--fgcolor-neutral-primary">
{$createPlatform.name} ({$createPlatform.key})
</Typography.Text>
</Layout.Stack>
</Card>
</Layout.Stack>
{/if}
<!-- Step Two -->
{#if isPlatformCreated}
<Fieldset legend="Clone starter">
<Layout.Stack gap="l">
<Typography.Text variant="m-500">
1. Clone the starter kit from GitHub using the terminal or Android
Studio.
</Typography.Text>
<!-- 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={gitCloneCode} />
</div>
<Typography.Text variant="m-500"
>2. Open the file <InlineCode
size="s"
code="data/repository/AppwriteRepository.kt" /> and update the configuration
settings.</Typography.Text>
<!-- Temporary fix: Remove this div once Code splitting issue with stack spacing is resolved -->
<div class="pink2-code-margin-fix">
<Code lang="kotlin" lineNumbers code={updateConfigCode} />
</div>
<Typography.Text variant="m-500"
>3. Run the app on a connected device or emulator, then click the <InlineCode
size="s"
code="Send a ping" /> button to verify the setup.</Typography.Text>
</Layout.Stack>
</Fieldset>
{/if}
</Layout.Stack>
</Form>
</Card>
</Layout.Stack>
{/if}
<!-- Step Two -->
{#if isPlatformCreated}
<Fieldset legend="Clone starter">
<Layout.Stack gap="l">
<Typography.Text variant="m-500">
1. If you're starting a new project, you can clone our starter kit from
GitHub using the terminal, VSCode or Android Studio.
</Typography.Text>
<!-- 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={gitCloneCode} />
</div>
<Typography.Text variant="m-500"
>2. Open the file <InlineCode
size="s"
code="data/repository/AppwriteRepository.kt" /> and update the configuration
settings.</Typography.Text>
<!-- Temporary fix: Remove this div once Code splitting issue with stack spacing is resolved -->
<div class="pink2-code-margin-fix">
<Code lang="kotlin" lineNumbers code={updateConfigCode} />
</div>
<Typography.Text variant="m-500"
>3. Run the app on a connected device or emulator, then click the <InlineCode
size="s"
code="Send a ping" /> button to verify the setup.</Typography.Text>
</Layout.Stack>
</Fieldset>
{/if}
</Layout.Stack>
<svelte:fragment slot="aside">
<Card padding="l" class="responsive-padding">
<Layout.Stack gap="xxl">
@@ -102,116 +102,121 @@ APPWRITE_PUBLIC_ENDPOINT: "${sdk.forProject(page.params.region, page.params.proj
});
</script>
<Wizard title="Add Apple platform" bind:showExitModal confirmExit>
<Form onSubmit={createApplePlatform}>
<Layout.Stack gap="xxl">
<!-- Step One -->
<Layout.Grid gap="l" rowGap="l" columns={4} columnsXS={2}>
{#each Object.entries(platforms) as [key, value]}
<Pink2Card.Selector
{value}
id={key}
title={key}
imageRadius="s"
name="framework"
bind:group={platform}
disabled={isCreatingPlatform || isPlatformCreated} />
{/each}
</Layout.Grid>
<Wizard title="Add Apple platform" bind:showExitModal confirmExit={!isPlatformCreated}>
<Layout.Stack gap="xxl">
<Form onSubmit={createApplePlatform}>
<Layout.Stack gap="xxl">
<!-- Step One -->
<Layout.Grid gap="l" rowGap="l" columns={4} columnsXS={2}>
{#each Object.entries(platforms) as [key, value]}
<Pink2Card.Selector
{value}
id={key}
title={key}
imageRadius="s"
name="framework"
bind:group={platform}
disabled={isCreatingPlatform || isPlatformCreated} />
{/each}
</Layout.Grid>
<!-- Step Two -->
{#if !isPlatformCreated}
<Fieldset legend="Details">
<Layout.Stack gap="l" alignItems="flex-end">
<Layout.Stack gap="s">
<InputText
id="name"
label="Name"
placeholder="My Apple App"
required
bind:value={$createPlatform.name} />
<!-- Step Two -->
{#if !isPlatformCreated}
<Fieldset legend="Details">
<Layout.Stack gap="l" alignItems="flex-end">
<Layout.Stack gap="s">
<InputText
id="name"
label="Name"
placeholder="My Apple App"
required
bind:value={$createPlatform.name} />
<!-- Tooltips on InputText don't work as of now -->
<InputText
id="hostname"
label="Bundle ID"
placeholder="com.company.appname"
required
bind:value={$createPlatform.key}>
<Tooltip slot="info" maxWidth="15rem">
<Icon icon={IconInfo} size="s" />
<Typography.Caption variant="400" slot="tooltip">
You can find your Bundle Identifier in the General tab for
your app's primary target in Xcode.
</Typography.Caption>
</Tooltip>
</InputText>
</Layout.Stack>
<Button
fullWidthMobile
size="s"
submit
forceShowLoader
submissionLoader={isCreatingPlatform}
disabled={!platform ||
!$createPlatform.name ||
!$createPlatform.key ||
isCreatingPlatform}>
Create platform
</Button>
</Layout.Stack>
</Fieldset>
{:else}
<Layout.Stack gap="xxl">
<Card padding="s" radius="s">
<Layout.Stack
direction="row"
justifyContent="space-between"
alignItems="center"
gap="xs">
<Layout.Stack direction="row" alignItems="center" gap="s">
<Icon size="m" icon={IconApple} />
<Typography.Text variant="m-400" color="--fgcolor-neutral-primary">
{$createPlatform.name} ({$createPlatform.key})
</Typography.Text>
<!-- Tooltips on InputText don't work as of now -->
<InputText
id="hostname"
label="Bundle ID"
placeholder="com.company.appname"
required
bind:value={$createPlatform.key}>
<Tooltip slot="info" maxWidth="15rem">
<Icon icon={IconInfo} size="s" />
<Typography.Caption variant="400" slot="tooltip">
You can find your Bundle Identifier in the General tab
for your app's primary target in Xcode.
</Typography.Caption>
</Tooltip>
</InputText>
</Layout.Stack>
</Layout.Stack>
</Card>
</Layout.Stack>
{/if}
<!-- Step Three -->
{#if isPlatformCreated}
<Fieldset legend="Clone starter">
<Layout.Stack gap="l">
<Typography.Text variant="m-500">
1. Clone the starter kit from GitHub using the terminal or XCode.
</Typography.Text>
<!-- 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={gitCloneCode} />
</div>
<Typography.Text variant="m-500"
>2. Open the file <InlineCode size="s" code="Sources/Config.plist" /> and
update the configuration settings.</Typography.Text>
<!-- Temporary fix: Remove this div once Code splitting issue with stack spacing is resolved -->
<div class="pink2-code-margin-fix">
<Code lang="plaintext" lineNumbers code={updateConfigCode} />
</div>
<Typography.Text variant="m-500"
>3. Run the app on a connected device or simulator, then click the <InlineCode
<Button
fullWidthMobile
size="s"
code="Send a ping" /> button to verify the setup.</Typography.Text>
submit
forceShowLoader
submissionLoader={isCreatingPlatform}
disabled={!platform ||
!$createPlatform.name ||
!$createPlatform.key ||
isCreatingPlatform}>
Create platform
</Button>
</Layout.Stack>
</Fieldset>
{:else}
<Layout.Stack gap="xxl">
<Card padding="s" radius="s">
<Layout.Stack
direction="row"
justifyContent="space-between"
alignItems="center"
gap="xs">
<Layout.Stack direction="row" alignItems="center" gap="s">
<Icon size="m" icon={IconApple} />
<Typography.Text
variant="m-400"
color="--fgcolor-neutral-primary">
{$createPlatform.name} ({$createPlatform.key})
</Typography.Text>
</Layout.Stack>
</Layout.Stack>
</Card>
</Layout.Stack>
</Fieldset>
{/if}
</Layout.Stack>
</Form>
{/if}
</Layout.Stack>
</Form>
<!-- Step Three -->
{#if isPlatformCreated}
<Fieldset legend="Clone starter">
<Layout.Stack gap="l">
<Typography.Text variant="m-500">
1. If you're starting a new project, you can clone our starter kit from
GitHub using the terminal or XCode.
</Typography.Text>
<!-- 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={gitCloneCode} />
</div>
<Typography.Text variant="m-500"
>2. Open the file <InlineCode size="s" code="Sources/Config.plist" /> and update
the configuration settings.</Typography.Text>
<!-- Temporary fix: Remove this div once Code splitting issue with stack spacing is resolved -->
<div class="pink2-code-margin-fix">
<Code lang="plaintext" lineNumbers code={updateConfigCode} />
</div>
<Typography.Text variant="m-500"
>3. Run the app on a connected device or simulator, then click the <InlineCode
size="s"
code="Send a ping" /> button to verify the setup.</Typography.Text>
</Layout.Stack>
</Fieldset>
{/if}
</Layout.Stack>
<svelte:fragment slot="aside">
<Card padding="l" class="responsive-padding">
<Layout.Stack gap="xxl">
@@ -161,133 +161,137 @@ static const String APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.reg
});
</script>
<Wizard title="Add Flutter platform" bind:showExitModal confirmExit>
<Form onSubmit={createFlutterPlatform}>
<Layout.Stack gap="xxl">
<!-- Step One -->
<Layout.Grid gap="l" rowGap="l" columns={3} columnsXS={2} columnsXXS={1}>
{#each Object.entries(platforms) as [key, value]}
<Pink2Card.Selector
{value}
id={key}
title={key}
imageRadius="s"
name="framework"
bind:group={platform}
disabled={isCreatingPlatform || isPlatformCreated} />
{/each}
</Layout.Grid>
<Wizard title="Add Flutter platform" bind:showExitModal confirmExit={!isPlatformCreated}>
<Layout.Stack gap="xxl">
<Form onSubmit={createFlutterPlatform}>
<Layout.Stack gap="xxl">
<!-- Step One -->
<Layout.Grid gap="l" rowGap="l" columns={3} columnsXS={2} columnsXXS={1}>
{#each Object.entries(platforms) as [key, value]}
<Pink2Card.Selector
{value}
id={key}
title={key}
imageRadius="s"
name="framework"
bind:group={platform}
disabled={isCreatingPlatform || isPlatformCreated} />
{/each}
</Layout.Grid>
<!-- Step Two -->
{#if !isPlatformCreated}
<Fieldset legend="Details">
<Layout.Stack gap="l" alignItems="flex-end">
<Layout.Stack gap="s">
<InputText
id="name"
label="Name"
placeholder={placeholder[platform].name}
required
bind:value={$createPlatform.name} />
<!-- Tooltips on InputText don't work as of now -->
{#if platform === PlatformType.Flutterweb}
<!-- Step Two -->
{#if !isPlatformCreated}
<Fieldset legend="Details">
<Layout.Stack gap="l" alignItems="flex-end">
<Layout.Stack gap="s">
<InputText
id="hostname"
label={hostname[platform]}
placeholder={placeholder[platform].hostname}
id="name"
label="Name"
placeholder={placeholder[platform].name}
required
bind:value={$createPlatform.hostname}>
<Tooltip slot="info" maxWidth="15rem">
<Icon icon={IconInfo} size="s" />
<Typography.Caption variant="400" slot="tooltip">
{placeholder[platform].tooltip}
</Typography.Caption>
</Tooltip>
</InputText>
{:else}
<InputText
id="key"
label={hostname[platform]}
placeholder={placeholder[platform].hostname}
required
bind:value={$createPlatform.key}>
<Tooltip slot="info" maxWidth="15rem">
<Icon icon={IconInfo} size="s" />
<Typography.Caption variant="400" slot="tooltip">
{placeholder[platform].tooltip}
</Typography.Caption>
</Tooltip>
</InputText>
{/if}
</Layout.Stack>
bind:value={$createPlatform.name} />
<Button
fullWidthMobile
size="s"
submit
forceShowLoader
submissionLoader={isCreatingPlatform}
disabled={!platform ||
!$createPlatform.name ||
(!$createPlatform.key && !$createPlatform.hostname) ||
isCreatingPlatform}>
Create platform
</Button>
</Layout.Stack>
</Fieldset>
{:else}
<Layout.Stack gap="xxl">
<Card padding="s" radius="s">
<Layout.Stack
direction="row"
justifyContent="space-between"
alignItems="center"
gap="xs">
<Layout.Stack direction="row" alignItems="center" gap="s">
<Icon size="m" icon={IconFlutter} />
<Typography.Text variant="m-400" color="--fgcolor-neutral-primary">
{$createPlatform.name} ({$createPlatform.key})
</Typography.Text>
<!-- Tooltips on InputText don't work as of now -->
{#if platform === PlatformType.Flutterweb}
<InputText
id="hostname"
label={hostname[platform]}
placeholder={placeholder[platform].hostname}
required
bind:value={$createPlatform.hostname}>
<Tooltip slot="info" maxWidth="15rem">
<Icon icon={IconInfo} size="s" />
<Typography.Caption variant="400" slot="tooltip">
{placeholder[platform].tooltip}
</Typography.Caption>
</Tooltip>
</InputText>
{:else}
<InputText
id="key"
label={hostname[platform]}
placeholder={placeholder[platform].hostname}
required
bind:value={$createPlatform.key}>
<Tooltip slot="info" maxWidth="15rem">
<Icon icon={IconInfo} size="s" />
<Typography.Caption variant="400" slot="tooltip">
{placeholder[platform].tooltip}
</Typography.Caption>
</Tooltip>
</InputText>
{/if}
</Layout.Stack>
<Button
fullWidthMobile
size="s"
submit
forceShowLoader
submissionLoader={isCreatingPlatform}
disabled={!platform ||
!$createPlatform.name ||
(!$createPlatform.key && !$createPlatform.hostname) ||
isCreatingPlatform}>
Create platform
</Button>
</Layout.Stack>
</Card>
</Layout.Stack>
{/if}
<!-- Step Three -->
{#if isPlatformCreated}
<Fieldset legend="Clone starter">
<Layout.Stack gap="l">
<Typography.Text variant="m-500">
1. Clone the starter kit from GitHub using the terminal, Android Studio
or VSCode.
</Typography.Text>
<!-- 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={gitCloneCode} />
</div>
<Typography.Text variant="m-500"
>2. Open the file <InlineCode
size="s"
code="lib/constants/appwrite.dart" /> and update the configuration settings.</Typography.Text>
<!-- Temporary fix: Remove this div once Code splitting issue with stack spacing is resolved -->
<div class="pink2-code-margin-fix">
<Code lang="dart" lineNumbers code={updateConfigCode} />
</div>
<Typography.Text variant="m-500"
>3. Run the app on a connected device or simulator, then click the <InlineCode
size="s"
code="Send a ping" /> button to verify the setup.</Typography.Text>
</Fieldset>
{:else}
<Layout.Stack gap="xxl">
<Card padding="s" radius="s">
<Layout.Stack
direction="row"
justifyContent="space-between"
alignItems="center"
gap="xs">
<Layout.Stack direction="row" alignItems="center" gap="s">
<Icon size="m" icon={IconFlutter} />
<Typography.Text
variant="m-400"
color="--fgcolor-neutral-primary">
{$createPlatform.name} ({$createPlatform.hostname ||
$createPlatform.key})
</Typography.Text>
</Layout.Stack>
</Layout.Stack>
</Card>
</Layout.Stack>
</Fieldset>
{/if}
</Layout.Stack>
</Form>
{/if}
</Layout.Stack>
</Form>
<!-- Step Three -->
{#if isPlatformCreated}
<Fieldset legend="Clone starter">
<Layout.Stack gap="l">
<Typography.Text variant="m-500">
1. If you're starting a new project, you can clone our starter kit from
GitHub using the terminal, VSCode or Android Studio.
</Typography.Text>
<!-- 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={gitCloneCode} />
</div>
<Typography.Text variant="m-500"
>2. Open the file <InlineCode size="s" code="lib/constants/appwrite.dart" />
and update the configuration settings.</Typography.Text>
<!-- Temporary fix: Remove this div once Code splitting issue with stack spacing is resolved -->
<div class="pink2-code-margin-fix">
<Code lang="dart" lineNumbers code={updateConfigCode} />
</div>
<Typography.Text variant="m-500"
>3. Run the app on a connected device or simulator, then click the <InlineCode
size="s"
code="Send a ping" /> button to verify the setup.</Typography.Text>
</Layout.Stack>
</Fieldset>
{/if}
</Layout.Stack>
<svelte:fragment slot="aside">
<Card padding="l" class="responsive-padding">
<Layout.Stack gap="xxl">
@@ -25,7 +25,6 @@
import ConnectionLine from './components/ConnectionLine.svelte';
import OnboardingPlatformCard from './components/OnboardingPlatformCard.svelte';
import { PlatformType } from '@appwrite.io/console';
import { isCloud } from '$lib/system';
let showExitModal = false;
let isPlatformCreated = false;
@@ -36,10 +35,8 @@
const gitCloneCode =
'\ngit clone https://github.com/appwrite/starter-for-react-native\ncd starter-for-react-native\n';
const updateConfigCode = isCloud
? `const APPWRITE_PROJECT_ID = "${projectId}";`
: `const APPWRITE_PROJECT_ID = "${projectId}";
const APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.region, page.params.project).client.config.endpoint}";
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}";
`;
export let platform: PlatformType = PlatformType.Reactnativeandroid;
@@ -129,116 +126,123 @@ const APPWRITE_PUBLIC_ENDPOINT = "${sdk.forProject(page.params.region, page.para
});
</script>
<Wizard title="Add React Native platform" bind:showExitModal confirmExit>
<Form onSubmit={createReactNativePlatform}>
<Layout.Stack gap="xxl">
<!-- Step One -->
<Layout.Stack gap="l" direction="row">
{#each Object.entries(platforms) as [key, value]}
<Pink2Card.Selector
{value}
id={key}
title={key}
imageRadius="s"
name="framework"
bind:group={platform}
disabled={isCreatingPlatform || isPlatformCreated} />
{/each}
</Layout.Stack>
<!-- Step Two -->
{#if !isPlatformCreated}
<Fieldset legend="Details">
<Layout.Stack gap="l" alignItems="flex-end">
<Layout.Stack gap="s">
<InputText
id="name"
label="Name"
placeholder={placeholder[platform].name}
required
bind:value={$createPlatform.name} />
<!-- Tooltips on InputText don't work as of now -->
<InputText
id="hostname"
label={hostname[platform]}
placeholder={placeholder[platform].hostname}
required
bind:value={$createPlatform.key}>
<Tooltip slot="info" maxWidth="15rem">
<Icon icon={IconInfo} size="s" />
<Typography.Caption variant="400" slot="tooltip">
{placeholder[platform].tooltip}
</Typography.Caption>
</Tooltip>
</InputText>
</Layout.Stack>
<Button
fullWidthMobile
size="s"
submit
forceShowLoader
submissionLoader={isCreatingPlatform}
disabled={!platform ||
!$createPlatform.name ||
!$createPlatform.key ||
isCreatingPlatform}>
Create platform
</Button>
</Layout.Stack>
</Fieldset>
{:else}
<Layout.Stack gap="xxl">
<Card padding="s" radius="s">
<Layout.Stack
direction="row"
justifyContent="space-between"
alignItems="center"
gap="xs">
<Layout.Stack direction="row" alignItems="center" gap="s">
<Icon size="m" icon={IconReact} />
<Typography.Text variant="m-400" color="--fgcolor-neutral-primary">
{$createPlatform.name} ({$createPlatform.key})
</Typography.Text>
</Layout.Stack>
</Layout.Stack>
</Card>
<Wizard title="Add React Native platform" bind:showExitModal confirmExit={!isPlatformCreated}>
<Layout.Stack gap="xxl">
<Form onSubmit={createReactNativePlatform}>
<Layout.Stack gap="xxl">
<!-- Step One -->
<Layout.Stack gap="l" direction="row">
{#each Object.entries(platforms) as [key, value]}
<Pink2Card.Selector
{value}
id={key}
title={key}
imageRadius="s"
name="framework"
bind:group={platform}
disabled={isCreatingPlatform || isPlatformCreated} />
{/each}
</Layout.Stack>
{/if}
<!-- Step Three -->
{#if isPlatformCreated}
<Fieldset legend="Clone starter">
<Layout.Stack gap="l">
<Typography.Text variant="m-500">
1. Clone the starter kit from GitHub using the terminal or VSCode.
</Typography.Text>
<!-- Step Two -->
{#if !isPlatformCreated}
<Fieldset legend="Details">
<Layout.Stack gap="l" alignItems="flex-end">
<Layout.Stack gap="s">
<InputText
id="name"
label="Name"
placeholder={placeholder[platform].name}
required
bind:value={$createPlatform.name} />
<!-- 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={gitCloneCode} />
</div>
<!-- Tooltips on InputText don't work as of now -->
<InputText
id="hostname"
label={hostname[platform]}
placeholder={placeholder[platform].hostname}
required
bind:value={$createPlatform.key}>
<Tooltip slot="info" maxWidth="15rem">
<Icon icon={IconInfo} size="s" />
<Typography.Caption variant="400" slot="tooltip">
{placeholder[platform].tooltip}
</Typography.Caption>
</Tooltip>
</InputText>
</Layout.Stack>
<Typography.Text variant="m-500"
>2. Open the file <InlineCode
<Button
fullWidthMobile
size="s"
code="lib/constants/appwrite.js" /> and update the configuration settings.</Typography.Text>
<!-- Temporary fix: Remove this div once Code splitting issue with stack spacing is resolved -->
<div class="pink2-code-margin-fix">
<Code lang="javascript" lineNumbers code={updateConfigCode} />
</div>
<Typography.Text variant="m-500"
>3. Run the app on a connected device or simulator, then click the <InlineCode
size="s"
code="Send a ping" /> button to verify the setup.</Typography.Text>
submit
forceShowLoader
submissionLoader={isCreatingPlatform}
disabled={!platform ||
!$createPlatform.name ||
!$createPlatform.key ||
isCreatingPlatform}>
Create platform
</Button>
</Layout.Stack>
</Fieldset>
{:else}
<Layout.Stack gap="xxl">
<Card padding="s" radius="s">
<Layout.Stack
direction="row"
justifyContent="space-between"
alignItems="center"
gap="xs">
<Layout.Stack direction="row" alignItems="center" gap="s">
<Icon size="m" icon={IconReact} />
<Typography.Text
variant="m-400"
color="--fgcolor-neutral-primary">
{$createPlatform.name} ({$createPlatform.key})
</Typography.Text>
</Layout.Stack>
</Layout.Stack>
</Card>
</Layout.Stack>
</Fieldset>
{/if}
</Layout.Stack>
</Form>
{/if}
</Layout.Stack>
</Form>
<!-- Step Three -->
{#if isPlatformCreated}
<Fieldset legend="Clone starter">
<Layout.Stack gap="l">
<Typography.Text variant="m-500">
1. If you're starting a new project, you can clone our starter kit from
GitHub using the terminal or VSCode.
</Typography.Text>
<!-- 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={gitCloneCode} />
</div>
<Typography.Text variant="m-500"
>2. Add your Appwrite credentials to <InlineCode
size="s"
code=".env.example" /> then rename it to <InlineCode
size="s"
code=".env" /> if needed.</Typography.Text>
<!-- 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} />
</div>
<Typography.Text variant="m-500"
>3. Run the app on a connected device or simulator, then click the <InlineCode
size="s"
code="Send a ping" /> button to verify the setup.</Typography.Text>
</Layout.Stack>
</Fieldset>
{/if}
</Layout.Stack>
<svelte:fragment slot="aside">
<Card padding="l" class="responsive-padding">
<Layout.Stack gap="xxl">
@@ -199,155 +199,150 @@ ${prefix}APPWRITE_ENDPOINT = "${sdk.forProject(page.params.region, page.params.p
});
</script>
<Wizard title="Add web platform" bind:showExitModal>
<Form onSubmit={createWebPlatform}>
<Layout.Stack gap="xxl">
<!-- Step One -->
{#if !isPlatformCreated || isChangingFramework}
<Fieldset legend="Type">
<Layout.Stack>
<div class="frameworks">
{#each frameworks as framework}
<Card.Selector
bind:group={selectedFrameworkKey}
name="framework"
id={framework.key}
value={framework.key}
title={framework.label}
icon={framework.icon}
imageRadius="s" />
{/each}
</div>
<Wizard title="Add web platform" bind:showExitModal confirmExit={!isPlatformCreated}>
<Layout.Stack gap="xxl">
<!-- Step One -->
{#if !isPlatformCreated || isChangingFramework}
<Form onSubmit={createWebPlatform}>
<Layout.Stack>
<Fieldset legend="Type">
<Layout.Stack>
<div class="frameworks">
{#each frameworks as framework}
<Card.Selector
bind:group={selectedFrameworkKey}
name="framework"
id={framework.key}
value={framework.key}
title={framework.label}
icon={framework.icon}
imageRadius="s" />
{/each}
</div>
<Layout.Stack direction="row" justifyContent="flex-end">
{#if isChangingFramework}
<Button
disabled={!selectedFramework}
on:click={() => (isChangingFramework = false)}>
Save</Button>
{/if}
</Layout.Stack>
</Layout.Stack>
</Fieldset>
{#if !isChangingFramework}
<Fieldset legend="Details">
<InputText
id="hostname"
label="Hostname"
placeholder="localhost"
error={hostnameError && 'Please enter a valid hostname'}
bind:value={hostname}>
<Tooltip slot="info">
<Icon icon={IconInfo} size="s" />
<span slot="tooltip">
The hostname that your website will use to interact with the
Appwrite APIs in production or development environments. No
protocol or port number required.
</span>
</Tooltip>
</InputText></Fieldset>
<Layout.Stack direction="row" justifyContent="flex-end">
{#if isChangingFramework}
<Button
disabled={!selectedFramework}
on:click={() => (isChangingFramework = false)}>
Save</Button>
{/if}
<Button submit disabled={!selectedFramework}>Create platform</Button>
</Layout.Stack>
{/if}
</Layout.Stack>
</Form>
{:else}
<Card.Base padding="s"
><Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
<Layout.Stack gap="xxs" direction="row">
<Icon icon={selectedFramework.smallIcon} />
<Typography.Text variant="m-500">{selectedFramework.label}</Typography.Text>
</Layout.Stack>
</Fieldset>
{#if !isChangingFramework}
<Fieldset legend="Details">
<InputText
id="hostname"
label="Hostname"
placeholder="localhost"
error={hostnameError && 'Please enter a valid hostname'}
bind:value={hostname}>
<Tooltip slot="info">
<Icon icon={IconInfo} size="s" />
<span slot="tooltip">
The hostname that your website will use to interact with the
Appwrite APIs in production or development environments. No
protocol or port number required.
</span>
</Tooltip>
</InputText></Fieldset>
<Layout.Stack direction="row" justifyContent="flex-end"
><Button submit disabled={!selectedFramework}>Create platform</Button
></Layout.Stack>
{/if}
{:else}
<Card.Base padding="s"
><Layout.Stack
direction="row"
justifyContent="space-between"
alignItems="center">
<Layout.Stack gap="xxs" direction="row">
<Icon icon={selectedFramework.smallIcon} />
<Typography.Text variant="m-500"
>{selectedFramework.label}</Typography.Text>
</Layout.Stack>
<Button
size="s"
secondary
on:click={() => {
isChangingFramework = true;
}}>Change</Button>
</Layout.Stack></Card.Base>
{/if}
<Button
size="s"
secondary
on:click={() => {
isChangingFramework = true;
}}>Change</Button>
</Layout.Stack></Card.Base>
{/if}
<!-- Step Three -->
{#if isPlatformCreated && !isChangingFramework}
<Fieldset legend="Clone starter">
<Layout.Stack gap="l">
<Typography.Text variant="m-500">
1. Clone the starter kit from GitHub using the terminal or VSCode.
</Typography.Text>
<!-- Step Three -->
{#if isPlatformCreated && !isChangingFramework}
<Fieldset legend="Clone starter">
<Layout.Stack gap="l">
<Typography.Text variant="m-500">
1. If you're starting a new project, you can clone our starter kit from
GitHub using the terminal or VSCode.
</Typography.Text>
<!-- 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={`\ngit clone https://github.com/appwrite/starter-for-${selectedFramework.key}\ncd starter-for-${selectedFramework.key}`} />
</div>
{#if selectedFramework.key === 'angular'}
<Typography.Text variant="m-500"
>2. Change <InlineCode
size="s"
code="src/environments/environment.ts" />
to reflect the values below:</Typography.Text>
{:else}
<Typography.Text variant="m-500"
>2. Rename <InlineCode size="s" code=".env.example" /> into <InlineCode
size="s"
code=".env" /> and update the values.</Typography.Text>
{/if}
<!-- 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} />
</div>
<!-- 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={`\ngit clone https://github.com/appwrite/starter-for-${selectedFramework.key}\ncd starter-for-${selectedFramework.key}`} />
</div>
{#if selectedFramework.key === 'angular'}
<Typography.Text variant="m-500"
>3. Install project dependencies</Typography.Text>
<!-- 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={'npm install'} />
</div>
<Typography.Text variant="m-500"
>4. Run the app, then click the <InlineCode
>2. Change <InlineCode
size="s"
code="Send a ping" /> button to verify the setup.</Typography.Text>
<!-- 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.runCommand} />
</div>
</Layout.Stack>
</Fieldset>
<Card.Base padding="s"
><Layout.Stack direction="row" justifyContent="space-between"
><Layout.Stack direction="row" alignItems="center">
<Icon
icon={IconInfo}
color="--fgcolor-neutral-tertiary" /><Typography.Text
variant="m-500"
color="--fgcolor-neutral-primary">
Demo app runs on http://localhost:{selectedFramework.portNumber}</Typography.Text
></Layout.Stack>
<Button
external
secondary
href={`http://localhost:${selectedFramework.portNumber}`}
><Layout.Stack direction="row" gap="xs"
>Open <Icon
icon={IconExternalLink}
color="--fgcolor-neutral-tertiary" /></Layout.Stack
></Button
></Layout.Stack
></Card.Base>
{/if}
</Layout.Stack>
</Form>
code="src/environments/environment.ts" />
to reflect the values below:</Typography.Text>
{:else}
<Typography.Text variant="m-500"
>2. Add your Appwrite credentials to <InlineCode
size="s"
code=".env.example" /> then rename it to <InlineCode
size="s"
code=".env" /> if needed.</Typography.Text>
{/if}
<!-- 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} />
</div>
<Typography.Text variant="m-500"
>3. Install project dependencies</Typography.Text>
<!-- 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={'npm install'} />
</div>
<Typography.Text variant="m-500"
>4. Run the app, then click the <InlineCode size="s" code="Send a ping" /> button
to verify the setup.</Typography.Text>
<!-- 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.runCommand} />
</div>
</Layout.Stack>
</Fieldset>
<Card.Base padding="s"
><Layout.Stack direction="row" justifyContent="space-between"
><Layout.Stack direction="row" alignItems="center">
<Icon icon={IconInfo} color="--fgcolor-neutral-tertiary" /><Typography.Text
variant="m-500"
color="--fgcolor-neutral-primary">
Demo app runs on http://localhost:{selectedFramework.portNumber}</Typography.Text
></Layout.Stack>
<Button
external
secondary
href={`http://localhost:${selectedFramework.portNumber}`}
><Layout.Stack direction="row" gap="xs"
>Open <Icon
icon={IconExternalLink}
color="--fgcolor-neutral-tertiary" /></Layout.Stack
></Button
></Layout.Stack
></Card.Base>
{/if}
</Layout.Stack>
<svelte:fragment slot="aside">
<Card.Base>
<Layout.Stack gap="xxl">
@@ -20,8 +20,17 @@
} from '@appwrite.io/pink-svelte';
import { Button } from '$lib/elements/forms';
export let migration: Models.Migration = null;
export let show = false;
export let migration: Models.Migration = null;
// convert to proper json objects from string!
migration.errors = migration.errors.map((err) => {
try {
return JSON.parse(err);
} catch {
return err;
}
});
type StatusCounters = {
[resource in 'Database' | 'Collection' | 'Function' | 'Users']?: StatusCounter;
@@ -55,7 +64,7 @@
};
let tab = 'details' as 'details' | 'logs';
let logs = JSON.stringify(migration, null, 2);
const logs = JSON.stringify(migration, null, 2);
</script>
<Modal
@@ -34,8 +34,14 @@
$: dbWrites = data.usage.databasesWrites;
$: legendData = [
{ name: 'Reads', value: data.usage.databasesReadsTotal },
{ name: 'Writes', value: data.usage.databasesWritesTotal }
{
name: 'Reads',
value: data.usage.databasesReads.reduce((sum, item) => sum + item.value, 0)
},
{
name: 'Writes',
value: data.usage.databasesWrites.reduce((sum, item) => sum + item.value, 0)
}
];
const tier = data?.currentInvoice?.plan ?? $organization?.billingPlan;
@@ -20,9 +20,8 @@
import { app } from '$lib/stores/app';
import { base } from '$app/paths';
import { isCloud } from '$lib/system';
import { getApiEndpoint } from '$lib/stores/sdk';
import { sdk } from '$lib/stores/sdk';
import { capitalize } from '$lib/helpers/string';
import { page } from '$app/state';
export let deployment: Models.Deployment;
export let proxyRuleList: Models.ProxyRuleList;
@@ -46,9 +45,7 @@
}
function getFilePreview(fileId: string) {
// TODO: @Meldiron use sdk.forConsole.storage.getFilePreview
const endpoint = getApiEndpoint(page.params.region);
return endpoint + `/storage/buckets/screenshots/files/${fileId}/view?project=console`;
return sdk.forConsole.storage.getFileView('screenshots', fileId);
}
</script>
@@ -17,8 +17,8 @@
let deployment = $state(data.deployment);
onMount(async () => {
sdk.forConsole.client.subscribe(
onMount(() => {
return sdk.forConsole.client.subscribe(
'console',
async (response: RealtimeResponseEvent<Models.Deployment>) => {
if (
@@ -62,7 +62,7 @@
.forProject(page.params.region, page.params.project)
.vcs.createRepositoryDetection(
$installation.$id,
data.repository.id,
page.params.repository,
VCSDetectionType.Framework,
rootDir
);
@@ -5,11 +5,14 @@ import { buildVerboseDomain } from '../../store';
export const load = async ({ parent, params, url }) => {
const { installations, frameworks, project, organization, regionalConsoleVariables } =
await parent();
const [repository] = await Promise.all([
sdk
.forProject(params.region, params.project)
.vcs.getRepository(url.searchParams.get('installation'), params.repository)
]);
const repository = await sdk
.forProject(params.region, params.project)
.vcs.getRepository(url.searchParams.get('installation'), params.repository);
const installation = installations.installations.find(
(installation) => installation.$id === url.searchParams.get('installation')
);
const domain = await buildVerboseDomain(
regionalConsoleVariables._APP_DOMAIN_SITES,
@@ -21,9 +24,7 @@ export const load = async ({ parent, params, url }) => {
return {
installations,
installation: installations.installations.find(
(installation) => installation.$id === url.searchParams.get('installation')
),
installation,
repository,
frameworks,
domain
@@ -7,7 +7,7 @@
import { generateSiteDeploymentDesc } from './store';
import { SvgIcon } from '$lib/components';
import { app } from '$lib/stores/app';
import { getApiEndpoint } from '$lib/stores/sdk';
import { sdk } from '$lib/stores/sdk';
import AddCollaboratorModal from './(components)/addCollaboratorModal.svelte';
import SitesActionMenu from './sitesActionMenu.svelte';
import { capitalize } from '$lib/helpers/string';
@@ -33,9 +33,7 @@
}
function getFilePreview(fileId: string) {
// TODO: @Meldiron use sdk.forConsole.storage.getFilePreview
const endpoint = getApiEndpoint(page.params.region);
return endpoint + `/storage/buckets/screenshots/files/${fileId}/view?project=console`;
return sdk.forConsole.storage.getFileView('screenshots', fileId);
}
</script>