mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'main' into 'add-arrow-navs'.
This commit is contained in:
+4
-4
@@ -12,7 +12,7 @@
|
||||
"clean": "rm -rf node_modules && rm -rf .svelte_kit && pnpm i --force",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"format": "prettier --write .",
|
||||
"format": "prettier --write --cache .",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"test": "TZ=EST vitest run",
|
||||
"test:ui": "TZ=EST vitest --ui",
|
||||
@@ -22,11 +22,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/svelte": "^1.1.24",
|
||||
"@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@fe3277e",
|
||||
"@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2752",
|
||||
"@appwrite.io/pink-icons": "0.25.0",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@d94920e",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4472521",
|
||||
"@appwrite.io/pink-legacy": "^1.0.3",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d94920e",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4472521",
|
||||
"@faker-js/faker": "^9.9.0",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@sentry/sveltekit": "^8.38.0",
|
||||
|
||||
Generated
+15
-15
@@ -12,20 +12,20 @@ importers:
|
||||
specifier: ^1.1.24
|
||||
version: 1.1.24(svelte@5.25.3)(zod@3.24.3)
|
||||
'@appwrite.io/console':
|
||||
specifier: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@fe3277e
|
||||
version: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@fe3277e
|
||||
specifier: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2752
|
||||
version: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2752
|
||||
'@appwrite.io/pink-icons':
|
||||
specifier: 0.25.0
|
||||
version: 0.25.0
|
||||
'@appwrite.io/pink-icons-svelte':
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@d94920e
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@d94920e(svelte@5.25.3)
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4472521
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4472521(svelte@5.25.3)
|
||||
'@appwrite.io/pink-legacy':
|
||||
specifier: ^1.0.3
|
||||
version: 1.0.3
|
||||
'@appwrite.io/pink-svelte':
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d94920e
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d94920e(svelte@5.25.3)
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4472521
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4472521(svelte@5.25.3)
|
||||
'@faker-js/faker':
|
||||
specifier: ^9.9.0
|
||||
version: 9.9.0
|
||||
@@ -260,8 +260,8 @@ packages:
|
||||
'@analytics/type-utils@0.6.2':
|
||||
resolution: {integrity: sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg==}
|
||||
|
||||
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@fe3277e':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@fe3277e}
|
||||
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2752':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2752}
|
||||
version: 1.10.0
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@2.0.0-RC.1':
|
||||
@@ -269,8 +269,8 @@ packages:
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@d94920e':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@d94920e}
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4472521':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4472521}
|
||||
version: 2.0.0-RC.1
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
@@ -284,8 +284,8 @@ packages:
|
||||
'@appwrite.io/pink-legacy@1.0.3':
|
||||
resolution: {integrity: sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ==}
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d94920e':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d94920e}
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4472521':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4472521}
|
||||
version: 2.0.0-RC.2
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
@@ -3703,13 +3703,13 @@ snapshots:
|
||||
|
||||
'@analytics/type-utils@0.6.2': {}
|
||||
|
||||
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@fe3277e': {}
|
||||
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2752': {}
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@2.0.0-RC.1(svelte@5.25.3)':
|
||||
dependencies:
|
||||
svelte: 5.25.3
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@d94920e(svelte@5.25.3)':
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4472521(svelte@5.25.3)':
|
||||
dependencies:
|
||||
svelte: 5.25.3
|
||||
|
||||
@@ -3722,7 +3722,7 @@ snapshots:
|
||||
'@appwrite.io/pink-icons': 1.0.0
|
||||
the-new-css-reset: 1.11.3
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@d94920e(svelte@5.25.3)':
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4472521(svelte@5.25.3)':
|
||||
dependencies:
|
||||
'@appwrite.io/pink-icons-svelte': 2.0.0-RC.1(svelte@5.25.3)
|
||||
'@floating-ui/dom': 1.6.13
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
import { IconPlus, IconX } from '@appwrite.io/pink-icons-svelte';
|
||||
import type { PinkColumn } from '$lib/helpers/types';
|
||||
import { Card } from '$lib/components';
|
||||
import { TableScroll } from '$lib/elements/table';
|
||||
|
||||
export let withCreate = false;
|
||||
export let permissions: string[] = [];
|
||||
@@ -124,7 +125,7 @@
|
||||
</script>
|
||||
|
||||
{#if [...$groups]?.length}
|
||||
<div class="table-wrapper">
|
||||
<TableScroll>
|
||||
<Table.Root {columns} let:root>
|
||||
<svelte:fragment slot="header" let:root>
|
||||
<Table.Header.Cell column="role" {root}>Role</Table.Header.Cell>
|
||||
@@ -139,7 +140,7 @@
|
||||
{#each [...$groups] as [role, permission] (role)}
|
||||
<Table.Row.Base {root}>
|
||||
<Table.Cell column="role" {root}>
|
||||
<Row {role} />
|
||||
<Row {role} onNotFound={() => deleteRole(role)} />
|
||||
</Table.Cell>
|
||||
<Table.Cell column="create" {root}>
|
||||
<Selector.Checkbox
|
||||
@@ -174,7 +175,7 @@
|
||||
</Table.Row.Base>
|
||||
{/each}
|
||||
</Table.Root>
|
||||
</div>
|
||||
</TableScroll>
|
||||
|
||||
<div>
|
||||
<Actions
|
||||
@@ -210,14 +211,3 @@
|
||||
</Layout.Stack>
|
||||
</Card>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.table-wrapper {
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { type ComponentProps, type Snippet } from 'svelte';
|
||||
import { type ComponentProps, type Snippet, onMount } from 'svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { AvatarInitials } from '../';
|
||||
@@ -34,9 +34,10 @@
|
||||
role: string;
|
||||
placement?: ComponentProps<Popover>['placement'];
|
||||
children?: Snippet;
|
||||
onNotFound?: (role: string) => void;
|
||||
}
|
||||
|
||||
let { role, placement = 'bottom-start', children }: Props = $props();
|
||||
let { role, placement = 'bottom-start', children, onNotFound }: Props = $props();
|
||||
|
||||
type ParsedPermission = {
|
||||
type: 'user' | 'team' | 'other';
|
||||
@@ -111,6 +112,21 @@
|
||||
return fetchPromise;
|
||||
}
|
||||
|
||||
async function verifyExistence() {
|
||||
try {
|
||||
const data = await getData(role);
|
||||
if (data?.notFound) {
|
||||
onNotFound?.(role);
|
||||
}
|
||||
} catch {
|
||||
// Intentionally ignore fetch/parse errors; UI handles missing data state
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
verifyExistence();
|
||||
});
|
||||
|
||||
let isMouseOverTooltip = $state(false);
|
||||
function hidePopover(hideTooltip: () => void, timeout = true) {
|
||||
if (!timeout) {
|
||||
@@ -162,7 +178,7 @@
|
||||
{:then data}
|
||||
{formatName(
|
||||
data.name ?? data?.email ?? data?.phone ?? '-',
|
||||
$isSmallViewport ? 16 : 18
|
||||
$isSmallViewport ? 16 : 20
|
||||
)}
|
||||
{/await}
|
||||
</Typography.Text>
|
||||
@@ -281,20 +297,21 @@
|
||||
<Divider />
|
||||
<Layout.Stack gap="xxs" alignItems="flex-start">
|
||||
{#if data.email}
|
||||
<Typography.Text
|
||||
size="xs"
|
||||
variant="m-400"
|
||||
<Typography.Caption
|
||||
variant="400"
|
||||
color="--fgcolor-neutral-secondary">
|
||||
Email: {data.email}
|
||||
</Typography.Text>
|
||||
Email: {formatName(
|
||||
data.email,
|
||||
$isSmallViewport ? 24 : 32
|
||||
)}
|
||||
</Typography.Caption>
|
||||
{/if}
|
||||
{#if data.phone}
|
||||
<Typography.Text
|
||||
size="xs"
|
||||
variant="m-400"
|
||||
<Typography.Caption
|
||||
variant="400"
|
||||
color="--fgcolor-neutral-secondary">
|
||||
Phone: {data.phone}
|
||||
</Typography.Text>
|
||||
</Typography.Caption>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
|
||||
@@ -21,16 +21,17 @@
|
||||
}[];
|
||||
export let leadingIcon: ComponentType | undefined = undefined;
|
||||
|
||||
let element: HTMLSelectElement;
|
||||
let error: string;
|
||||
|
||||
const handleInvalid = (event: Event) => {
|
||||
event.preventDefault();
|
||||
const element = event.target as HTMLInputElement;
|
||||
|
||||
if (element.validity.valueMissing) {
|
||||
error = 'This field is required';
|
||||
return;
|
||||
}
|
||||
|
||||
error = element.validationMessage;
|
||||
};
|
||||
|
||||
@@ -38,12 +39,6 @@
|
||||
return typeof value === 'boolean' ? true : !!value;
|
||||
};
|
||||
|
||||
$: if (required && !isNotEmpty(value)) {
|
||||
element?.setCustomValidity('This field is required');
|
||||
} else {
|
||||
element?.setCustomValidity('');
|
||||
}
|
||||
|
||||
$: if (isNotEmpty(value)) {
|
||||
error = null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
/**
|
||||
* Checks if a build has exceeded the maximum build timeout duration
|
||||
*/
|
||||
function isBuildTimedOut(createdAt: string, status: string, timeoutSeconds: number): boolean {
|
||||
if (!['waiting', 'processing', 'building'].includes(status)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!timeoutSeconds || timeoutSeconds <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const created = new Date(createdAt);
|
||||
const elapsedSeconds = Math.floor((Date.now() - created.getTime()) / 1000);
|
||||
|
||||
return elapsedSeconds > timeoutSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the effective status for a build, considering timeout
|
||||
*/
|
||||
export function getEffectiveBuildStatus(
|
||||
originalStatus: string,
|
||||
createdAt: string,
|
||||
consoleVariables: Models.ConsoleVariables | undefined
|
||||
): string {
|
||||
const timeoutSeconds = getBuildTimeoutSeconds(consoleVariables);
|
||||
if (isBuildTimedOut(createdAt, originalStatus, timeoutSeconds)) {
|
||||
return 'failed';
|
||||
}
|
||||
return originalStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get timeout value from console variables
|
||||
*/
|
||||
function getBuildTimeoutSeconds(consoleVariables: Models.ConsoleVariables | undefined): number {
|
||||
if (!consoleVariables?._APP_COMPUTE_BUILD_TIMEOUT) {
|
||||
return 0;
|
||||
}
|
||||
const timeout = parseInt(String(consoleVariables._APP_COMPUTE_BUILD_TIMEOUT), 10);
|
||||
return isNaN(timeout) ? 0 : timeout;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 221 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 91 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 83 KiB |
@@ -76,7 +76,7 @@
|
||||
<Icon size="s" icon={IconCloud} />
|
||||
{/if}
|
||||
|
||||
{#if $version}
|
||||
{#if $version && !isCloud}
|
||||
<Link.Anchor
|
||||
size="s"
|
||||
variant="quiet"
|
||||
@@ -137,7 +137,7 @@
|
||||
</Link.Anchor>
|
||||
{/if}
|
||||
{#if $isSmallViewport}
|
||||
{#if $version}
|
||||
{#if $version && !isCloud}
|
||||
<span class="divider-wrapper">
|
||||
<Divider vertical />
|
||||
</span>
|
||||
@@ -151,15 +151,18 @@
|
||||
style="white-space: nowrap;">
|
||||
Version {$version}
|
||||
</Link.Anchor>
|
||||
{#if isCloud}
|
||||
<Icon size="s" icon={IconCloud} />
|
||||
<Badge
|
||||
size="xs"
|
||||
type="success"
|
||||
variant="secondary"
|
||||
content="Generally Available"
|
||||
style="white-space: nowrap;" />
|
||||
{/if}
|
||||
{/if}
|
||||
{#if isCloud}
|
||||
<span class="divider-wrapper">
|
||||
<Divider vertical />
|
||||
</span>
|
||||
<Icon size="s" icon={IconCloud} />
|
||||
<Badge
|
||||
size="xs"
|
||||
type="success"
|
||||
variant="secondary"
|
||||
content="Generally Available"
|
||||
style="white-space: nowrap;" />
|
||||
{/if}
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
@@ -111,7 +111,6 @@ if (!building) {
|
||||
scopedConsoleClient.setProject('console');
|
||||
clientConsole.setEndpoint(endpoint).setProject('console');
|
||||
|
||||
clientRealtime.setEndpoint(endpoint).setProject('console');
|
||||
clientProject.setEndpoint(endpoint).setMode('admin');
|
||||
clientRealtime.setEndpoint(endpoint).setProject('console');
|
||||
}
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import { isCloud } from '$lib/system';
|
||||
import { isSameDay } from '$lib/helpers/date';
|
||||
import { type BottomModalAlertItem, showBottomModalAlert } from '$lib/stores/bottom-alerts';
|
||||
import TransactionsApiDark from '$lib/images/promos/transactions-api-dark.png';
|
||||
import TransactionsApiLight from '$lib/images/promos/transactions-api-light.png';
|
||||
import DbOperatorsDark from '$lib/images/promos/db-operators-dark.png';
|
||||
import DbOperatorsLight from '$lib/images/promos/db-operators-light.png';
|
||||
|
||||
const listOfPromotions: BottomModalAlertItem[] = [];
|
||||
|
||||
if (isCloud) {
|
||||
const transactionsApiPromo: BottomModalAlertItem = {
|
||||
id: 'modal:transactions_api_announcement',
|
||||
const dbOperatorsPromo: BottomModalAlertItem = {
|
||||
id: 'modal:db_operators_announcement',
|
||||
src: {
|
||||
dark: TransactionsApiDark,
|
||||
light: TransactionsApiLight
|
||||
dark: DbOperatorsDark,
|
||||
light: DbOperatorsLight
|
||||
},
|
||||
title: 'Announcing Transactions API',
|
||||
message: 'Ensure data consistency across tables with atomic, all-or-nothing commits.',
|
||||
title: 'Announcing DB operators',
|
||||
message: 'Update multiple fields without fetching the entire row.',
|
||||
plan: 'free',
|
||||
importance: 8,
|
||||
scope: 'project',
|
||||
cta: {
|
||||
text: 'Read announcement',
|
||||
link: () => 'https://appwrite.io/blog/post/announcing-transactions-api',
|
||||
link: () => 'https://appwrite.io/blog/post/announcing-db-operators',
|
||||
external: true,
|
||||
hideOnClick: true
|
||||
},
|
||||
show: true
|
||||
};
|
||||
listOfPromotions.push(transactionsApiPromo);
|
||||
listOfPromotions.push(dbOperatorsPromo);
|
||||
}
|
||||
|
||||
export function addBottomModalAlerts() {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Card, Layout, Button } from '@appwrite.io/pink-svelte';
|
||||
import { Form } from '$lib/elements/forms';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { ID, Region } from '@appwrite.io/console';
|
||||
@@ -97,24 +98,22 @@
|
||||
class="u-only-dark"
|
||||
alt="Appwrite Logo" />
|
||||
<Card.Base variant="primary" padding="l">
|
||||
<CreateProject
|
||||
showTitle
|
||||
bind:projectName
|
||||
bind:id={projectId}
|
||||
bind:region={projectRegion}
|
||||
regions={$regionsStore?.regions}>
|
||||
{#snippet submit()}
|
||||
<Layout.Stack direction="row" justifyContent="flex-end">
|
||||
<Button.Button
|
||||
on:click={createProject}
|
||||
type="submit"
|
||||
variant="primary"
|
||||
size="s">
|
||||
Create
|
||||
</Button.Button>
|
||||
</Layout.Stack>
|
||||
{/snippet}
|
||||
</CreateProject>
|
||||
<Form noStyle onSubmit={createProject}>
|
||||
<CreateProject
|
||||
showTitle
|
||||
bind:projectName
|
||||
bind:id={projectId}
|
||||
bind:region={projectRegion}
|
||||
regions={$regionsStore?.regions}>
|
||||
{#snippet submit()}
|
||||
<Layout.Stack direction="row" justifyContent="flex-end">
|
||||
<Button.Button type="submit" variant="primary" size="s">
|
||||
Create
|
||||
</Button.Button>
|
||||
</Layout.Stack>
|
||||
{/snippet}
|
||||
</CreateProject>
|
||||
</Form>
|
||||
</Card.Base>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
+4
-23
@@ -36,6 +36,7 @@
|
||||
expandTabs,
|
||||
databaseRelatedRowSheetOptions,
|
||||
rowPermissionSheet,
|
||||
isWaterfallFromFaker,
|
||||
type Columns
|
||||
} from './store';
|
||||
import { addSubPanel, registerCommands, updateCommandGroupRanks } from '$lib/commandCenter';
|
||||
@@ -84,12 +85,6 @@
|
||||
let selectedOption: Option['name'] = 'String';
|
||||
let createMoreColumns = false;
|
||||
|
||||
/**
|
||||
* adding a lot of fake data will trigger the realtime below
|
||||
* and will keep invalidating the `Dependencies.TABLE` making a lot of API noise!
|
||||
*/
|
||||
let isWaterfallFromFaker = false;
|
||||
|
||||
let columnCreationHandler: ((response: RealtimeResponse) => void) | null = null;
|
||||
|
||||
onMount(() => {
|
||||
@@ -100,21 +95,9 @@
|
||||
response.events.includes('databases.*.tables.*.columns.*') ||
|
||||
response.events.includes('databases.*.tables.*.indexes.*')
|
||||
) {
|
||||
if (isWaterfallFromFaker) {
|
||||
if ($isWaterfallFromFaker) {
|
||||
columnCreationHandler?.(response);
|
||||
}
|
||||
|
||||
// don't invalidate when -
|
||||
// 1. from faker
|
||||
// 2. ai columns creation
|
||||
// 3. ai indexes creation
|
||||
if (
|
||||
!isWaterfallFromFaker &&
|
||||
!$showIndexesSuggestions &&
|
||||
!$tableColumnSuggestions.table
|
||||
) {
|
||||
invalidate(Dependencies.TABLE);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -316,7 +299,7 @@
|
||||
}
|
||||
|
||||
async function createFakeData() {
|
||||
isWaterfallFromFaker = true;
|
||||
isWaterfallFromFaker.set(true);
|
||||
|
||||
$spreadsheetLoading = true;
|
||||
$randomDataModalState.show = false;
|
||||
@@ -394,10 +377,8 @@
|
||||
$randomDataModalState.value = 25;
|
||||
}
|
||||
|
||||
/* api is too fast! */
|
||||
// await sleep(1250);
|
||||
$spreadsheetLoading = false;
|
||||
isWaterfallFromFaker = false;
|
||||
isWaterfallFromFaker.set(false);
|
||||
|
||||
spreadsheetRenderKey.set(hash(rowIds));
|
||||
}
|
||||
|
||||
+77
-26
@@ -26,16 +26,24 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Click, Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import { IconChevronDown, IconChevronUp, IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
import {
|
||||
IconChevronDown,
|
||||
IconChevronUp,
|
||||
IconPlus,
|
||||
IconRefresh
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import EmptySheet from './layout/emptySheet.svelte';
|
||||
import CreateRow from './rows/create.svelte';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { Empty as SuggestionsEmptySheet, tableColumnSuggestions } from '../(suggestions)';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let isRefreshing = false;
|
||||
let showImportCSV = false;
|
||||
|
||||
// todo: might need a type fix here.
|
||||
@@ -147,36 +155,66 @@
|
||||
<svelte:fragment slot="tooltip">Filters</svelte:fragment>
|
||||
</Tooltip>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<Button
|
||||
secondary
|
||||
event={Click.DatabaseImportCsv}
|
||||
disabled={!(hasColumns && hasValidColumns)}
|
||||
on:click={() => (showImportCSV = true)}>
|
||||
Import CSV
|
||||
</Button>
|
||||
{#if !$isSmallViewport}
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="flex-end"
|
||||
style="padding-right: 40px;">
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<Button
|
||||
secondary
|
||||
event="create_row"
|
||||
event={Click.DatabaseImportCsv}
|
||||
disabled={!(hasColumns && hasValidColumns)}
|
||||
on:click={() => ($showRowCreateSheet.show = true)}>
|
||||
<Icon icon={IconPlus} slot="start" size="s" />
|
||||
Create row
|
||||
on:click={() => (showImportCSV = true)}>
|
||||
Import CSV
|
||||
</Button>
|
||||
{#if !$isSmallViewport}
|
||||
<Button
|
||||
secondary
|
||||
event="create_row"
|
||||
disabled={!(hasColumns && hasValidColumns)}
|
||||
on:click={() => ($showRowCreateSheet.show = true)}>
|
||||
<Icon icon={IconPlus} slot="start" size="s" />
|
||||
Create row
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
icon
|
||||
size="s"
|
||||
secondary
|
||||
class="small-button-dimensions"
|
||||
on:click={() => {
|
||||
$expandTabs = !$expandTabs;
|
||||
preferences.setKey('tableHeaderExpanded', $expandTabs);
|
||||
}}>
|
||||
<Icon icon={!$expandTabs ? IconChevronDown : IconChevronUp} size="s" />
|
||||
</Button>
|
||||
{/if}
|
||||
<Button
|
||||
icon
|
||||
size="s"
|
||||
secondary
|
||||
class="small-button-dimensions"
|
||||
on:click={() => {
|
||||
$expandTabs = !$expandTabs;
|
||||
preferences.setKey('tableHeaderExpanded', $expandTabs);
|
||||
}}>
|
||||
<Icon
|
||||
icon={!$expandTabs ? IconChevronDown : IconChevronUp}
|
||||
size="s" />
|
||||
</Button>
|
||||
|
||||
<Tooltip disabled={isRefreshing || !data.rows.total} placement="top">
|
||||
<Button
|
||||
icon
|
||||
size="s"
|
||||
secondary
|
||||
disabled={isRefreshing ||
|
||||
!data.rows.total ||
|
||||
!(hasColumns && hasValidColumns)}
|
||||
class="small-button-dimensions"
|
||||
on:click={async () => {
|
||||
isRefreshing = true;
|
||||
await invalidate(Dependencies.TABLE);
|
||||
isRefreshing = false;
|
||||
}}>
|
||||
<div style:line-height="0px" class:rotating={isRefreshing}>
|
||||
<Icon icon={IconRefresh} size="s" />
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
<svelte:fragment slot="tooltip">Refresh</svelte:fragment>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
{#if $isSmallViewport}
|
||||
@@ -282,4 +320,17 @@
|
||||
width: 32px !important;
|
||||
height: 32px !important;
|
||||
}
|
||||
|
||||
:global(.rotating) {
|
||||
animation: rotate 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+14
@@ -23,6 +23,7 @@
|
||||
type ColumnsWidth,
|
||||
indexes,
|
||||
isCsvImportInProgress,
|
||||
isWaterfallFromFaker,
|
||||
reorderItems,
|
||||
showCreateIndexSheet
|
||||
} from '../store';
|
||||
@@ -55,6 +56,9 @@
|
||||
import { page } from '$app/state';
|
||||
import { debounce } from '$lib/helpers/debounce';
|
||||
import type { PageData } from './$types';
|
||||
import { realtime } from '$lib/stores/sdk';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
|
||||
const {
|
||||
data
|
||||
@@ -140,6 +144,16 @@
|
||||
onMount(() => {
|
||||
columnsOrder = preferences.getColumnOrder(tableId);
|
||||
columnsWidth = preferences.getColumnWidths(tableId + '#columns');
|
||||
|
||||
return realtime.forProject(page.params.region, ['project', 'console'], async (response) => {
|
||||
if (
|
||||
response.events.includes('databases.*.tables.*.columns.*.delete') ||
|
||||
(response.events.includes('databases.*.tables.*.columns.*.update') &&
|
||||
!$isWaterfallFromFaker)
|
||||
) {
|
||||
await invalidate(Dependencies.TABLE);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function getColumnStatusBadge(status: string): ComponentProps<Badge>['type'] {
|
||||
|
||||
-3
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { table } from '../store';
|
||||
@@ -9,7 +8,6 @@
|
||||
import { isRelationship } from '../rows/store';
|
||||
import Confirm from '$lib/components/confirm.svelte';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
let {
|
||||
@@ -59,7 +57,6 @@
|
||||
: `${selectedColumns.length} columns have been deleted`
|
||||
});
|
||||
|
||||
await invalidate(Dependencies.TABLE);
|
||||
showDelete = false;
|
||||
selectedColumn = Array.isArray(selectedColumn) ? [] : null;
|
||||
} catch (e) {
|
||||
|
||||
+9
@@ -37,6 +37,9 @@
|
||||
import { showCreateColumnSheet } from '../store';
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import { page } from '$app/state';
|
||||
import { realtime } from '$lib/stores/sdk';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
|
||||
let {
|
||||
data
|
||||
@@ -96,6 +99,12 @@
|
||||
|
||||
onMount(() => {
|
||||
columnsWidth = preferences.getColumnWidths(tableId + '#indexes');
|
||||
|
||||
return realtime.forProject(page.params.region, ['project', 'console'], (response) => {
|
||||
if (response.events.includes('databases.*.tables.*.indexes.*')) {
|
||||
invalidate(Dependencies.TABLE);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function getColumnStatusBadge(status: string): ComponentProps<Badge>['type'] {
|
||||
|
||||
+5
-2
@@ -104,9 +104,12 @@
|
||||
};
|
||||
|
||||
$: rows = writable(data.rows);
|
||||
$: if (rows) {
|
||||
$: if ($rows) {
|
||||
paginatedRows.clear();
|
||||
paginatedRows.setPage(1, $rows.rows);
|
||||
|
||||
/* reset ui when the underlying data changes */
|
||||
spreadsheetRenderKey.set(hash(Date.now().toString()));
|
||||
}
|
||||
|
||||
// create index map for O(1) row lookups, reactive!
|
||||
@@ -478,7 +481,7 @@
|
||||
message: 'Column deleted'
|
||||
});
|
||||
|
||||
invalidate(Dependencies.TABLE);
|
||||
await invalidate(Dependencies.TABLE);
|
||||
|
||||
$databaseColumnSheetOptions.column = null;
|
||||
} catch (error) {
|
||||
|
||||
+6
@@ -28,6 +28,12 @@ export const table = derived(page, ($page) => $page.data.table as Table);
|
||||
export const columns = derived(page, ($page) => $page.data.table.columns as Columns[]);
|
||||
export const indexes = derived(page, ($page) => $page.data.table.indexes as Models.ColumnIndex[]);
|
||||
|
||||
/**
|
||||
* adding a lot of fake data will trigger the realtime below
|
||||
* and will keep invalidating the `Dependencies.TABLE` making a lot of API noise!
|
||||
*/
|
||||
export const isWaterfallFromFaker = writable(false);
|
||||
|
||||
export const tableColumns = writable<Column[]>([]);
|
||||
|
||||
export const isCsvImportInProgress = writable(false);
|
||||
|
||||
+7
-2
@@ -15,6 +15,8 @@
|
||||
import { DeploymentSource, DeploymentCreatedBy, DeploymentDomains } from '$lib/components/git';
|
||||
import { func } from '../store';
|
||||
import { capitalize } from '$lib/helpers/string';
|
||||
import { getEffectiveBuildStatus } from '$lib/helpers/buildTimeout';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { IconInfo } from '@appwrite.io/pink-icons-svelte';
|
||||
import Link from '$lib/elements/link.svelte';
|
||||
@@ -36,6 +38,9 @@
|
||||
footer?: Snippet;
|
||||
} = $props();
|
||||
|
||||
let effectiveStatus = $derived(
|
||||
getEffectiveBuildStatus(deployment.status, deployment.$createdAt, $regionalConsoleVariables)
|
||||
);
|
||||
let totalSize = $derived(humanFileSize(deployment?.totalSize ?? 0));
|
||||
</script>
|
||||
|
||||
@@ -122,11 +127,11 @@
|
||||
</Layout.Stack>
|
||||
|
||||
<Layout.Stack direction="row" gap="xl">
|
||||
{#if deployment.status === 'failed'}
|
||||
{#if effectiveStatus === 'failed'}
|
||||
<Layout.Stack gap="xxs" inline>
|
||||
{@render titleSnippet('Status')}
|
||||
<Typography.Text variant="m-400" color="--fgcolor-neutral-primary">
|
||||
<Status status={deployment.status} label={deployment.status} />
|
||||
<Status status={effectiveStatus} label={effectiveStatus} />
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
{:else}
|
||||
|
||||
+3
-2
@@ -2,7 +2,7 @@
|
||||
import { page } from '$app/state';
|
||||
import { SubMenu } from '$lib/components/menu';
|
||||
import { type Models } from '@appwrite.io/console';
|
||||
import { IconDownload } from '@appwrite.io/pink-icons-svelte';
|
||||
import { IconDownload, IconChevronRight } from '@appwrite.io/pink-icons-svelte';
|
||||
import { ActionMenu } from '@appwrite.io/pink-svelte';
|
||||
import { getOutputDownload, getSourceDownload } from '../store';
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
{#if deployment?.status === 'ready' || deployment?.status === 'failed' || deployment?.status === 'building'}
|
||||
<SubMenu>
|
||||
<ActionMenu.Root noPadding>
|
||||
<ActionMenu.Item.Button trailingIcon={IconDownload}>Download</ActionMenu.Item.Button>
|
||||
<ActionMenu.Item.Button leadingIcon={IconDownload} trailingIcon={IconChevronRight}
|
||||
>Download</ActionMenu.Item.Button>
|
||||
</ActionMenu.Root>
|
||||
<svelte:fragment slot="menu">
|
||||
<ActionMenu.Root>
|
||||
|
||||
+8
-1
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import Confirm from '$lib/components/confirm.svelte';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
@@ -20,6 +21,12 @@
|
||||
functionId: selectedDeployment.resourceId,
|
||||
deploymentId: selectedDeployment.$id
|
||||
});
|
||||
if (page.route.id?.includes('deployment-[deployment]')) {
|
||||
goto(
|
||||
`${base}/project-${page.params.region}-${page.params.project}/functions/function-${page.params.function}/deployments`
|
||||
);
|
||||
return;
|
||||
}
|
||||
await invalidate(Dependencies.FUNCTION);
|
||||
showDelete = false;
|
||||
addNotification({
|
||||
|
||||
+2
-2
@@ -113,7 +113,7 @@
|
||||
placement={'bottom'}>
|
||||
<div>
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconRefresh}
|
||||
leadingIcon={IconRefresh}
|
||||
disabled={activeDeployment.sourceSize === 0}
|
||||
on:click={() => {
|
||||
selectedDeployment = activeDeployment;
|
||||
@@ -134,7 +134,7 @@
|
||||
deployment={activeDeployment}
|
||||
{toggle} />
|
||||
<ActionMenu.Item.Anchor
|
||||
trailingIcon={IconTerminal}
|
||||
leadingIcon={IconTerminal}
|
||||
href={`${base}/project-${page.params.region}-${page.params.project}/functions/function-${page.params.function}/deployment-${activeDeployment.$id}`}>
|
||||
Build logs
|
||||
</ActionMenu.Item.Anchor>
|
||||
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import Confirm from '$lib/components/confirm.svelte';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { page } from '$app/state';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
export let showDelete = false;
|
||||
export let selectedDeployment: Models.Deployment = null;
|
||||
let error: string;
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.functions.deleteDeployment({
|
||||
functionId: selectedDeployment.resourceId,
|
||||
deploymentId: selectedDeployment.$id
|
||||
});
|
||||
await invalidate(Dependencies.FUNCTION);
|
||||
showDelete = false;
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `Deployment has been deleted`
|
||||
});
|
||||
trackEvent(Submit.DeploymentDelete);
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
trackError(e, Submit.DeploymentDelete);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Confirm onSubmit={handleSubmit} title="Delete deployment" bind:open={showDelete} bind:error>
|
||||
Are you sure you want to delete this deployment?
|
||||
</Confirm>
|
||||
+36
-15
@@ -24,9 +24,11 @@
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import { capitalize } from '$lib/helpers/string';
|
||||
import { formatTimeDetailed } from '$lib/helpers/timeConversion';
|
||||
import { getEffectiveBuildStatus } from '$lib/helpers/buildTimeout';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import { timer } from '$lib/actions/timer';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { IconDotsHorizontal, IconRefresh } from '@appwrite.io/pink-icons-svelte';
|
||||
import { IconDotsHorizontal, IconRefresh, IconTrash } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Menu } from '$lib/components/menu';
|
||||
import { canWriteFunctions } from '$lib/stores/roles';
|
||||
import { Click, trackEvent } from '$lib/actions/analytics';
|
||||
@@ -36,22 +38,29 @@
|
||||
import { readOnly } from '$lib/stores/billing';
|
||||
import RedeployModal from '../(modals)/redeployModal.svelte';
|
||||
|
||||
export let data;
|
||||
let { data } = $props();
|
||||
|
||||
let showDelete = false;
|
||||
let showCancel = false;
|
||||
let showActivate = false;
|
||||
let showRedeploy = false;
|
||||
let effectiveStatus = $derived(
|
||||
getEffectiveBuildStatus(
|
||||
data.deployment.status,
|
||||
data.deployment.$createdAt,
|
||||
$regionalConsoleVariables
|
||||
)
|
||||
);
|
||||
let showDelete = $state(false);
|
||||
let showCancel = $state(false);
|
||||
let showActivate = $state(false);
|
||||
let showRedeploy = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
return realtime.forProject(page.params.region, 'console', (response) => {
|
||||
return realtime.forConsole(page.params.region, 'console', (message) => {
|
||||
if (
|
||||
response.events.includes(
|
||||
message.events.includes(
|
||||
`functions.${page.params.function}.deployments.${page.params.deployment}.update`
|
||||
)
|
||||
) {
|
||||
const payload = response.payload as Models.Deployment;
|
||||
if (payload.status === 'ready') {
|
||||
const payload = message.payload as Models.Deployment;
|
||||
if (['ready', 'failed'].includes(payload.status)) {
|
||||
invalidate(Dependencies.DEPLOYMENT);
|
||||
}
|
||||
}
|
||||
@@ -78,7 +87,7 @@
|
||||
<DeploymentCard proxyRuleList={data.proxyRuleList} deployment={data.deployment}>
|
||||
{#snippet footer()}
|
||||
<Layout.Stack direction="row" alignItems="center" inline>
|
||||
{#if data.deployment.status === 'processing' || data.deployment.status === 'building' || data.deployment.status === 'waiting'}
|
||||
{#if effectiveStatus === 'processing' || effectiveStatus === 'building' || effectiveStatus === 'waiting'}
|
||||
<Button
|
||||
text
|
||||
on:click={() => {
|
||||
@@ -98,7 +107,7 @@
|
||||
placement={'bottom'}>
|
||||
<div>
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconRefresh}
|
||||
leadingIcon={IconRefresh}
|
||||
disabled={data.deployment.sourceSize === 0}
|
||||
on:click={() => {
|
||||
showRedeploy = true;
|
||||
@@ -115,6 +124,18 @@
|
||||
{#if !!data.deployment?.sourceSize || !!data.deployment?.sourceSize}
|
||||
<DownloadActionMenuItem deployment={data.deployment} {toggle} />
|
||||
{/if}
|
||||
{#if $canWriteFunctions && ['ready', 'failed'].includes(data.deployment.status)}
|
||||
<ActionMenu.Item.Button
|
||||
status="danger"
|
||||
leadingIcon={IconTrash}
|
||||
on:click={() => {
|
||||
showDelete = true;
|
||||
toggle();
|
||||
}}
|
||||
style="width: 100%">
|
||||
Delete
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
</ActionMenu.Root>
|
||||
</svelte:fragment>
|
||||
</Menu>
|
||||
@@ -140,9 +161,9 @@
|
||||
<Card.Base padding="s">
|
||||
<Accordion
|
||||
title="Deployment logs"
|
||||
badge={capitalize(data.deployment.status)}
|
||||
badge={capitalize(effectiveStatus)}
|
||||
open
|
||||
badgeType={badgeTypeDeployment(data.deployment.status)}
|
||||
badgeType={badgeTypeDeployment(effectiveStatus)}
|
||||
hideDivider>
|
||||
<Layout.Stack gap="xl">
|
||||
{#key data.deployment.buildLogs}
|
||||
@@ -155,7 +176,7 @@
|
||||
|
||||
<svelte:fragment slot="end">
|
||||
<Layout.Stack direction="row" alignItems="center" inline>
|
||||
{#if ['processing', 'building'].includes(data.deployment.status)}
|
||||
{#if ['processing', 'building'].includes(effectiveStatus)}
|
||||
<Typography.Code color="--fgcolor-neutral-secondary">
|
||||
<Layout.Stack direction="row" alignItems="center" inline>
|
||||
<p use:timer={{ start: data.deployment.$createdAt }}></p>
|
||||
|
||||
+16
-12
@@ -28,6 +28,8 @@
|
||||
import Delete from './(modals)/deleteModal.svelte';
|
||||
import { capitalize } from '$lib/helpers/string';
|
||||
import { deploymentStatusConverter } from '$lib/stores/git';
|
||||
import { getEffectiveBuildStatus } from '$lib/helpers/buildTimeout';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import DownloadActionMenuItem from './(components)/downloadActionMenuItem.svelte';
|
||||
import { Menu } from '$lib/components/menu';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
@@ -82,9 +84,13 @@
|
||||
{/each}
|
||||
<Table.Header.Cell column="actions" {root} />
|
||||
{/snippet}
|
||||
|
||||
{#snippet children(root)}
|
||||
{#each data.deploymentList.deployments as deployment (deployment.$id)}
|
||||
{@const effectiveStatus = getEffectiveBuildStatus(
|
||||
deployment.status,
|
||||
deployment.$createdAt,
|
||||
$regionalConsoleVariables
|
||||
)}
|
||||
<Table.Row.Link
|
||||
{root}
|
||||
id={deployment.$id}
|
||||
@@ -96,23 +102,21 @@
|
||||
<Id value={deployment.$id}>{deployment.$id}</Id>
|
||||
{/key}
|
||||
{:else if column.id === 'status'}
|
||||
{@const status = deployment.status}
|
||||
|
||||
{#if data?.activeDeployment?.$id === deployment?.$id}
|
||||
<Status status="complete" label="Active" />
|
||||
{:else}
|
||||
<Status
|
||||
status={deploymentStatusConverter(status)}
|
||||
label={capitalize(status)} />
|
||||
status={deploymentStatusConverter(effectiveStatus)}
|
||||
label={capitalize(effectiveStatus)} />
|
||||
{/if}
|
||||
{:else if column.id === 'type'}
|
||||
<DeploymentSource {deployment} />
|
||||
{:else if column.id === '$updatedAt'}
|
||||
<DeploymentCreatedBy {deployment} />
|
||||
{:else if column.id === 'buildDuration'}
|
||||
{#if ['waiting'].includes(deployment.status)}
|
||||
{#if ['waiting'].includes(effectiveStatus)}
|
||||
-
|
||||
{:else if ['processing', 'building'].includes(deployment.status)}
|
||||
{:else if ['processing', 'building'].includes(effectiveStatus)}
|
||||
<span use:timer={{ start: deployment.$createdAt }}></span>
|
||||
{:else}
|
||||
{formatTimeDetailed(deployment.buildDuration)}
|
||||
@@ -139,7 +143,7 @@
|
||||
placement={'bottom'}>
|
||||
<div>
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconRefresh}
|
||||
leadingIcon={IconRefresh}
|
||||
disabled={deployment.sourceSize === 0}
|
||||
on:click={() => {
|
||||
selectedDeployment = deployment;
|
||||
@@ -155,7 +159,7 @@
|
||||
</Tooltip>
|
||||
{#if deployment.status === 'ready' && deployment.$id !== $func.deploymentId}
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconLightningBolt}
|
||||
leadingIcon={IconLightningBolt}
|
||||
on:click={() => {
|
||||
selectedDeployment = deployment;
|
||||
showActivate = true;
|
||||
@@ -167,7 +171,7 @@
|
||||
|
||||
<DownloadActionMenuItem {deployment} {toggle} />
|
||||
|
||||
{#if deployment.status === 'processing' || deployment.status === 'building' || deployment.status === 'waiting'}
|
||||
{#if effectiveStatus === 'processing' || effectiveStatus === 'building' || effectiveStatus === 'waiting'}
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconXCircle}
|
||||
on:click={() => {
|
||||
@@ -180,9 +184,9 @@
|
||||
Cancel
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
{#if deployment.status !== 'building' && deployment.status !== 'processing' && deployment.status !== 'waiting'}
|
||||
{#if effectiveStatus !== 'building' && effectiveStatus !== 'processing' && effectiveStatus !== 'waiting'}
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconTrash}
|
||||
leadingIcon={IconTrash}
|
||||
status="danger"
|
||||
on:click={() => {
|
||||
selectedDeployment = deployment;
|
||||
|
||||
+12
-5
@@ -15,6 +15,8 @@
|
||||
IconXCircle
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import { ActionMenu, Icon, Tooltip } from '@appwrite.io/pink-svelte';
|
||||
import { getEffectiveBuildStatus } from '$lib/helpers/buildTimeout';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
|
||||
export let selectedDeployment: Models.Deployment;
|
||||
export let deployment: Models.Deployment;
|
||||
@@ -51,6 +53,11 @@
|
||||
</Button>
|
||||
<svelte:fragment slot="menu" let:toggle>
|
||||
<ActionMenu.Root>
|
||||
{@const effectiveStatus = getEffectiveBuildStatus(
|
||||
deployment.status,
|
||||
deployment.$createdAt,
|
||||
$regionalConsoleVariables
|
||||
)}
|
||||
{#if !inCard}
|
||||
<Tooltip disabled={selectedDeployment?.sourceSize !== 0} placement={'bottom'}>
|
||||
<div>
|
||||
@@ -70,7 +77,7 @@
|
||||
<div slot="tooltip">Source is empty</div>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
{#if deployment?.status === 'ready' && deployment?.$id !== activeDeployment}
|
||||
{#if effectiveStatus === 'ready' && deployment?.$id !== activeDeployment}
|
||||
<ActionMenu.Item.Button
|
||||
leadingIcon={IconLightningBolt}
|
||||
on:click={(e) => {
|
||||
@@ -82,7 +89,7 @@
|
||||
Activate
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
{#if deployment?.status === 'ready' || deployment?.status === 'failed' || deployment?.status === 'building'}
|
||||
{#if effectiveStatus === 'ready' || effectiveStatus === 'failed' || effectiveStatus === 'building'}
|
||||
<SubMenu>
|
||||
<ActionMenu.Root noPadding>
|
||||
<ActionMenu.Item.Button
|
||||
@@ -101,7 +108,7 @@
|
||||
</ActionMenu.Item.Anchor>
|
||||
|
||||
<ActionMenu.Item.Anchor
|
||||
disabled={deployment?.status !== 'ready'}
|
||||
disabled={effectiveStatus !== 'ready'}
|
||||
on:click={toggle}
|
||||
href={getOutputDownload(deployment.$id)}
|
||||
external>
|
||||
@@ -112,7 +119,7 @@
|
||||
</SubMenu>
|
||||
{/if}
|
||||
|
||||
{#if deployment?.status === 'processing' || deployment?.status === 'building' || deployment.status === 'waiting'}
|
||||
{#if effectiveStatus === 'processing' || effectiveStatus === 'building' || effectiveStatus === 'waiting'}
|
||||
<ActionMenu.Item.Button
|
||||
leadingIcon={IconXCircle}
|
||||
status="danger"
|
||||
@@ -124,7 +131,7 @@
|
||||
Cancel
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
{#if deployment.status !== 'building' && deployment.status !== 'processing' && deployment?.status !== 'waiting'}
|
||||
{#if ['ready', 'failed'].includes(deployment.status)}
|
||||
<ActionMenu.Item.Button
|
||||
status="danger"
|
||||
leadingIcon={IconTrash}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
<script lang="ts">
|
||||
import { capitalize } from '$lib/helpers/string';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { getEffectiveBuildStatus } from '$lib/helpers/buildTimeout';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { Badge, Card, Layout, Logs, Spinner, Typography } from '@appwrite.io/pink-svelte';
|
||||
import LogsTimer from './logsTimer.svelte';
|
||||
@@ -38,15 +40,19 @@
|
||||
emptyCopy?: string;
|
||||
} = $props();
|
||||
|
||||
let effectiveStatus = $derived(
|
||||
getEffectiveBuildStatus(deployment.status, deployment.$createdAt, $regionalConsoleVariables)
|
||||
);
|
||||
|
||||
function setCopy() {
|
||||
if (deployment.status === 'failed') {
|
||||
if (effectiveStatus === 'failed') {
|
||||
return 'Your deployment has failed.';
|
||||
} else if (deployment.status === 'building') {
|
||||
} else if (effectiveStatus === 'building') {
|
||||
//Do not remove empty space before the string it's an invisible character
|
||||
return '[37mPreparing for build ... [0m\n';
|
||||
} else if (deployment.status === 'waiting') {
|
||||
} else if (effectiveStatus === 'waiting') {
|
||||
return '[37mPreparing for build ... [0m\n';
|
||||
} else if (deployment.status === 'processing') {
|
||||
} else if (effectiveStatus === 'processing') {
|
||||
return '[37mPreparing for build ... [0m\n';
|
||||
} else {
|
||||
return emptyCopy;
|
||||
@@ -62,16 +68,16 @@
|
||||
Deployment logs
|
||||
</Typography.Text>
|
||||
<Badge
|
||||
content={capitalize(deployment.status)}
|
||||
content={capitalize(effectiveStatus)}
|
||||
size="xs"
|
||||
variant="secondary"
|
||||
type={badgeTypeDeployment(deployment.status)} />
|
||||
type={badgeTypeDeployment(effectiveStatus)} />
|
||||
</Layout.Stack>
|
||||
<LogsTimer status={deployment.status} {deployment} />
|
||||
<LogsTimer status={effectiveStatus} {deployment} />
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
|
||||
{#if ['waiting', 'processing'].includes(deployment.status) || (deployment.status === 'building' && !deployment?.buildLogs?.length)}
|
||||
{#if ['waiting', 'processing'].includes(effectiveStatus) || (effectiveStatus === 'building' && !deployment?.buildLogs?.length)}
|
||||
<Card.Base variant="secondary">
|
||||
<Layout.Stack direction="row" justifyContent="center" gap="s">
|
||||
<Spinner /> Waiting for build to start...
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
<script lang="ts">
|
||||
import { timer } from '$lib/actions/timer';
|
||||
import { formatTimeDetailed } from '$lib/helpers/timeConversion';
|
||||
import { getEffectiveBuildStatus } from '$lib/helpers/buildTimeout';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
import { Layout, Spinner, Typography } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let status: string;
|
||||
export let deployment: Models.Deployment;
|
||||
let { status, deployment }: { status: string; deployment: Models.Deployment } = $props();
|
||||
|
||||
let effectiveStatus = $derived(
|
||||
getEffectiveBuildStatus(status, deployment.$createdAt, $regionalConsoleVariables)
|
||||
);
|
||||
</script>
|
||||
|
||||
<Layout.Stack direction="row" alignItems="center" inline>
|
||||
{#if ['processing', 'building'].includes(status)}
|
||||
{#if ['processing', 'building'].includes(effectiveStatus)}
|
||||
<Typography.Code color="--fgcolor-neutral-secondary">
|
||||
<Layout.Stack direction="row" alignItems="center" inline>
|
||||
<p use:timer={{ start: deployment.$createdAt }}></p>
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
import { isCloud } from '$lib/system';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { capitalize } from '$lib/helpers/string';
|
||||
import { getEffectiveBuildStatus } from '$lib/helpers/buildTimeout';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import { regionalProtocol } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
@@ -40,6 +42,9 @@
|
||||
footer?: Snippet;
|
||||
} = $props();
|
||||
|
||||
let effectiveStatus = $derived(
|
||||
getEffectiveBuildStatus(deployment.status, deployment.$createdAt, $regionalConsoleVariables)
|
||||
);
|
||||
let show = $state(false);
|
||||
|
||||
const totalSize = $derived(humanFileSize(deployment?.totalSize ?? 0));
|
||||
@@ -113,15 +118,15 @@
|
||||
</Layout.Stack>
|
||||
|
||||
<Layout.Stack direction="row" gap="xl">
|
||||
{#if deployment.status === 'failed'}
|
||||
{#if effectiveStatus === 'failed'}
|
||||
<Layout.Stack gap="xxs" inline>
|
||||
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
|
||||
Status
|
||||
</Typography.Text>
|
||||
<Typography.Text variant="m-400" color="--fgcolor-neutral-primary">
|
||||
<Status
|
||||
status={deployment.status}
|
||||
label={capitalize(deployment.status)} />
|
||||
status={effectiveStatus}
|
||||
label={capitalize(effectiveStatus)} />
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
{:else}
|
||||
|
||||
+6
-5
@@ -22,15 +22,16 @@
|
||||
siteId: selectedDeployment.resourceId,
|
||||
deploymentId: selectedDeployment.$id
|
||||
});
|
||||
if (page.route.id?.includes('deployment-[deployment]')) {
|
||||
goto(
|
||||
`${base}/project-${page.params.region}-${page.params.project}/sites/site-${page.params.site}/deployments`
|
||||
);
|
||||
return;
|
||||
}
|
||||
await Promise.all([
|
||||
invalidate(Dependencies.DEPLOYMENTS),
|
||||
invalidate(Dependencies.SITE)
|
||||
]);
|
||||
if (page.url.href.includes(`deployment-${selectedDeployment.$id}`)) {
|
||||
await goto(
|
||||
`${base}/project-${page.params.region}-${page.params.project}/sites/site-${page.params.site}/deployments`
|
||||
);
|
||||
}
|
||||
showDelete = false;
|
||||
addNotification({
|
||||
type: 'success',
|
||||
|
||||
+10
-5
@@ -13,6 +13,8 @@
|
||||
import ActivateDeploymentModal from '../../../activateDeploymentModal.svelte';
|
||||
import { Accordion, Tooltip } from '@appwrite.io/pink-svelte';
|
||||
import { capitalize } from '$lib/helpers/string';
|
||||
import { getEffectiveBuildStatus } from '$lib/helpers/buildTimeout';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import LogsTimer from '../../../(components)/logsTimer.svelte';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
@@ -24,6 +26,9 @@
|
||||
let { data }: PageProps = $props();
|
||||
|
||||
let deployment = $derived(data.deployment);
|
||||
let effectiveStatus = $derived(
|
||||
getEffectiveBuildStatus(deployment.status, deployment.$createdAt, $regionalConsoleVariables)
|
||||
);
|
||||
|
||||
let showRedeploy = $state(false);
|
||||
let showActivate = $state(false);
|
||||
@@ -46,7 +51,7 @@
|
||||
<Container>
|
||||
<SiteCard {deployment} proxyRuleList={data.proxyRuleList}>
|
||||
{#snippet footer()}
|
||||
{#if deployment?.status === 'ready' && data.proxyRuleList?.total}
|
||||
{#if effectiveStatus === 'ready' && data.proxyRuleList?.total}
|
||||
<Button
|
||||
external
|
||||
href={`${$regionalProtocol}${data.proxyRuleList.rules[0]?.domain}`}>
|
||||
@@ -79,13 +84,13 @@
|
||||
<Card padding="s">
|
||||
<Accordion
|
||||
title="Deployment logs"
|
||||
badge={capitalize(deployment.status)}
|
||||
badge={capitalize(effectiveStatus)}
|
||||
open
|
||||
badgeType={badgeTypeDeployment(deployment.status)}
|
||||
badgeType={badgeTypeDeployment(effectiveStatus)}
|
||||
hideDivider>
|
||||
<Logs {deployment} hideTitle hideScrollButtons fullHeight />
|
||||
<Logs {deployment} hideTitle fullHeight />
|
||||
<svelte:fragment slot="end">
|
||||
<LogsTimer status={deployment.status} {deployment} />
|
||||
<LogsTimer status={effectiveStatus} {deployment} />
|
||||
</svelte:fragment>
|
||||
</Accordion>
|
||||
</Card>
|
||||
|
||||
+11
-12
@@ -17,6 +17,8 @@
|
||||
import { capitalize } from '$lib/helpers/string';
|
||||
import DeploymentActionMenu from '../../(components)/deploymentActionMenu.svelte';
|
||||
import { deploymentStatusConverter } from '$lib/stores/git';
|
||||
import { getEffectiveBuildStatus } from '$lib/helpers/buildTimeout';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { invalidate } from '$app/navigation';
|
||||
@@ -69,9 +71,13 @@
|
||||
{/each}
|
||||
<Table.Header.Cell column="actions" {root} />
|
||||
{/snippet}
|
||||
|
||||
{#snippet children(root)}
|
||||
{#each data.deploymentList.deployments as deployment (deployment.$id)}
|
||||
{@const effectiveStatus = getEffectiveBuildStatus(
|
||||
deployment.status,
|
||||
deployment.$createdAt,
|
||||
$regionalConsoleVariables
|
||||
)}
|
||||
<Table.Row.Link
|
||||
{root}
|
||||
id={deployment.$id}
|
||||
@@ -83,22 +89,21 @@
|
||||
<Id value={deployment.$id}>{deployment.$id}</Id>
|
||||
{/key}
|
||||
{:else if column.id === 'status'}
|
||||
{@const status = deployment.status}
|
||||
{#if data?.activeDeployment?.$id === deployment?.$id}
|
||||
<Status status="complete" label="Active" />
|
||||
{:else}
|
||||
<Status
|
||||
status={deploymentStatusConverter(status)}
|
||||
label={capitalize(status)} />
|
||||
status={deploymentStatusConverter(effectiveStatus)}
|
||||
label={capitalize(effectiveStatus)} />
|
||||
{/if}
|
||||
{:else if column.id === 'type'}
|
||||
<DeploymentSource {deployment} />
|
||||
{:else if column.id === '$updatedAt'}
|
||||
<DeploymentCreatedBy {deployment} />
|
||||
{:else if column.id === 'buildDuration'}
|
||||
{#if ['waiting'].includes(deployment.status)}
|
||||
{#if ['waiting'].includes(effectiveStatus)}
|
||||
-
|
||||
{:else if ['processing', 'building'].includes(deployment.status)}
|
||||
{:else if ['processing', 'building'].includes(effectiveStatus)}
|
||||
<span use:timer={{ start: deployment.$createdAt }}></span>
|
||||
{:else}
|
||||
{formatTimeDetailed(deployment.buildDuration)}
|
||||
@@ -127,12 +132,6 @@
|
||||
</Table.Row.Link>
|
||||
{/each}
|
||||
{/snippet}
|
||||
|
||||
{#snippet deleteContent(count)}
|
||||
Are you sure you want to delete <strong>{count}</strong>
|
||||
{count > 1 ? 'deployments' : 'deployment'} from your site -
|
||||
<strong>{page.data.site.name}</strong>?
|
||||
{/snippet}
|
||||
</MultiSelectionTable>
|
||||
|
||||
{#if selectedDeployment}
|
||||
|
||||
+9
-2
@@ -11,6 +11,8 @@
|
||||
import ActivateDeploymentModal from '../activateDeploymentModal.svelte';
|
||||
import CancelDeploymentModal from './deployments/cancelDeploymentModal.svelte';
|
||||
import { capitalize } from '$lib/helpers/string';
|
||||
import { getEffectiveBuildStatus } from '$lib/helpers/buildTimeout';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import DeleteDeploymentModal from './deployments/deleteDeploymentModal.svelte';
|
||||
import DeploymentActionMenu from '../(components)/deploymentActionMenu.svelte';
|
||||
import { deploymentStatusConverter } from '$lib/stores/git';
|
||||
@@ -72,12 +74,17 @@
|
||||
</Table.Cell>
|
||||
<Table.Cell {root}>
|
||||
{@const status = deployment.status}
|
||||
{@const effectiveStatus = getEffectiveBuildStatus(
|
||||
status,
|
||||
deployment.$createdAt,
|
||||
$regionalConsoleVariables
|
||||
)}
|
||||
{#if activeDeployment?.$id === deployment?.$id}
|
||||
<Status status="complete" label="Active" />
|
||||
{:else}
|
||||
<Status
|
||||
status={deploymentStatusConverter(status)}
|
||||
label={capitalize(status)} />
|
||||
status={deploymentStatusConverter(effectiveStatus)}
|
||||
label={capitalize(effectiveStatus)} />
|
||||
{/if}
|
||||
</Table.Cell>
|
||||
|
||||
|
||||
@@ -69,12 +69,12 @@ export const adapterDataList = [
|
||||
{
|
||||
framework: 'nextjs',
|
||||
ssr: {
|
||||
desc: "Ensure you don't set $ in $ file.",
|
||||
code: ['output', 'next.config.js'],
|
||||
desc: 'Set $ in $ file.',
|
||||
code: ["output: 'standalone'", 'next.config.js'],
|
||||
url: 'https://nextjs.org/docs/pages/building-your-application/deploying'
|
||||
},
|
||||
static: {
|
||||
desc: 'Set $ in $ file',
|
||||
desc: 'Set $ in $ file.',
|
||||
code: ["output: 'export'", 'next.config.js'],
|
||||
url: 'https://nextjs.org/docs/pages/building-your-application/deploying/static-exports'
|
||||
}
|
||||
@@ -82,12 +82,12 @@ export const adapterDataList = [
|
||||
{
|
||||
framework: 'analog',
|
||||
ssr: {
|
||||
desc: 'Set $ in $ plugin in $',
|
||||
desc: 'Set $ in $ plugin in $.',
|
||||
code: ['ssr: true', 'analog', 'vite.config.ts'],
|
||||
url: 'https://analogjs.org/docs/features/server/server-side-rendering'
|
||||
},
|
||||
static: {
|
||||
desc: 'Set $ in $ plugin in $',
|
||||
desc: 'Set $ in $ plugin in $.',
|
||||
code: ['static: true', 'analog', 'vite.config.ts'],
|
||||
url: 'https://analogjs.org/docs/features/server/static-site-generation'
|
||||
}
|
||||
|
||||
+48
-15
@@ -36,21 +36,34 @@
|
||||
frameworks.find((framework) => framework.key === site.framework)
|
||||
);
|
||||
let showFallback = $derived(adapter === Adapter.Static);
|
||||
let hasChanges = $derived(
|
||||
|
||||
let isUntouched = $derived(
|
||||
installCommand === site?.installCommand &&
|
||||
buildCommand === site?.buildCommand &&
|
||||
outputDirectory === site?.outputDirectory &&
|
||||
selectedFramework?.key === site?.framework &&
|
||||
fallback === (site?.fallbackFile || undefined) &&
|
||||
adapter === site?.adapter
|
||||
(fallback ?? '') === (site?.fallbackFile ?? '') &&
|
||||
(adapter ?? '') === (site?.adapter ?? '')
|
||||
);
|
||||
let frameworkAdapterData = $derived(
|
||||
selectedFramework.adapters.find((a) => a.key === adapter) ?? selectedFramework.adapters[0]
|
||||
);
|
||||
|
||||
$effect(() => {
|
||||
if (adapter) {
|
||||
const data = selectedFramework.adapters.find((a) => a.key === adapter);
|
||||
if (data) {
|
||||
installCommand = data.installCommand;
|
||||
buildCommand = data.buildCommand;
|
||||
outputDirectory = data.outputDirectory;
|
||||
fallback = data.fallbackFile;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (selectedFramework?.key !== site.framework) {
|
||||
//Update adapter
|
||||
// Update adapter
|
||||
const singleAdapter = selectedFramework?.adapters?.length <= 1;
|
||||
if (singleAdapter) {
|
||||
const hasSSR = selectedFramework?.adapters?.some((a) => a?.key === Adapter.Ssr);
|
||||
@@ -65,11 +78,13 @@
|
||||
}
|
||||
|
||||
//Update values
|
||||
const data = selectedFramework.adapters.find((a) => a.key === adapter);
|
||||
const data =
|
||||
selectedFramework.adapters.find((a) => a.key === adapter) ??
|
||||
selectedFramework.adapters[0];
|
||||
installCommand = data.installCommand;
|
||||
buildCommand = data.buildCommand;
|
||||
outputDirectory = data.outputDirectory;
|
||||
adapter = selectedFramework.adapters[0].key as Adapter;
|
||||
adapter = data.key as Adapter;
|
||||
fallback = data.fallbackFile;
|
||||
} else {
|
||||
adapter = site.adapter as Adapter;
|
||||
@@ -157,11 +172,11 @@
|
||||
const data = selectedFramework.adapters.find((a) => a.key === adapter);
|
||||
|
||||
if (type === 'installCommand') {
|
||||
installCommand = site?.installCommand ?? data.installCommand;
|
||||
installCommand = data.installCommand;
|
||||
} else if (type === 'buildCommand') {
|
||||
buildCommand = site?.buildCommand ?? data.buildCommand;
|
||||
buildCommand = data.buildCommand;
|
||||
} else if (type === 'outputDirectory') {
|
||||
outputDirectory = site?.outputDirectory ?? data.outputDirectory;
|
||||
outputDirectory = data.outputDirectory;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -216,7 +231,8 @@
|
||||
{adapterData.ssr.desc}
|
||||
{/if}
|
||||
{#if adapterData?.ssr?.url}
|
||||
<Link external href={adapterData.ssr.url}>Learn more</Link>
|
||||
<Link variant="muted" size="m" external href={adapterData.ssr.url}
|
||||
>Learn more</Link>
|
||||
{/if}
|
||||
</Card.Selector>
|
||||
<Card.Selector
|
||||
@@ -243,7 +259,11 @@
|
||||
{adapterData.static.desc}
|
||||
{/if}
|
||||
{#if adapterData?.static?.url}
|
||||
<Link external href={adapterData.static.url}>Learn more</Link>
|
||||
<Link
|
||||
variant="muted"
|
||||
size="m"
|
||||
external
|
||||
href={adapterData.static.url}>Learn more</Link>
|
||||
{/if}
|
||||
</Card.Selector>
|
||||
</Layout.Grid>
|
||||
@@ -258,7 +278,11 @@
|
||||
placeholder={frameworkAdapterData?.installCommand ||
|
||||
'Enter install command'} />
|
||||
|
||||
<Button secondary size="s" on:click={() => reset('installCommand')}>
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
on:click={() => reset('installCommand')}
|
||||
disabled={installCommand === frameworkAdapterData?.installCommand}>
|
||||
Reset
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
@@ -269,7 +293,11 @@
|
||||
bind:value={buildCommand}
|
||||
placeholder={frameworkAdapterData?.buildCommand ||
|
||||
'Enter build command'} />
|
||||
<Button secondary size="s" on:click={() => reset('buildCommand')}>
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
disabled={buildCommand === frameworkAdapterData?.buildCommand}
|
||||
on:click={() => reset('buildCommand')}>
|
||||
Reset
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
@@ -280,7 +308,12 @@
|
||||
bind:value={outputDirectory}
|
||||
placeholder={frameworkAdapterData?.outputDirectory ||
|
||||
'Enter output directory'} />
|
||||
<Button secondary size="s" on:click={() => reset('outputDirectory')}>
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
disabled={(outputDirectory ?? '') ===
|
||||
(frameworkAdapterData?.outputDirectory ?? '')}
|
||||
on:click={() => reset('outputDirectory')}>
|
||||
Reset
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
@@ -305,7 +338,7 @@
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button disabled={hasChanges} submit>Update</Button>
|
||||
<Button disabled={isUntouched} submit>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
|
||||
@@ -125,11 +125,11 @@
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
<InputSelect
|
||||
required
|
||||
id="project"
|
||||
label="Choose a project"
|
||||
options={projectOptions ?? []}
|
||||
bind:value={$supportData.project}
|
||||
required={false}
|
||||
placeholder="Select project" />
|
||||
<InputText
|
||||
id="subject"
|
||||
|
||||
Reference in New Issue
Block a user