mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'main' into 'remove-archives'.
This commit is contained in:
+1
-1
@@ -39,7 +39,7 @@ ENV NODE_OPTIONS=--max_old_space_size=8192
|
||||
|
||||
RUN bun run build
|
||||
|
||||
FROM nginx:1.26.3-alpine
|
||||
FROM nginx:1.29.5-alpine
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
"name": "@appwrite/console",
|
||||
"dependencies": {
|
||||
"@ai-sdk/svelte": "^1.1.24",
|
||||
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@95675c4",
|
||||
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@c6f60aa",
|
||||
"@appwrite.io/pink-icons": "0.25.0",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@c1feb89",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@df765cc",
|
||||
"@appwrite.io/pink-legacy": "^1.0.3",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@dd7e01c",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@df765cc",
|
||||
"@faker-js/faker": "^9.9.0",
|
||||
"@plausible-analytics/tracker": "^0.4.4",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
@@ -107,15 +107,15 @@
|
||||
|
||||
"@analytics/type-utils": ["@analytics/type-utils@0.6.4", "", {}, "sha512-Ou1gQxFakOWLcPnbFVsrPb8g1wLLUZYYJXDPjHkG07+5mustGs5yqACx42UAu4A6NszNN6Z5gGxhyH45zPWRxw=="],
|
||||
|
||||
"@appwrite.io/console": ["@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@95675c4", { "dependencies": { "bignumber.js": "9.0.0", "json-bigint": "1.0.0" } }],
|
||||
"@appwrite.io/console": ["@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@c6f60aa", { "dependencies": { "bignumber.js": "9.0.0", "json-bigint": "1.0.0" } }],
|
||||
|
||||
"@appwrite.io/pink-icons": ["@appwrite.io/pink-icons@0.25.0", "", {}, "sha512-0O3i2oEuh5mWvjO80i+X6rbzrWLJ1m5wmv2/M3a1p2PyBJsFxN8xQMTEmTn3Wl/D26SsM7SpzbdW6gmfgoVU9Q=="],
|
||||
|
||||
"@appwrite.io/pink-icons-svelte": ["@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@c1feb89", { "peerDependencies": { "svelte": "^4.0.0" } }],
|
||||
"@appwrite.io/pink-icons-svelte": ["@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@df765cc", { "peerDependencies": { "svelte": "^4.0.0" } }],
|
||||
|
||||
"@appwrite.io/pink-legacy": ["@appwrite.io/pink-legacy@1.0.3", "", { "dependencies": { "@appwrite.io/pink-icons": "1.0.0", "the-new-css-reset": "^1.11.2" } }, "sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ=="],
|
||||
|
||||
"@appwrite.io/pink-svelte": ["@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@dd7e01c", { "dependencies": { "@appwrite.io/pink-icons-svelte": "2.0.0-RC.1", "@floating-ui/dom": "^1.6.13", "@melt-ui/pp": "^0.3.2", "@melt-ui/svelte": "^0.86.6", "@tanstack/svelte-virtual": "^3.13.10", "ansicolor": "^2.0.3", "d3": "^7.9.0", "fuse.js": "^7.1.0", "pretty-bytes": "^6.1.1", "shiki": "^1.18.0", "svelte-motion": "^0.12.2", "svelte-sonner": "^0.3.28" }, "peerDependencies": { "svelte": "^4.0.0" } }],
|
||||
"@appwrite.io/pink-svelte": ["@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@df765cc", { "dependencies": { "@appwrite.io/pink-icons-svelte": "2.0.0-RC.1", "@floating-ui/dom": "^1.6.13", "@melt-ui/pp": "^0.3.2", "@melt-ui/svelte": "^0.86.6", "@tanstack/svelte-virtual": "^3.13.10", "ansicolor": "^2.0.3", "d3": "^7.9.0", "fuse.js": "^7.1.0", "pretty-bytes": "^6.1.1", "shiki": "^1.18.0", "svelte-motion": "^0.12.2", "svelte-sonner": "^0.3.28" }, "peerDependencies": { "svelte": "^4.0.0" } }],
|
||||
|
||||
"@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.2.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw=="],
|
||||
|
||||
|
||||
+3
-3
@@ -20,11 +20,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/svelte": "^1.1.24",
|
||||
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@95675c4",
|
||||
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@c6f60aa",
|
||||
"@appwrite.io/pink-icons": "0.25.0",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@c1feb89",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@df765cc",
|
||||
"@appwrite.io/pink-legacy": "^1.0.3",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@dd7e01c",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@df765cc",
|
||||
"@faker-js/faker": "^9.9.0",
|
||||
"@plausible-analytics/tracker": "^0.4.4",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
|
||||
@@ -62,12 +62,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
bind:error
|
||||
bind:show
|
||||
dismissible={false}
|
||||
onSubmit={handleSubmit}
|
||||
title="Update payment method state">
|
||||
<Modal bind:show bind:error onSubmit={handleSubmit} title="Update payment method state">
|
||||
<Layout.Stack direction="column" gap="m">
|
||||
<Typography.Text>
|
||||
State information is required for US payment methods to apply correct taxes and meet
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
</Layout.Stack>
|
||||
|
||||
<Table.Root
|
||||
class="responsive-table"
|
||||
columns={[
|
||||
{ id: 'type', width: { min: 150 } },
|
||||
{ id: 'name', width: { min: 80 } },
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
</Layout.Stack>
|
||||
|
||||
<Table.Root
|
||||
class="responsive-table"
|
||||
columns={[
|
||||
{ id: 'type', width: { min: 150 } },
|
||||
{ id: 'name', width: { min: 80 } },
|
||||
|
||||
@@ -1,35 +1,246 @@
|
||||
<script lang="ts">
|
||||
import { Icon, Layout, Tag, Tooltip } from '@appwrite.io/pink-svelte';
|
||||
import { queries, tagFormat, tags } from './store';
|
||||
import {
|
||||
Icon,
|
||||
Layout,
|
||||
Tooltip,
|
||||
CompoundTagRoot,
|
||||
CompoundTagChild,
|
||||
Typography,
|
||||
ActionMenu,
|
||||
Selector
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import { capitalize } from '$lib/helpers/string';
|
||||
import { queries, tags } from './store';
|
||||
import { IconX } from '@appwrite.io/pink-icons-svelte';
|
||||
import { parsedTags } from './setFilters';
|
||||
import { parsedTags, type ParsedTag } from './setFilters';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
import Menu from '$lib/components/menu/menu.svelte';
|
||||
import { addFilterAndApply, buildFilterCol, type FilterData } from './quickFilters';
|
||||
import QuickFilters from '$lib/components/filters/quickFilters.svelte';
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
|
||||
let {
|
||||
columns = writable([]),
|
||||
analyticsSource = ''
|
||||
}: { columns?: Writable<Column[]>; analyticsSource?: string } = $props();
|
||||
|
||||
function parseTagParts(tagString: string): { text: string; operator: boolean }[] {
|
||||
return tagString
|
||||
.split(/\*\*(.*?)\*\*/)
|
||||
.map((part, index) => {
|
||||
// Even indices are outside bold (operators), odd indices are inside bold (values)
|
||||
if (index % 2 === 0) {
|
||||
return part
|
||||
.split(/\s+/)
|
||||
.filter(Boolean)
|
||||
.map((t) => ({ text: t, operator: true }));
|
||||
} else {
|
||||
return [{ text: part, operator: false }];
|
||||
}
|
||||
})
|
||||
.flat()
|
||||
.filter((p) => Boolean(p.text));
|
||||
}
|
||||
|
||||
function getFilterFor(title: string): FilterData | null {
|
||||
if (!columns) return null;
|
||||
const col = ($columns as unknown as Column[]).find((c) => c.title === title);
|
||||
if (!col) return null;
|
||||
const filter = buildFilterCol(col);
|
||||
return filter ?? null;
|
||||
}
|
||||
|
||||
// Build available filter definitions from provided columns
|
||||
let availableFilters = $derived(
|
||||
($columns as unknown as Column[] | undefined)?.length
|
||||
? (($columns as unknown as Column[])
|
||||
.map((c) => (c.filter !== false ? buildFilterCol(c) : null))
|
||||
.filter((f) => f && f.options) as FilterData[])
|
||||
: []
|
||||
);
|
||||
|
||||
// QuickFilters uses the same filters list
|
||||
let filterCols = $derived(availableFilters);
|
||||
|
||||
// Always-show placeholders are derived from available filters (no hardcoding)
|
||||
// Use reactive array so runes can track changes
|
||||
let hiddenPlaceholders: string[] = $state([]);
|
||||
|
||||
let activeTitles = $derived(
|
||||
($parsedTags || []).map((t) => (t as ParsedTag).title).filter(Boolean) as string[]
|
||||
);
|
||||
|
||||
// Compute current placeholders (major filters not already active or dismissed)
|
||||
let placeholders = $derived(
|
||||
availableFilters
|
||||
.filter((f) => !activeTitles.includes(f.title))
|
||||
.filter((f) => !hiddenPlaceholders.includes(f.title))
|
||||
);
|
||||
</script>
|
||||
|
||||
{#if $parsedTags?.length}
|
||||
<Layout.Stack direction="row" gap="s" wrap="wrap" alignItems="center" inline>
|
||||
<Layout.Stack direction="row" gap="s" wrap="wrap" alignItems="center" inline>
|
||||
{#if $parsedTags?.length}
|
||||
{#each $parsedTags as tag (tag.tag)}
|
||||
<span>
|
||||
<Tooltip
|
||||
disabled={Array.isArray(tag.value) ? tag.value?.length < 3 : true}
|
||||
maxWidth="600px">
|
||||
<Tag
|
||||
size="s"
|
||||
on:click={() => {
|
||||
const t = $tags.filter((t) => t.tag.includes(tag.tag.split(' ')[0]));
|
||||
t.forEach((t) => (t ? queries.removeFilter(t) : null));
|
||||
queries.apply();
|
||||
parsedTags.update((tags) => tags.filter((t) => t.tag !== tag.tag));
|
||||
}}>
|
||||
{#key tag.tag}
|
||||
<span use:tagFormat>{tag.tag}</span>
|
||||
{/key}
|
||||
<Icon icon={IconX} size="s" slot="end" />
|
||||
</Tag>
|
||||
<CompoundTagRoot size="s">
|
||||
{@const parts = parseTagParts(tag.tag)}
|
||||
{@const property = (tag as ParsedTag).title}
|
||||
|
||||
{#each parts as part}
|
||||
<CompoundTagChild>
|
||||
<Menu>
|
||||
<span>
|
||||
{#if part.operator}
|
||||
<Typography.Text color="--fgcolor-neutral-secondary"
|
||||
>{part.text}</Typography.Text>
|
||||
{:else}
|
||||
<Typography.Text
|
||||
variant="m-500"
|
||||
color="--fgcolor-neutral-secondary"
|
||||
>{part.text
|
||||
.split(' or ')
|
||||
.map((t) => capitalize(t))
|
||||
.join(' or ')}</Typography.Text>
|
||||
{/if}
|
||||
</span>
|
||||
<svelte:fragment slot="menu">
|
||||
{#if property}
|
||||
{@const filter = getFilterFor(property)}
|
||||
{#if filter}
|
||||
{@const isArray = filter?.array}
|
||||
{@const selectedArray = Array.isArray(tag.value)
|
||||
? tag.value
|
||||
: []}
|
||||
{#each filter.options as option (filter.title + option.value + option.label)}
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Button
|
||||
on:click={() => {
|
||||
if (isArray) {
|
||||
const exists =
|
||||
selectedArray.includes(
|
||||
option.value
|
||||
);
|
||||
const next = exists
|
||||
? selectedArray.filter(
|
||||
(v) =>
|
||||
v !== option.value
|
||||
)
|
||||
: [
|
||||
...selectedArray,
|
||||
option.value
|
||||
];
|
||||
addFilterAndApply(
|
||||
filter.id,
|
||||
filter.title,
|
||||
filter.operator,
|
||||
null,
|
||||
next,
|
||||
$columns,
|
||||
analyticsSource
|
||||
);
|
||||
} else {
|
||||
addFilterAndApply(
|
||||
filter.id,
|
||||
filter.title,
|
||||
filter.operator,
|
||||
option.value,
|
||||
[],
|
||||
$columns,
|
||||
analyticsSource
|
||||
);
|
||||
}
|
||||
}}>
|
||||
<Layout.Stack direction="row" gap="s">
|
||||
{#if isArray}
|
||||
<Selector.Checkbox
|
||||
checked={selectedArray.includes(
|
||||
option.value
|
||||
)}
|
||||
size="s" />
|
||||
{/if}
|
||||
{capitalize(option.label)}
|
||||
</Layout.Stack>
|
||||
</ActionMenu.Item.Button>
|
||||
</ActionMenu.Root>
|
||||
{/each}
|
||||
{/if}
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Menu>
|
||||
</CompoundTagChild>
|
||||
{/each}
|
||||
<CompoundTagChild
|
||||
dismiss
|
||||
on:click={() => {
|
||||
const t = $tags.filter((t) =>
|
||||
t.tag.includes((tag as ParsedTag).title)
|
||||
);
|
||||
t.forEach((t) => (t ? queries.removeFilter(t) : null));
|
||||
queries.apply();
|
||||
parsedTags.update((tags) => tags.filter((t) => t.tag !== tag.tag));
|
||||
}}>
|
||||
<Icon icon={IconX} size="s" />
|
||||
</CompoundTagChild>
|
||||
</CompoundTagRoot>
|
||||
<span slot="tooltip">{tag?.value?.toString()}</span>
|
||||
</Tooltip>
|
||||
</span>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<!-- Always render remaining placeholder tags alongside active tags -->
|
||||
{#if placeholders?.length}
|
||||
{#each placeholders as filter (filter.title + filter.id)}
|
||||
<span>
|
||||
<Menu>
|
||||
<CompoundTagRoot size="s">
|
||||
<CompoundTagChild>
|
||||
<span>{capitalize(filter.title)}</span>
|
||||
</CompoundTagChild>
|
||||
<CompoundTagChild
|
||||
dismiss
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
if (!hiddenPlaceholders.includes(filter.title)) {
|
||||
hiddenPlaceholders = [...hiddenPlaceholders, filter.title];
|
||||
}
|
||||
}}>
|
||||
<Icon icon={IconX} size="s" />
|
||||
</CompoundTagChild>
|
||||
</CompoundTagRoot>
|
||||
<svelte:fragment slot="menu">
|
||||
{#if filter.options}
|
||||
{#each filter.options as option (filter.title + option.value + option.label)}
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Button
|
||||
on:click={() => {
|
||||
addFilterAndApply(
|
||||
filter.id,
|
||||
filter.title,
|
||||
filter.operator,
|
||||
filter?.array ? null : option.value,
|
||||
filter?.array ? [option.value] : [],
|
||||
$columns,
|
||||
analyticsSource
|
||||
);
|
||||
}}>
|
||||
{capitalize(option.label)}
|
||||
</ActionMenu.Item.Button>
|
||||
</ActionMenu.Root>
|
||||
{/each}
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Menu>
|
||||
</span>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#if $parsedTags?.length}
|
||||
<Button
|
||||
size="s"
|
||||
text
|
||||
@@ -38,5 +249,9 @@
|
||||
queries.apply();
|
||||
parsedTags.set([]);
|
||||
}}>Clear all</Button>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if filterCols?.length && !$isSmallViewport}
|
||||
<QuickFilters {columns} {analyticsSource} {filterCols} />
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
@@ -13,19 +13,36 @@
|
||||
let {
|
||||
columns,
|
||||
filterCols,
|
||||
analyticsSource
|
||||
analyticsSource,
|
||||
buttonVariant = 'ghost'
|
||||
}: {
|
||||
columns: Writable<Column[]>;
|
||||
filterCols: FilterData[];
|
||||
analyticsSource?: string;
|
||||
buttonVariant?: 'ghost' | 'secondary';
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<Menu>
|
||||
<Button secondary badge={$parsedTags?.length ? `${$parsedTags.length}` : undefined}>
|
||||
<Icon icon={IconFilterLine} slot="start" size="s" />
|
||||
Filters
|
||||
</Button>
|
||||
{#if buttonVariant === 'secondary'}
|
||||
<Button
|
||||
ariaLabel="Filters"
|
||||
secondary
|
||||
size="s"
|
||||
badge={$parsedTags?.length ? `${$parsedTags.length}` : undefined}>
|
||||
<Icon icon={IconFilterLine} size="s" slot="start" />
|
||||
<span class="text">Filters</span>
|
||||
</Button>
|
||||
{:else}
|
||||
<Button
|
||||
ariaLabel="Filters"
|
||||
text
|
||||
icon
|
||||
size="s"
|
||||
badge={$parsedTags?.length ? `${$parsedTags.length}` : undefined}>
|
||||
<Icon icon={IconFilterLine} size="s" />
|
||||
</Button>
|
||||
{/if}
|
||||
<svelte:fragment slot="menu">
|
||||
{#each filterCols.filter((f) => f?.options) as filter (filter.title + filter.id)}
|
||||
{#if filter.options}
|
||||
|
||||
@@ -3,15 +3,21 @@ import { get, writable } from 'svelte/store';
|
||||
import { type FilterData } from './quickFilters';
|
||||
import { tags, type TagValue } from './store';
|
||||
|
||||
export const parsedTags = writable<TagValue[]>([]);
|
||||
export type ParsedTag = TagValue & {
|
||||
title: string;
|
||||
};
|
||||
|
||||
export const parsedTags = writable<ParsedTag[]>([]);
|
||||
|
||||
export function setFilters(localTags: TagValue[], filterCols: FilterData[], $columns: Column[]) {
|
||||
if (!localTags?.length) {
|
||||
parsedTags.set([]);
|
||||
filterCols.forEach((filter) => {
|
||||
resetOptions(filter);
|
||||
cleanOldTags(filter?.title);
|
||||
});
|
||||
} else {
|
||||
parsedTags.set([]);
|
||||
filterCols.forEach((filter) => {
|
||||
const id = filter?.id?.toLowerCase();
|
||||
if (id?.includes('duration')) {
|
||||
@@ -47,9 +53,10 @@ export function setFilterData(filter: FilterData) {
|
||||
});
|
||||
}
|
||||
cleanOldTags(filter?.title);
|
||||
const newTag = {
|
||||
const newTag: ParsedTag = {
|
||||
tag: tagData.tag.replace(',', ' or '),
|
||||
value: tagData.value
|
||||
value: tagData.value,
|
||||
title: filter.title
|
||||
};
|
||||
|
||||
parsedTags.update((tags) => {
|
||||
@@ -69,9 +76,10 @@ export function setTimeFilter(filter: FilterData, columns: Column[]) {
|
||||
const ranges = col.elements as { value: string; label: string }[];
|
||||
const timeRange = ranges.find((range) => range.value === timeTag.value);
|
||||
if (timeRange) {
|
||||
const newTag = {
|
||||
const newTag: ParsedTag = {
|
||||
tag: `**${filter.title}** is **${timeRange.label}**`,
|
||||
value: timeRange.value
|
||||
value: timeRange.value,
|
||||
title: filter.title
|
||||
};
|
||||
|
||||
cleanOldTags(filter?.title);
|
||||
@@ -102,9 +110,10 @@ export function setSizeFilter(filter: FilterData, columns: Column[]) {
|
||||
if (sizeRange) {
|
||||
cleanOldTags(filter?.title);
|
||||
|
||||
const newTag = {
|
||||
const newTag: ParsedTag = {
|
||||
tag: `**${filter.title}** is **${sizeRange.label}**`,
|
||||
value: sizeTag.value
|
||||
value: sizeTag.value,
|
||||
title: filter.title
|
||||
};
|
||||
parsedTags.update((tags) => {
|
||||
tags.push(newTag);
|
||||
@@ -126,9 +135,10 @@ export function setStatusCodeFilter(filter: FilterData, columns: Column[]) {
|
||||
const codeRange = ranges.find((c) => c?.value && c.value === statusCodeTag.value);
|
||||
if (codeRange) {
|
||||
cleanOldTags(filter?.title);
|
||||
const newTag = {
|
||||
const newTag: ParsedTag = {
|
||||
tag: `**${filter.title}** is **${codeRange.label}**`,
|
||||
value: statusCodeTag.value
|
||||
value: statusCodeTag.value,
|
||||
title: filter.title
|
||||
};
|
||||
parsedTags.update((tags) => {
|
||||
tags.push(newTag);
|
||||
@@ -156,9 +166,10 @@ export function setDateFilter(filter: FilterData, columns: Column[]) {
|
||||
});
|
||||
if (dateRange) {
|
||||
cleanOldTags(filter?.title);
|
||||
const newTag = {
|
||||
const newTag: ParsedTag = {
|
||||
tag: `**${filter.title}** is **${dateRange.label}**`,
|
||||
value: dateTag.value
|
||||
value: dateTag.value,
|
||||
title: filter.title
|
||||
};
|
||||
parsedTags.update((tags) => {
|
||||
tags.push(newTag);
|
||||
|
||||
@@ -156,6 +156,10 @@ export enum ValidOperators {
|
||||
|
||||
export enum ValidTypes {
|
||||
String = 'string',
|
||||
Varchar = 'varchar',
|
||||
Text = 'text',
|
||||
Mediumtext = 'mediumtext',
|
||||
Longtext = 'longtext',
|
||||
Integer = 'integer',
|
||||
Double = 'double',
|
||||
Boolean = 'boolean',
|
||||
@@ -175,8 +179,32 @@ const operatorsDefault = new Map<
|
||||
hideInput?: boolean;
|
||||
}
|
||||
>([
|
||||
[ValidOperators.StartsWith, { query: Query.startsWith, types: [ValidTypes.String] }],
|
||||
[ValidOperators.EndsWith, { query: Query.endsWith, types: [ValidTypes.String] }],
|
||||
[
|
||||
ValidOperators.StartsWith,
|
||||
{
|
||||
query: Query.startsWith,
|
||||
types: [
|
||||
ValidTypes.String,
|
||||
ValidTypes.Varchar,
|
||||
ValidTypes.Text,
|
||||
ValidTypes.Mediumtext,
|
||||
ValidTypes.Longtext
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
ValidOperators.EndsWith,
|
||||
{
|
||||
query: Query.endsWith,
|
||||
types: [
|
||||
ValidTypes.String,
|
||||
ValidTypes.Varchar,
|
||||
ValidTypes.Text,
|
||||
ValidTypes.Mediumtext,
|
||||
ValidTypes.Longtext
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
ValidOperators.GreaterThan,
|
||||
{
|
||||
@@ -211,6 +239,10 @@ const operatorsDefault = new Map<
|
||||
query: Query.equal,
|
||||
types: [
|
||||
ValidTypes.String,
|
||||
ValidTypes.Varchar,
|
||||
ValidTypes.Text,
|
||||
ValidTypes.Mediumtext,
|
||||
ValidTypes.Longtext,
|
||||
ValidTypes.Integer,
|
||||
ValidTypes.Double,
|
||||
ValidTypes.Boolean,
|
||||
@@ -227,6 +259,10 @@ const operatorsDefault = new Map<
|
||||
query: Query.notEqual,
|
||||
types: [
|
||||
ValidTypes.String,
|
||||
ValidTypes.Varchar,
|
||||
ValidTypes.Text,
|
||||
ValidTypes.Mediumtext,
|
||||
ValidTypes.Longtext,
|
||||
ValidTypes.Integer,
|
||||
ValidTypes.Double,
|
||||
ValidTypes.Boolean,
|
||||
@@ -242,6 +278,10 @@ const operatorsDefault = new Map<
|
||||
query: Query.isNotNull,
|
||||
types: [
|
||||
ValidTypes.String,
|
||||
ValidTypes.Varchar,
|
||||
ValidTypes.Text,
|
||||
ValidTypes.Mediumtext,
|
||||
ValidTypes.Longtext,
|
||||
ValidTypes.Integer,
|
||||
ValidTypes.Double,
|
||||
ValidTypes.Boolean,
|
||||
@@ -260,6 +300,10 @@ const operatorsDefault = new Map<
|
||||
query: Query.isNull,
|
||||
types: [
|
||||
ValidTypes.String,
|
||||
ValidTypes.Varchar,
|
||||
ValidTypes.Text,
|
||||
ValidTypes.Mediumtext,
|
||||
ValidTypes.Longtext,
|
||||
ValidTypes.Integer,
|
||||
ValidTypes.Double,
|
||||
ValidTypes.Boolean,
|
||||
@@ -278,6 +322,10 @@ const operatorsDefault = new Map<
|
||||
query: Query.contains,
|
||||
types: [
|
||||
ValidTypes.String,
|
||||
ValidTypes.Varchar,
|
||||
ValidTypes.Text,
|
||||
ValidTypes.Mediumtext,
|
||||
ValidTypes.Longtext,
|
||||
ValidTypes.Integer,
|
||||
ValidTypes.Double,
|
||||
ValidTypes.Boolean,
|
||||
|
||||
@@ -152,7 +152,13 @@
|
||||
<span slot="description">
|
||||
Select the directory where your site code is located using the menu below.
|
||||
</span>
|
||||
<DirectoryPicker {directories} {isLoading} on:select={fetchContents} bind:expanded />
|
||||
<DirectoryPicker
|
||||
{directories}
|
||||
{isLoading}
|
||||
bind:expanded
|
||||
selected={currentPath}
|
||||
openTo={currentPath}
|
||||
on:select={fetchContents} />
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button secondary on:click={() => (show = false)}>Cancel</Button>
|
||||
|
||||
@@ -9,19 +9,31 @@
|
||||
*/
|
||||
export let maxSize: $$Props['maxSize'];
|
||||
|
||||
/**
|
||||
* Hides empty segments where size is <= 0;
|
||||
*/
|
||||
export let hideEmptySegments: $$Props['hideEmptySegments'] = false;
|
||||
|
||||
/**
|
||||
* The data for the progressbar
|
||||
*/
|
||||
export let data: $$Props['data'];
|
||||
|
||||
$: segments = hideEmptySegments ? data.filter((item) => item.size > 0) : data;
|
||||
|
||||
/**
|
||||
* The remaining value of the progressbar
|
||||
*/
|
||||
$: remainder = data.reduce((sum: number, item: ProgressbarData) => sum - item.size, maxSize);
|
||||
$: remainder = segments.reduce(
|
||||
(sum: number, item: ProgressbarData) => sum - item.size,
|
||||
maxSize
|
||||
);
|
||||
|
||||
$: segmentGap = segments.length > 1 ? '2px' : '0px';
|
||||
</script>
|
||||
|
||||
<section class="progressbar__container">
|
||||
{#each $$props.data as item}
|
||||
<section class="progressbar__container" style:--progressbar-segment-gap={segmentGap}>
|
||||
{#each segments as item}
|
||||
<Tooltip disabled={!item.tooltip} maxWidth="max-content">
|
||||
<div
|
||||
class="progressbar__content"
|
||||
@@ -64,7 +76,7 @@
|
||||
border-radius: var(--progressbar-border-radius);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 2px;
|
||||
gap: var(--progressbar-segment-gap, 2px);
|
||||
margin-top: 1rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ export type ProgressbarData = {
|
||||
|
||||
export type ProgressbarProps = {
|
||||
maxSize: number;
|
||||
hideEmptySegments?: boolean;
|
||||
data: Array<ProgressbarData>;
|
||||
};
|
||||
|
||||
|
||||
@@ -183,7 +183,8 @@ function generateValueForField(
|
||||
|
||||
function generateSingleValue(field: Field): string | number | boolean | NestedNumberArray | null {
|
||||
switch (field.type) {
|
||||
case 'string': {
|
||||
case 'string':
|
||||
case 'varchar': {
|
||||
if ('format' in field && field.format) {
|
||||
switch (field.format) {
|
||||
case 'email': {
|
||||
@@ -214,6 +215,21 @@ function generateSingleValue(field: Field): string | number | boolean | NestedNu
|
||||
}
|
||||
}
|
||||
|
||||
case 'text': {
|
||||
// TEXT type max size is 16,383 characters (utf8mb4)
|
||||
return generateStringValue(field.key, 1000);
|
||||
}
|
||||
|
||||
case 'mediumtext': {
|
||||
// MEDIUMTEXT type max size is 4,194,303 characters (utf8mb4)
|
||||
return generateStringValue(field.key, 1000);
|
||||
}
|
||||
|
||||
case 'longtext': {
|
||||
// LONGTEXT type max size is 1,073,741,823 characters (utf8mb4)
|
||||
return generateStringValue(field.key, 1000);
|
||||
}
|
||||
|
||||
case 'integer': {
|
||||
const intAttr = field as Models.ColumnInteger;
|
||||
const minCompat = coerceToNumber(intAttr.min);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
import { deepClone, objectEntries } from './object';
|
||||
import { objectEntries } from './object';
|
||||
|
||||
/**
|
||||
* Given an object `obj`, returns an object of writable stores for each key in that object.
|
||||
@@ -16,7 +16,7 @@ export function createConservative<Obj extends Record<string, unknown>>(obj: Obj
|
||||
[K in keyof Obj]: Writable<Obj[K]>;
|
||||
};
|
||||
|
||||
const history = deepClone(obj);
|
||||
const history = structuredClone(obj);
|
||||
|
||||
function listen(input: Obj) {
|
||||
objectEntries(input).forEach(([key, value]) => {
|
||||
|
||||
@@ -30,6 +30,10 @@ export type Prettify<T> = T & {};
|
||||
|
||||
const columnTypes = [
|
||||
'string',
|
||||
'varchar',
|
||||
'text',
|
||||
'mediumtext',
|
||||
'longtext',
|
||||
'integer',
|
||||
'double',
|
||||
'boolean',
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
expanded={databasesScreen && !insideSideSheet}
|
||||
slotSpacing={databasesScreen && !insideSideSheet}>
|
||||
{#if logs.total}
|
||||
<Table.Root {columns} let:root>
|
||||
<Table.Root class="responsive-table" {columns} let:root>
|
||||
<svelte:fragment slot="header" let:root>
|
||||
<Table.Header.Cell column="user" {root}>User</Table.Header.Cell>
|
||||
<Table.Header.Cell column="event" {root}>Event</Table.Header.Cell>
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { SearchQuery, ViewSelector } from '$lib/components';
|
||||
import {
|
||||
FiltersBottomSheet,
|
||||
ParsedTagList,
|
||||
queryParamToMap,
|
||||
Filters
|
||||
} from '$lib/components/filters';
|
||||
import { FiltersBottomSheet, ParsedTagList, queryParamToMap } from '$lib/components/filters';
|
||||
import QuickFilters from '$lib/components/filters/quickFilters.svelte';
|
||||
|
||||
import Button from '$lib/elements/forms/button.svelte';
|
||||
import { View } from '$lib/helpers/load';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
@@ -18,7 +14,7 @@
|
||||
import DisplaySettingsModal from './displaySettingsModal.svelte';
|
||||
import { buildFilterCol } from '$lib/components/filters/quickFilters';
|
||||
import { afterNavigate } from '$app/navigation';
|
||||
import { setFilters } from '$lib/components/filters/setFilters';
|
||||
import { parsedTags, setFilters } from '$lib/components/filters/setFilters';
|
||||
|
||||
let {
|
||||
columns,
|
||||
@@ -28,7 +24,7 @@
|
||||
hasSearch = false,
|
||||
searchPlaceholder = 'Search by ID',
|
||||
hasFilters = false,
|
||||
hasCustomFiltersOnly = false,
|
||||
filtersStyle = 'chips',
|
||||
analyticsSource = '',
|
||||
children
|
||||
}: {
|
||||
@@ -39,7 +35,7 @@
|
||||
hasSearch?: boolean;
|
||||
searchPlaceholder?: string;
|
||||
hasFilters?: boolean;
|
||||
hasCustomFiltersOnly?: boolean;
|
||||
filtersStyle?: 'chips' | 'dropdown';
|
||||
analyticsSource?: string;
|
||||
children?: Snippet;
|
||||
} = $props();
|
||||
@@ -52,9 +48,12 @@
|
||||
let showDisplaySettingsModal = $state(false);
|
||||
let showFilters = $state(false);
|
||||
|
||||
let filterCols = $derived(
|
||||
const filterCols = $derived(
|
||||
$columns.map((col) => (col.filter !== false ? buildFilterCol(col) : null)).filter(Boolean)
|
||||
);
|
||||
const filtersBadge = $derived(
|
||||
filtersStyle === 'dropdown' && $parsedTags?.length ? `${$parsedTags.length}` : undefined
|
||||
);
|
||||
|
||||
afterNavigate((p) => {
|
||||
if (!hasFilters) return;
|
||||
@@ -62,7 +61,6 @@
|
||||
const localQueries = queryParamToMap(paramQueries || '[]');
|
||||
const localTags = Array.from(localQueries.keys());
|
||||
setFilters(localTags, filterCols, $columns);
|
||||
filterCols = filterCols;
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -106,21 +104,47 @@
|
||||
{#if showSearch && hasSearch}
|
||||
<SearchQuery placeholder={searchPlaceholder} />
|
||||
{/if}
|
||||
{#if hasFilters && filtersStyle === 'chips'}
|
||||
<div style="overflow-x: auto;">
|
||||
<ParsedTagList {columns} {analyticsSource} />
|
||||
</div>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{:else}
|
||||
<Layout.Stack direction="row" justifyContent="space-between">
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<Layout.Stack direction="row" justifyContent="space-between" alignItems="flex-start">
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
alignItems="flex-start"
|
||||
gap="m"
|
||||
style="min-width: 0; flex: 1 1 auto;">
|
||||
{#if hasSearch}
|
||||
<SearchQuery placeholder={searchPlaceholder} />
|
||||
<div style="flex: 0 0 auto; max-width: 360px; min-width: 0;">
|
||||
<SearchQuery placeholder={searchPlaceholder} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if hasFilters && filtersStyle === 'chips'}
|
||||
<!-- Tags with Filters button (rendered inside ParsedTagList) -->
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap="s"
|
||||
wrap="wrap"
|
||||
style="min-width: 0; flex: 1 1 auto;">
|
||||
<ParsedTagList {columns} {analyticsSource} />
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
{#if hasFilters && $columns?.length}
|
||||
{#if hasCustomFiltersOnly}
|
||||
<Filters query="[]" {columns} {analyticsSource} />
|
||||
{:else}
|
||||
<QuickFilters {columns} {analyticsSource} {filterCols} />
|
||||
{/if}
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="flex-end"
|
||||
style="align-self: flex-start; white-space: nowrap;">
|
||||
{#if hasFilters && filtersStyle === 'dropdown'}
|
||||
<QuickFilters
|
||||
{columns}
|
||||
{analyticsSource}
|
||||
buttonVariant="secondary"
|
||||
filterCols={filterCols.filter((f) => f?.options)} />
|
||||
{/if}
|
||||
{#if hasDisplaySettings}
|
||||
<ViewSelector ui="new" {view} {columns} {hideView} {hideColumns} />
|
||||
@@ -131,7 +155,6 @@
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
<ParsedTagList />
|
||||
</Layout.Stack>
|
||||
</header>
|
||||
|
||||
@@ -171,7 +194,12 @@
|
||||
{/snippet}
|
||||
|
||||
{#snippet filtersButton(icon = false)}
|
||||
<Button ariaLabel="Filters" on:click={() => (showFilters = !showFilters)} secondary {icon}>
|
||||
<Button
|
||||
ariaLabel="Filters"
|
||||
on:click={() => (showFilters = !showFilters)}
|
||||
text
|
||||
{icon}
|
||||
badge={filtersBadge}>
|
||||
<Icon icon={IconFilterLine} />
|
||||
</Button>
|
||||
{/snippet}
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
import { activeHeaderAlert } from '$routes/(console)/store';
|
||||
import { onMount, setContext } from 'svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
import { showSubNavigation } from '$lib/stores/layout';
|
||||
import { showSubNavigation, showOnboardingAnimation } from '$lib/stores/layout';
|
||||
import { organization, organizationList } from '$lib/stores/organization';
|
||||
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { user } from '$lib/stores/user';
|
||||
import { isCloud } from '$lib/system';
|
||||
@@ -176,7 +177,8 @@
|
||||
|
||||
$: subNavigation = $page.data.subNavigation;
|
||||
|
||||
$: shouldRenderSidebar = !$isNewWizardStatusOpen && showSideNavigation;
|
||||
$: shouldRenderSidebar =
|
||||
!$isNewWizardStatusOpen && showSideNavigation && !$showOnboardingAnimation;
|
||||
$: hasSidebarSpace = shouldRenderSidebar && !$isTabletViewport && !!selectedProject;
|
||||
|
||||
$: {
|
||||
@@ -203,9 +205,9 @@
|
||||
class:is-open={$showSubNavigation}
|
||||
class:u-hide={$wizard.show || $wizard.cover}
|
||||
class:is-fixed-layout={$activeHeaderAlert?.show}
|
||||
class:no-header={!showHeader}
|
||||
class:no-header={!showHeader || $showOnboardingAnimation}
|
||||
style:--p-side-size={sideSize}>
|
||||
{#if showHeader}
|
||||
{#if showHeader && !$showOnboardingAnimation}
|
||||
<Navbar {...navbarProps} bind:sideBarIsOpen={$isSidebarOpen} bind:showAccountMenu />
|
||||
{/if}
|
||||
|
||||
@@ -220,7 +222,9 @@
|
||||
bind:state />
|
||||
{/if}
|
||||
|
||||
<SideNavigation bind:subNavigation />
|
||||
{#if !$showOnboardingAnimation}
|
||||
<SideNavigation bind:subNavigation />
|
||||
{/if}
|
||||
|
||||
<div
|
||||
class="content"
|
||||
|
||||
@@ -27,3 +27,4 @@ export function updateLayout(args: updateLayoutArguments) {
|
||||
}
|
||||
|
||||
export const showSubNavigation = writable(false);
|
||||
export const showOnboardingAnimation = writable(false);
|
||||
|
||||
+46
-61
@@ -1,5 +1,5 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
import { Resources } from '@appwrite.io/console';
|
||||
import { includesAll } from '$lib/helpers/array';
|
||||
|
||||
const initialFormData = {
|
||||
@@ -51,66 +51,66 @@ export const ResourcesFriendly = {
|
||||
row: { singular: 'Row', plural: 'Rows' }
|
||||
};
|
||||
|
||||
const resources = Object.keys(ResourcesFriendly);
|
||||
|
||||
type Resource = (typeof resources)[number];
|
||||
|
||||
// @todo: @itznotabug - check if other resources are correct and work fine!
|
||||
export const providerResources: Record<Provider, Resource[]> = {
|
||||
appwrite: [...resources], // new terminology, others are ok?
|
||||
export const providerResources: Record<Provider, Resources[]> = {
|
||||
appwrite: Object.values(Resources),
|
||||
supabase: [
|
||||
'user',
|
||||
'database',
|
||||
'collection',
|
||||
'attribute',
|
||||
'index',
|
||||
'document',
|
||||
'bucket',
|
||||
'file'
|
||||
Resources.User,
|
||||
Resources.Database,
|
||||
Resources.Collection,
|
||||
Resources.Attribute,
|
||||
Resources.Index,
|
||||
Resources.Document,
|
||||
Resources.Bucket,
|
||||
Resources.File
|
||||
],
|
||||
nhost: ['user', 'database', 'collection', 'attribute', 'index', 'document', 'bucket', 'file'],
|
||||
firebase: ['user', 'database', 'collection', 'attribute', 'document', 'bucket', 'file']
|
||||
nhost: [
|
||||
Resources.User,
|
||||
Resources.Database,
|
||||
Resources.Collection,
|
||||
Resources.Attribute,
|
||||
Resources.Index,
|
||||
Resources.Document,
|
||||
Resources.Bucket,
|
||||
Resources.File
|
||||
],
|
||||
firebase: [
|
||||
Resources.User,
|
||||
Resources.Database,
|
||||
Resources.Collection,
|
||||
Resources.Attribute,
|
||||
Resources.Document,
|
||||
Resources.Bucket,
|
||||
Resources.File
|
||||
]
|
||||
};
|
||||
|
||||
export const migrationFormToResources = (
|
||||
formData: MigrationFormData,
|
||||
provider: Provider
|
||||
): Resource[] => {
|
||||
const resources: Resource[] = [];
|
||||
const addResource = (resource: Resource) => {
|
||||
): Resources[] => {
|
||||
const resources: Resources[] = [];
|
||||
const addResource = (resource: Resources) => {
|
||||
if (providerResources[provider].includes(resource)) {
|
||||
resources.push(resource);
|
||||
}
|
||||
};
|
||||
|
||||
if (formData.users.root) {
|
||||
addResource('user');
|
||||
}
|
||||
if (formData.users.teams) {
|
||||
addResource('team');
|
||||
addResource('membership');
|
||||
addResource(Resources.User);
|
||||
}
|
||||
if (formData.databases.root) {
|
||||
addResource('database');
|
||||
addResource('table');
|
||||
addResource('column');
|
||||
addResource('columnIndex');
|
||||
addResource(Resources.Database);
|
||||
addResource(Resources.Table);
|
||||
addResource(Resources.Column);
|
||||
addResource(Resources.Index);
|
||||
}
|
||||
if (formData.databases.rows) {
|
||||
addResource('row');
|
||||
}
|
||||
if (formData.functions.root) {
|
||||
addResource('function');
|
||||
}
|
||||
if (formData.functions.env) {
|
||||
addResource('environment-variable');
|
||||
}
|
||||
if (formData.functions.inactive) {
|
||||
addResource('deployment');
|
||||
addResource(Resources.Row);
|
||||
}
|
||||
if (formData.storage.root) {
|
||||
addResource('bucket');
|
||||
addResource('file');
|
||||
addResource(Resources.Bucket);
|
||||
addResource(Resources.File);
|
||||
}
|
||||
|
||||
return resources;
|
||||
@@ -137,33 +137,18 @@ export const isVersionAtLeast = (version: string, atLeast: string) => {
|
||||
return compareVersions(version, atLeast) >= 0;
|
||||
};
|
||||
|
||||
export const resourcesToMigrationForm = (
|
||||
resources: Resource[],
|
||||
version = '0.0.0'
|
||||
): MigrationFormData => {
|
||||
export const resourcesToMigrationForm = (resources: Resources[]): MigrationFormData => {
|
||||
const formData = { ...initialFormData };
|
||||
if (resources.includes('user')) {
|
||||
if (resources.includes(Resources.User)) {
|
||||
formData.users.root = true;
|
||||
}
|
||||
if (includesAll(resources, ['team', 'membership'])) {
|
||||
formData.users.teams = true;
|
||||
}
|
||||
if (resources.includes('database')) {
|
||||
if (resources.includes(Resources.Database)) {
|
||||
formData.databases.root = true;
|
||||
}
|
||||
if (includesAll(resources, ['table', 'column', 'row'])) {
|
||||
if (includesAll(resources, [Resources.Table, Resources.Column, Resources.Row] as Resources[])) {
|
||||
formData.databases.rows = true;
|
||||
}
|
||||
if (resources.includes('function') && isVersionAtLeast(version, '1.4.0')) {
|
||||
formData.functions.root = true;
|
||||
}
|
||||
if (resources.includes('environment-variable') && isVersionAtLeast(version, '1.4.0')) {
|
||||
formData.functions.env = true;
|
||||
}
|
||||
if (resources.includes('deployment') && isVersionAtLeast(version, '1.4.0')) {
|
||||
formData.functions.inactive = true;
|
||||
}
|
||||
if (includesAll(resources, ['bucket', 'file'])) {
|
||||
if (includesAll(resources, [Resources.Bucket, Resources.File] as Resources[])) {
|
||||
formData.storage.root = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,14 +5,13 @@
|
||||
import {
|
||||
createMigrationFormStore,
|
||||
createMigrationProviderStore,
|
||||
isVersionAtLeast,
|
||||
type MigrationFormData,
|
||||
providerResources,
|
||||
resourcesToMigrationForm
|
||||
} from '$lib/stores/migration';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { Resources, type Models } from '@appwrite.io/console';
|
||||
import type { sdk } from '$lib/stores/sdk';
|
||||
import ImportReport from '$routes/(console)/project-[region]-[project]/settings/migrations/(import)/importReport.svelte';
|
||||
|
||||
@@ -33,11 +32,9 @@
|
||||
}
|
||||
|
||||
function selectAll() {
|
||||
$formData = resourcesToMigrationForm(resources, version);
|
||||
$formData = resourcesToMigrationForm(resources);
|
||||
}
|
||||
|
||||
$: version = report?.version || '0.0.0';
|
||||
|
||||
let error = false;
|
||||
let isOpen = false;
|
||||
let report: Models.MigrationReport;
|
||||
@@ -98,14 +95,21 @@
|
||||
|
||||
const shouldRenderGroup = (groupKey: string): boolean => {
|
||||
if (groupKey === 'functions') {
|
||||
return resources.includes('function') && isVersionAtLeast(version, '1.4.0');
|
||||
// Functions not in SDK Resources enum, skip
|
||||
return false;
|
||||
}
|
||||
|
||||
if (groupKey === 'storage') {
|
||||
return resources.includes('bucket') && resources.includes('file');
|
||||
return resources.includes(Resources.Bucket) && resources.includes(Resources.File);
|
||||
}
|
||||
|
||||
return resources.includes(groupKey.slice(0, -1));
|
||||
// Map groupKey to Resources enum
|
||||
const groupToResource: Record<string, Resources> = {
|
||||
users: Resources.User,
|
||||
databases: Resources.Database
|
||||
};
|
||||
const resource = groupToResource[groupKey];
|
||||
return resource ? resources.includes(resource) : false;
|
||||
};
|
||||
|
||||
// no typecasting in svelte context!
|
||||
@@ -125,14 +129,6 @@
|
||||
</script>
|
||||
|
||||
<Layout.Stack gap="l">
|
||||
{#if report && !isVersionAtLeast(version, '1.4.0') && $provider.provider === 'appwrite'}
|
||||
<Alert.Inline status="warning">
|
||||
<svelte:fragment slot="title">Functions not available for import</svelte:fragment>
|
||||
To migrate your functions, update the version of the Appwrite instance you're importing from
|
||||
to a version newer than 1.4
|
||||
</Alert.Inline>
|
||||
{/if}
|
||||
|
||||
{#if error}
|
||||
<Alert.Inline status="error" title="Couldn’t load resources">
|
||||
{#if migrationType === 'provider'}
|
||||
|
||||
@@ -3,6 +3,7 @@ import Breadcrumbs from './breadcrumbs.svelte';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Platform, Query } from '@appwrite.io/console';
|
||||
|
||||
export const load: LayoutLoad = async ({ depends }) => {
|
||||
depends(Dependencies.FACTORS);
|
||||
@@ -10,7 +11,9 @@ export const load: LayoutLoad = async ({ depends }) => {
|
||||
|
||||
const [factors, identities] = await Promise.all([
|
||||
sdk.forConsole.account.listMFAFactors(),
|
||||
sdk.forConsole.account.listIdentities()
|
||||
sdk.forConsole.account.listIdentities({
|
||||
queries: [Query.notContains('provider', Platform.Imagine)]
|
||||
})
|
||||
]);
|
||||
|
||||
return {
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
</Layout.Stack>
|
||||
|
||||
<Table.Root
|
||||
class="responsive-table"
|
||||
let:root
|
||||
columns={[
|
||||
{ id: 'client', width: { min: 450 } },
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { base, resolve } from '$app/paths';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import CreateProject from '$lib/layout/createProject.svelte';
|
||||
import { loadAvailableRegions } from '$routes/(console)/regions';
|
||||
import { regions as regionsStore } from '$lib/stores/organization';
|
||||
import { user } from '$lib/stores/user';
|
||||
import { showOnboardingAnimation } from '$lib/stores/layout';
|
||||
|
||||
let isLoading = false;
|
||||
let startAnimation = false;
|
||||
@@ -57,10 +58,17 @@
|
||||
});
|
||||
|
||||
startAnimation = true;
|
||||
showOnboardingAnimation.set(true);
|
||||
|
||||
setTimeout(async () => {
|
||||
await invalidate(Dependencies.ACCOUNT);
|
||||
goto(`${base}/project-${project.region ?? 'default'}-${project.$id}`);
|
||||
await goto(
|
||||
resolve('/(console)/project-[region]-[project]', {
|
||||
region: project.region ?? 'default',
|
||||
project: project.$id
|
||||
})
|
||||
);
|
||||
showOnboardingAnimation.set(false);
|
||||
}, 3000);
|
||||
} catch (e) {
|
||||
trackError(e, Submit.ProjectCreate);
|
||||
|
||||
@@ -169,11 +169,11 @@
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
{#if isCloud && data.currentPlan?.projects !== 0 && activeProjectsTotal <= data.currentPlan.projects && !freePlanAlertDismissed}
|
||||
{#if isCloud && !data.program && data.currentPlan?.projects && activeProjectsTotal <= data.currentPlan.projects && !freePlanAlertDismissed}
|
||||
<Alert.Inline dismissible on:dismiss={dismissFreePlanAlert}>
|
||||
<Typography.Text
|
||||
>Your Free plan includes up to 2 projects and limited resources. Upgrade to unlock
|
||||
more capacity and features.</Typography.Text>
|
||||
>Your Free plan includes up to {data.currentPlan?.projects} projects and limited resources.
|
||||
Upgrade to unlock more capacity and features.</Typography.Text>
|
||||
<svelte:fragment slot="actions">
|
||||
<Button
|
||||
compact
|
||||
|
||||
@@ -151,11 +151,17 @@
|
||||
}
|
||||
|
||||
async function trackDowngradeFeedback() {
|
||||
const selectedReason = feedbackDowngradeOptions.find(
|
||||
(option) => option.value === feedbackDowngradeReason
|
||||
)?.label;
|
||||
|
||||
const defaultReason = feedbackDowngradeOptions.find(
|
||||
(option) => option.value === 'other'
|
||||
)?.label;
|
||||
|
||||
await sdk.forConsole.organizations.createDowngradeFeedback({
|
||||
organizationId: data.organization.$id,
|
||||
reason: feedbackDowngradeOptions.find(
|
||||
(option) => option.value === feedbackDowngradeReason
|
||||
)?.label,
|
||||
reason: selectedReason ?? defaultReason,
|
||||
message: feedbackMessage ?? '',
|
||||
fromPlanId: data.organization.billingPlanId,
|
||||
toPlanId: selectedPlan.$id
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<ResponsiveContainerHeader
|
||||
hasSearch
|
||||
{columns}
|
||||
bind:view={data.view}
|
||||
view={data.view}
|
||||
searchPlaceholder="Search by name or ID">
|
||||
{#if $canWriteDatabases}
|
||||
<Tooltip disabled={!isLimited}>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { Button, InputText } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { ID, type Models } from '@appwrite.io/console';
|
||||
import { BackupServices, ID, type Models } from '@appwrite.io/console';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { currentPlan } from '$lib/stores/organization';
|
||||
@@ -65,7 +65,7 @@
|
||||
|
||||
return sdk.forProject(page.params.region, page.params.project).backups.createPolicy({
|
||||
policyId: ID.unique(),
|
||||
services: ['databases'],
|
||||
services: [BackupServices.Databases],
|
||||
retention: policy.retained,
|
||||
schedule: policy.schedule,
|
||||
name: policy.label,
|
||||
|
||||
+9
-11
@@ -1,10 +1,11 @@
|
||||
<script module lang="ts">
|
||||
import { IndexType, OrderBy } from '@appwrite.io/console';
|
||||
export type CreateIndexesCallbackType = {
|
||||
key: string;
|
||||
type: IndexType;
|
||||
fields: string[];
|
||||
lengths: (number | null)[];
|
||||
orders: string[];
|
||||
orders: OrderBy[];
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -22,9 +23,7 @@
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import { type Entity, getTerminologies } from '$database/(entity)';
|
||||
import { resolveRoute, withPath } from '$lib/stores/navigation';
|
||||
import { IndexType } from '@appwrite.io/console';
|
||||
import { columnOptions as baseColumnOptions } from '$database/table-[table]/columns/store';
|
||||
import { IndexOrder } from '$database/(suggestions)';
|
||||
|
||||
let {
|
||||
entity,
|
||||
@@ -63,7 +62,7 @@
|
||||
|
||||
let fieldList: Array<{
|
||||
value: string;
|
||||
order: IndexOrder | null;
|
||||
order: OrderBy | null;
|
||||
length: number | null;
|
||||
}> = $state([{ value: '', order: null, length: null }]);
|
||||
|
||||
@@ -78,13 +77,13 @@
|
||||
let orderOptions = $derived.by(() =>
|
||||
selectedType === IndexType.Spatial
|
||||
? [
|
||||
{ value: 'ASC', label: 'ASC' },
|
||||
{ value: 'DESC', label: 'DESC' },
|
||||
{ value: OrderBy.Asc, label: 'ASC' },
|
||||
{ value: OrderBy.Desc, label: 'DESC' },
|
||||
{ value: null, label: 'NONE' }
|
||||
]
|
||||
: [
|
||||
{ value: 'ASC', label: 'ASC' },
|
||||
{ value: 'DESC', label: 'DESC' }
|
||||
{ value: OrderBy.Asc, label: 'ASC' },
|
||||
{ value: OrderBy.Desc, label: 'DESC' }
|
||||
]
|
||||
);
|
||||
|
||||
@@ -111,7 +110,7 @@
|
||||
function initialize() {
|
||||
const field = entity.fields.filter((field) => externalFieldKey === field.key);
|
||||
const isSpatial = field.length && isSpatialType(field[0]);
|
||||
const order = isSpatial ? null : IndexOrder.ASC;
|
||||
const order = isSpatial ? null : OrderBy.Asc;
|
||||
|
||||
selectedType = isSpatial ? IndexType.Spatial : IndexType.Key;
|
||||
|
||||
@@ -165,8 +164,7 @@
|
||||
try {
|
||||
const orders = fieldList
|
||||
.map((field) => field.order)
|
||||
.filter((order: IndexOrder) => order !== null)
|
||||
.map((order) => String(order));
|
||||
.filter((order): order is OrderBy => order !== null);
|
||||
|
||||
await onCreateIndex({
|
||||
key,
|
||||
|
||||
+1
-1
@@ -1504,7 +1504,7 @@
|
||||
id="type"
|
||||
required
|
||||
label="Type"
|
||||
value={selectedOption?.name || 'String'}
|
||||
value={selectedOption?.name || 'Text'}
|
||||
on:change={(e) => {
|
||||
const newOption = columnOptions.find(
|
||||
(opt) => opt.name === e.detail
|
||||
|
||||
+14
-15
@@ -2,16 +2,11 @@
|
||||
import { Alert, Accordion, Icon, Layout, Skeleton, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { IconPlus, IconX } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Button, InputSelect /*InputNumber*/ } from '$lib/elements/forms';
|
||||
import {
|
||||
showIndexesSuggestions,
|
||||
IndexOrder,
|
||||
mockSuggestions,
|
||||
type SuggestedIndexSchema
|
||||
} from './store';
|
||||
import { showIndexesSuggestions, mockSuggestions, type SuggestedIndexSchema } from './store';
|
||||
import { Modal } from '$lib/components';
|
||||
import { type Entity, SideSheet } from '$database/(entity)';
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import { IndexType } from '@appwrite.io/console';
|
||||
import { IndexType, OrderBy } from '@appwrite.io/console';
|
||||
import { capitalize } from '$lib/helpers/string';
|
||||
import { type Columns } from '../table-[table]/store';
|
||||
import { isRelationship } from '../table-[table]/rows/store';
|
||||
@@ -74,7 +69,7 @@
|
||||
key: column.name,
|
||||
type: IndexType.Key,
|
||||
fields: [column.name],
|
||||
orders: index === 2 ? IndexOrder.DESC : IndexOrder.ASC,
|
||||
orders: index === 2 ? OrderBy.Desc : OrderBy.Asc,
|
||||
lengths: []
|
||||
}));
|
||||
} else {
|
||||
@@ -90,7 +85,7 @@
|
||||
return {
|
||||
key: index.columns[0],
|
||||
type: index.type as IndexType,
|
||||
orders: (index.orders?.[0] as IndexOrder) || IndexOrder.ASC,
|
||||
orders: (index.orders?.[0] as OrderBy) || OrderBy.Asc,
|
||||
fields: index.columns,
|
||||
lengths: index.lengths ?? []
|
||||
};
|
||||
@@ -118,7 +113,7 @@
|
||||
indexes.push({
|
||||
key: '',
|
||||
type: IndexType.Key,
|
||||
orders: IndexOrder.ASC,
|
||||
orders: OrderBy.Asc,
|
||||
fields: [],
|
||||
lengths: null
|
||||
});
|
||||
@@ -139,11 +134,11 @@
|
||||
}
|
||||
|
||||
function getOrderOptions(selectedType: IndexType) {
|
||||
const base = [IndexOrder.ASC, IndexOrder.DESC];
|
||||
const values = selectedType === IndexType.Spatial ? [...base, IndexOrder.NONE] : base;
|
||||
const base = [OrderBy.Asc, OrderBy.Desc];
|
||||
const values = selectedType === IndexType.Spatial ? [...base, null] : base;
|
||||
|
||||
return values.map((order) => ({
|
||||
label: capitalize(String(order)),
|
||||
label: order ? capitalize(String(order)) : 'None',
|
||||
value: order
|
||||
}));
|
||||
}
|
||||
@@ -165,7 +160,8 @@
|
||||
|
||||
function prepareIndexForCreation(index: SuggestedIndexSchema, columnMap: Map<string, number>) {
|
||||
// prepare orders array
|
||||
const orders = index.orders !== null ? index.fields.map(() => String(index.orders)) : [];
|
||||
const orders: OrderBy[] =
|
||||
index.orders !== null ? index.fields.map(() => index.orders as OrderBy) : [];
|
||||
|
||||
// prepare lengths array
|
||||
let lengths: (number | null)[];
|
||||
@@ -222,7 +218,10 @@
|
||||
const usedKeys = new Set<string>();
|
||||
const columnMap: Map<string, number> = new Map(
|
||||
table.fields
|
||||
.filter((field) => field.type === 'string' && 'size' in field)
|
||||
.filter(
|
||||
(field) =>
|
||||
(field.type === 'string' || field.type === 'varchar') && 'size' in field
|
||||
)
|
||||
.map((field) => [field.key, field['size']])
|
||||
);
|
||||
|
||||
|
||||
+3
-7
@@ -1,5 +1,5 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import { IndexType } from '@appwrite.io/console';
|
||||
import { IndexType, OrderBy } from '@appwrite.io/console';
|
||||
import { columnOptions } from '../table-[table]/columns/store';
|
||||
|
||||
export type TableColumnSuggestions = {
|
||||
@@ -30,11 +30,7 @@ export type SuggestedColumnSchema = {
|
||||
isPlaceholder?: boolean;
|
||||
};
|
||||
|
||||
export enum IndexOrder {
|
||||
ASC = 'ASC',
|
||||
DESC = 'DESC',
|
||||
NONE = null
|
||||
}
|
||||
export type IndexOrder = OrderBy | null;
|
||||
|
||||
export type SuggestedIndexSchema = {
|
||||
key: string;
|
||||
@@ -146,7 +142,7 @@ export function mapSuggestedColumns<T extends ColumnInput>(columns: T[]): Sugges
|
||||
required: col.required ?? false,
|
||||
array: false,
|
||||
default: col.default ?? null,
|
||||
size: col.type === 'string' ? (col.size ?? undefined) : undefined,
|
||||
size: col.type === 'string' || col.type === 'varchar' ? (col.size ?? undefined) : undefined,
|
||||
min:
|
||||
col.type === 'integer' || col.type === 'double'
|
||||
? (col.min ?? col.formatOptions?.min ?? undefined)
|
||||
|
||||
+3
-3
@@ -17,7 +17,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { feedback } from '$lib/stores/feedback';
|
||||
import { cronExpression, type UserBackupPolicy } from '$lib/helpers/backups';
|
||||
import { ID } from '@appwrite.io/console';
|
||||
import { BackupServices, ID } from '@appwrite.io/console';
|
||||
import { showCreateBackup, showCreatePolicy } from './store';
|
||||
import { getProjectId } from '$lib/helpers/project';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
@@ -68,7 +68,7 @@
|
||||
const createManualBackup = async () => {
|
||||
try {
|
||||
await sdk.forProject(page.params.region, page.params.project).backups.createArchive({
|
||||
services: ['databases'],
|
||||
services: [BackupServices.Databases],
|
||||
resourceId: data.database.$id
|
||||
});
|
||||
await invalidate(Dependencies.BACKUPS);
|
||||
@@ -123,7 +123,7 @@
|
||||
|
||||
return sdk.forProject(page.params.region, page.params.project).backups.createPolicy({
|
||||
policyId: ID.unique(),
|
||||
services: ['databases'],
|
||||
services: [BackupServices.Databases],
|
||||
retention: policy.retained,
|
||||
schedule: policy.schedule,
|
||||
name: policy.label,
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { calculateSize } from '$lib/helpers/sizeConvertion';
|
||||
import { ID, type Models } from '@appwrite.io/console';
|
||||
import { BackupServices, ID, type Models } from '@appwrite.io/console';
|
||||
import { columns } from './store';
|
||||
import { Click, Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { copy } from '$lib/helpers/copy';
|
||||
@@ -157,7 +157,7 @@
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.backups.createRestoration({
|
||||
archiveId: selectedBackup.$id,
|
||||
services: ['databases'],
|
||||
services: [BackupServices.Databases],
|
||||
newResourceId: newDatabaseInfo.id ?? ID.unique(),
|
||||
newResourceName: newDatabaseInfo.name
|
||||
});
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@
|
||||
|
||||
let createIndex: CreateIndex;
|
||||
let createColumn: CreateColumn;
|
||||
let selectedOption: Option['name'] = 'String';
|
||||
let selectedOption: Option['name'] = 'Text';
|
||||
let createMoreColumns = false;
|
||||
|
||||
/* terminology */
|
||||
|
||||
+5
-1
@@ -191,7 +191,11 @@
|
||||
function getMinMaxSizeForColumn(
|
||||
column: Columns
|
||||
): { display: string; tooltip?: string } | undefined {
|
||||
if (column.type === 'string' && !column['format'] && column.key !== '$id') {
|
||||
if (
|
||||
(column.type === 'string' || column.type === 'varchar') &&
|
||||
!column['format'] &&
|
||||
column.key !== '$id'
|
||||
) {
|
||||
const stringColumn = column as Models.ColumnString;
|
||||
return { display: `Size: ${stringColumn.size}` };
|
||||
} else if (column.type === 'integer' || column.type === 'double') {
|
||||
|
||||
+1
-1
@@ -32,9 +32,9 @@
|
||||
tableId,
|
||||
key: originalKey,
|
||||
required: data.required,
|
||||
xdefault: data.default,
|
||||
min: data.min,
|
||||
max: data.max,
|
||||
xdefault: data.default,
|
||||
newKey: data.key !== originalKey ? data.key : undefined
|
||||
});
|
||||
}
|
||||
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
<script context="module" lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
export async function submitLongtext(
|
||||
databaseId: string,
|
||||
tableId: string,
|
||||
key: string,
|
||||
data: Partial<Models.ColumnLongtext>
|
||||
) {
|
||||
await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.tablesDB.createLongtextColumn({
|
||||
databaseId,
|
||||
tableId,
|
||||
key,
|
||||
required: data.required,
|
||||
xdefault: data.default,
|
||||
array: data.array
|
||||
});
|
||||
}
|
||||
export async function updateLongtext(
|
||||
databaseId: string,
|
||||
tableId: string,
|
||||
data: Partial<Models.ColumnLongtext>,
|
||||
originalKey?: string
|
||||
) {
|
||||
await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.tablesDB.updateLongtextColumn({
|
||||
databaseId,
|
||||
tableId,
|
||||
key: originalKey,
|
||||
required: data.required,
|
||||
xdefault: data.default,
|
||||
newKey: data.key !== originalKey ? data.key : undefined
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { createConservative } from '$lib/helpers/stores';
|
||||
import RequiredArrayCheckboxes from './requiredArrayCheckboxes.svelte';
|
||||
import { InputTextarea } from '$lib/elements/forms';
|
||||
import { Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let data: Partial<Models.ColumnLongtext> = {
|
||||
required: false,
|
||||
array: false
|
||||
};
|
||||
|
||||
export let editing = false;
|
||||
export let disabled = false;
|
||||
|
||||
let savedDefault = data.default;
|
||||
|
||||
function handleDefaultState(hideDefault: boolean) {
|
||||
if (hideDefault) {
|
||||
savedDefault = data.default;
|
||||
data.default = null;
|
||||
} else {
|
||||
data.default = savedDefault;
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
stores: { required, array },
|
||||
listen
|
||||
} = createConservative<Partial<Models.ColumnLongtext>>({
|
||||
required: false,
|
||||
array: false,
|
||||
...data
|
||||
});
|
||||
|
||||
$: listen(data);
|
||||
|
||||
$: handleDefaultState($required || $array);
|
||||
</script>
|
||||
|
||||
<Layout.Stack gap="xs" direction="column">
|
||||
<Typography.Text variant="m-500" color="--fgcolor-neutral-secondary">
|
||||
Maximum size: 1,073,741,823 characters
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
|
||||
<InputTextarea
|
||||
id="default"
|
||||
label="Default"
|
||||
placeholder="Enter text"
|
||||
bind:value={data.default}
|
||||
disabled={data.required || data.array || disabled}
|
||||
nullable={!data.required && !data.array} />
|
||||
|
||||
<RequiredArrayCheckboxes
|
||||
{editing}
|
||||
{disabled}
|
||||
bind:array={data.array}
|
||||
bind:required={data.required} />
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
<script context="module" lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
export async function submitMediumtext(
|
||||
databaseId: string,
|
||||
tableId: string,
|
||||
key: string,
|
||||
data: Partial<Models.ColumnMediumtext>
|
||||
) {
|
||||
await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.tablesDB.createMediumtextColumn({
|
||||
databaseId,
|
||||
tableId,
|
||||
key,
|
||||
required: data.required,
|
||||
xdefault: data.default,
|
||||
array: data.array
|
||||
});
|
||||
}
|
||||
export async function updateMediumtext(
|
||||
databaseId: string,
|
||||
tableId: string,
|
||||
data: Partial<Models.ColumnMediumtext>,
|
||||
originalKey?: string
|
||||
) {
|
||||
await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.tablesDB.updateMediumtextColumn({
|
||||
databaseId,
|
||||
tableId,
|
||||
key: originalKey,
|
||||
required: data.required,
|
||||
xdefault: data.default,
|
||||
newKey: data.key !== originalKey ? data.key : undefined
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { createConservative } from '$lib/helpers/stores';
|
||||
import RequiredArrayCheckboxes from './requiredArrayCheckboxes.svelte';
|
||||
import { InputTextarea } from '$lib/elements/forms';
|
||||
import { Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let data: Partial<Models.ColumnMediumtext> = {
|
||||
required: false,
|
||||
array: false
|
||||
};
|
||||
|
||||
export let editing = false;
|
||||
export let disabled = false;
|
||||
|
||||
let savedDefault = data.default;
|
||||
|
||||
function handleDefaultState(hideDefault: boolean) {
|
||||
if (hideDefault) {
|
||||
savedDefault = data.default;
|
||||
data.default = null;
|
||||
} else {
|
||||
data.default = savedDefault;
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
stores: { required, array },
|
||||
listen
|
||||
} = createConservative<Partial<Models.ColumnMediumtext>>({
|
||||
required: false,
|
||||
array: false,
|
||||
...data
|
||||
});
|
||||
|
||||
$: listen(data);
|
||||
|
||||
$: handleDefaultState($required || $array);
|
||||
</script>
|
||||
|
||||
<Layout.Stack gap="xs" direction="column">
|
||||
<Typography.Text variant="m-500" color="--fgcolor-neutral-secondary">
|
||||
Maximum size: 4,194,303 characters
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
|
||||
<InputTextarea
|
||||
id="default"
|
||||
label="Default"
|
||||
placeholder="Enter text"
|
||||
bind:value={data.default}
|
||||
disabled={data.required || data.array || disabled}
|
||||
nullable={!data.required && !data.array} />
|
||||
|
||||
<RequiredArrayCheckboxes
|
||||
{editing}
|
||||
{disabled}
|
||||
bind:array={data.array}
|
||||
bind:required={data.required} />
|
||||
+56
-8
@@ -7,6 +7,10 @@ import Float, { submitFloat, updateFloat } from './float.svelte';
|
||||
import Integer, { submitInteger, updateInteger } from './integer.svelte';
|
||||
import Ip, { submitIp, updateIp } from './ip.svelte';
|
||||
import String, { submitString, updateString } from './string.svelte';
|
||||
import Varchar, { submitVarchar, updateVarchar } from './varchar.svelte';
|
||||
import Text, { submitText, updateText } from './text.svelte';
|
||||
import Mediumtext, { submitMediumtext, updateMediumtext } from './mediumtext.svelte';
|
||||
import Longtext, { submitLongtext, updateLongtext } from './longtext.svelte';
|
||||
import Url, { submitUrl, updateUrl } from './url.svelte';
|
||||
import Datetime, { submitDatetime, updateDatetime } from './datetime.svelte';
|
||||
import Point, { submitPoint, updatePoint } from './point.svelte';
|
||||
@@ -32,7 +36,10 @@ import type { ComponentType } from 'svelte';
|
||||
|
||||
export type Option = {
|
||||
name:
|
||||
| 'String'
|
||||
| 'Text'
|
||||
| 'Mediumtext'
|
||||
| 'Longtext'
|
||||
| 'Varchar'
|
||||
| 'Integer'
|
||||
| 'Float'
|
||||
| 'Boolean'
|
||||
@@ -44,11 +51,16 @@ export type Option = {
|
||||
| 'Relationship'
|
||||
| 'Point'
|
||||
| 'Line'
|
||||
| 'Polygon';
|
||||
| 'Polygon'
|
||||
| 'String (deprecated)';
|
||||
sentenceName: string;
|
||||
component: Component;
|
||||
type:
|
||||
| 'string'
|
||||
| 'text'
|
||||
| 'mediumtext'
|
||||
| 'longtext'
|
||||
| 'varchar'
|
||||
| 'integer'
|
||||
| 'double'
|
||||
| 'boolean'
|
||||
@@ -75,12 +87,39 @@ export type Option = {
|
||||
|
||||
export const columnOptions: Option[] = [
|
||||
{
|
||||
name: 'String',
|
||||
sentenceName: 'string',
|
||||
component: String,
|
||||
type: 'string',
|
||||
create: submitString,
|
||||
update: updateString,
|
||||
name: 'Text',
|
||||
sentenceName: 'text',
|
||||
component: Text,
|
||||
type: 'text',
|
||||
create: submitText,
|
||||
update: updateText,
|
||||
icon: IconText
|
||||
},
|
||||
{
|
||||
name: 'Mediumtext',
|
||||
sentenceName: 'mediumtext',
|
||||
component: Mediumtext,
|
||||
type: 'mediumtext',
|
||||
create: submitMediumtext,
|
||||
update: updateMediumtext,
|
||||
icon: IconText
|
||||
},
|
||||
{
|
||||
name: 'Longtext',
|
||||
sentenceName: 'longtext',
|
||||
component: Longtext,
|
||||
type: 'longtext',
|
||||
create: submitLongtext,
|
||||
update: updateLongtext,
|
||||
icon: IconText
|
||||
},
|
||||
{
|
||||
name: 'Varchar',
|
||||
sentenceName: 'varchar',
|
||||
component: Varchar,
|
||||
type: 'varchar',
|
||||
create: submitVarchar,
|
||||
update: updateVarchar,
|
||||
icon: IconText
|
||||
},
|
||||
{
|
||||
@@ -194,6 +233,15 @@ export const columnOptions: Option[] = [
|
||||
create: submitRelationship,
|
||||
update: updateRelationship,
|
||||
icon: IconRelationship
|
||||
},
|
||||
{
|
||||
name: 'String (deprecated)',
|
||||
sentenceName: 'string',
|
||||
component: String,
|
||||
type: 'string',
|
||||
create: submitString,
|
||||
update: updateString,
|
||||
icon: IconText
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
<script context="module" lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
export async function submitText(
|
||||
databaseId: string,
|
||||
tableId: string,
|
||||
key: string,
|
||||
data: Partial<Models.ColumnText>
|
||||
) {
|
||||
await sdk.forProject(page.params.region, page.params.project).tablesDB.createTextColumn({
|
||||
databaseId,
|
||||
tableId,
|
||||
key,
|
||||
required: data.required,
|
||||
xdefault: data.default,
|
||||
array: data.array
|
||||
});
|
||||
}
|
||||
export async function updateText(
|
||||
databaseId: string,
|
||||
tableId: string,
|
||||
data: Partial<Models.ColumnText>,
|
||||
originalKey?: string
|
||||
) {
|
||||
await sdk.forProject(page.params.region, page.params.project).tablesDB.updateTextColumn({
|
||||
databaseId,
|
||||
tableId,
|
||||
key: originalKey,
|
||||
required: data.required,
|
||||
xdefault: data.default,
|
||||
newKey: data.key !== originalKey ? data.key : undefined
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { createConservative } from '$lib/helpers/stores';
|
||||
import RequiredArrayCheckboxes from './requiredArrayCheckboxes.svelte';
|
||||
import { InputTextarea } from '$lib/elements/forms';
|
||||
import { Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let data: Partial<Models.ColumnText> = {
|
||||
required: false,
|
||||
array: false
|
||||
};
|
||||
|
||||
export let editing = false;
|
||||
export let disabled = false;
|
||||
|
||||
let savedDefault = data.default;
|
||||
|
||||
function handleDefaultState(hideDefault: boolean) {
|
||||
if (hideDefault) {
|
||||
savedDefault = data.default;
|
||||
data.default = null;
|
||||
} else {
|
||||
data.default = savedDefault;
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
stores: { required, array },
|
||||
listen
|
||||
} = createConservative<Partial<Models.ColumnText>>({
|
||||
required: false,
|
||||
array: false,
|
||||
...data
|
||||
});
|
||||
|
||||
$: listen(data);
|
||||
|
||||
$: handleDefaultState($required || $array);
|
||||
</script>
|
||||
|
||||
<Layout.Stack gap="xs" direction="column">
|
||||
<Typography.Text variant="m-500" color="--fgcolor-neutral-secondary">
|
||||
Maximum size: 16,383 characters
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
|
||||
<InputTextarea
|
||||
id="default"
|
||||
label="Default"
|
||||
placeholder="Enter text"
|
||||
bind:value={data.default}
|
||||
disabled={data.required || data.array || disabled}
|
||||
nullable={!data.required && !data.array} />
|
||||
|
||||
<RequiredArrayCheckboxes
|
||||
{editing}
|
||||
{disabled}
|
||||
bind:array={data.array}
|
||||
bind:required={data.required} />
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
<script context="module" lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
export async function submitVarchar(
|
||||
databaseId: string,
|
||||
tableId: string,
|
||||
key: string,
|
||||
data: Partial<Models.ColumnVarchar>
|
||||
) {
|
||||
await sdk.forProject(page.params.region, page.params.project).tablesDB.createVarcharColumn({
|
||||
databaseId,
|
||||
tableId,
|
||||
key,
|
||||
size: data.size,
|
||||
required: data.required,
|
||||
xdefault: data.default,
|
||||
array: data.array
|
||||
});
|
||||
}
|
||||
export async function updateVarchar(
|
||||
databaseId: string,
|
||||
tableId: string,
|
||||
data: Partial<Models.ColumnVarchar>,
|
||||
originalKey?: string
|
||||
) {
|
||||
await sdk.forProject(page.params.region, page.params.project).tablesDB.updateVarcharColumn({
|
||||
databaseId,
|
||||
tableId,
|
||||
key: originalKey,
|
||||
required: data.required,
|
||||
xdefault: data.default,
|
||||
size: data.size,
|
||||
newKey: data.key !== originalKey ? data.key : undefined
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { createConservative } from '$lib/helpers/stores';
|
||||
import RequiredArrayCheckboxes from './requiredArrayCheckboxes.svelte';
|
||||
import { InputNumber, InputText, InputTextarea } from '$lib/elements/forms';
|
||||
import { table } from '../store';
|
||||
import { ProgressBar } from '$lib/components';
|
||||
import { Layout, Typography, Tooltip, Icon } from '@appwrite.io/pink-svelte';
|
||||
import { IconInfo } from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
export let data: Partial<Models.ColumnVarchar> = {
|
||||
required: false,
|
||||
size: 255,
|
||||
array: false
|
||||
};
|
||||
|
||||
export let editing = false;
|
||||
export let disabled = false;
|
||||
|
||||
// Local size for reactivity
|
||||
let size = data.size ?? 255;
|
||||
$: data.size = size;
|
||||
|
||||
function formatBytes(bytes: number): string {
|
||||
return bytes < 1024 ? `${bytes.toLocaleString()} bytes` : `${(bytes / 1024).toFixed(1)} KB`;
|
||||
}
|
||||
|
||||
function getNewColumnBytes(s: number): number {
|
||||
return s * 4 + (s <= 255 ? 1 : 2);
|
||||
}
|
||||
|
||||
function getProgressData(bytesUsed: number, bytesMax: number, s: number) {
|
||||
const newBytes = getNewColumnBytes(s);
|
||||
const exceeds = bytesUsed + newBytes > bytesMax;
|
||||
return [
|
||||
{
|
||||
size: bytesUsed,
|
||||
color: 'hsl(var(--color-information-100))',
|
||||
tooltip: {
|
||||
title: 'Current usage:',
|
||||
label: formatBytes(bytesUsed)
|
||||
}
|
||||
},
|
||||
{
|
||||
size: newBytes,
|
||||
color: exceeds ? 'hsl(var(--color-danger-100))' : 'hsl(var(--color-success-100))',
|
||||
tooltip: {
|
||||
title: 'New column:',
|
||||
label: formatBytes(newBytes)
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
$: bytesMax = $table?.bytesMax ?? 65535;
|
||||
$: bytesUsed = $table?.bytesUsed ?? 0;
|
||||
$: newColumnBytes = getNewColumnBytes(size);
|
||||
$: exceedsLimit = !editing && bytesUsed + newColumnBytes > bytesMax;
|
||||
|
||||
let savedDefault = data.default;
|
||||
|
||||
function handleDefaultState(hideDefault: boolean) {
|
||||
if (hideDefault) {
|
||||
savedDefault = data.default;
|
||||
data.default = null;
|
||||
} else {
|
||||
data.default = savedDefault;
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
stores: { required, array },
|
||||
listen
|
||||
} = createConservative<Partial<Models.ColumnVarchar>>({
|
||||
required: false,
|
||||
array: false,
|
||||
...data
|
||||
});
|
||||
|
||||
$: listen(data);
|
||||
|
||||
$: handleDefaultState($required || $array);
|
||||
</script>
|
||||
|
||||
<InputNumber
|
||||
id="size"
|
||||
label="Size"
|
||||
required
|
||||
{disabled}
|
||||
placeholder="Enter size"
|
||||
bind:value={size}
|
||||
min={1}
|
||||
max={16383} />
|
||||
|
||||
{#if !editing}
|
||||
<Layout.Stack gap="xs">
|
||||
<Layout.Stack direction="row" gap="xs" alignItems="center">
|
||||
<Typography.Text variant="m-500" color="--fgcolor-neutral-secondary">
|
||||
Row size usage
|
||||
</Typography.Text>
|
||||
<Tooltip maxWidth="300px">
|
||||
<Icon icon={IconInfo} size="s" />
|
||||
<span slot="tooltip">
|
||||
Database rows have a maximum size of 64 KB. varchar columns use 4 bytes per
|
||||
character plus a small overhead. text, mediumtext, and longtext columns only use
|
||||
~20 bytes regardless of content length.
|
||||
</span>
|
||||
</Tooltip>
|
||||
</Layout.Stack>
|
||||
<ProgressBar
|
||||
hideEmptySegments
|
||||
maxSize={bytesMax}
|
||||
data={getProgressData(bytesUsed, bytesMax, size)} />
|
||||
{#if exceedsLimit}
|
||||
<Typography.Text variant="m-400" color="--fgcolor-danger">
|
||||
This column exceeds the remaining row space. Consider using text, mediumtext, or
|
||||
longtext instead.
|
||||
</Typography.Text>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
|
||||
<svelte:component
|
||||
this={size >= 50 ? InputTextarea : InputText}
|
||||
id="default"
|
||||
label="Default"
|
||||
placeholder="Enter string"
|
||||
maxlength={size}
|
||||
bind:value={data.default}
|
||||
disabled={data.required || data.array || disabled}
|
||||
nullable={!data.required && !data.array} />
|
||||
|
||||
<RequiredArrayCheckboxes
|
||||
{editing}
|
||||
{disabled}
|
||||
bind:array={data.array}
|
||||
bind:required={data.required} />
|
||||
+3
-3
@@ -23,7 +23,7 @@
|
||||
columns = $bindable(null),
|
||||
columnId = $bindable(null),
|
||||
columnsOrder = $bindable(null),
|
||||
selectedOption = $bindable('String'),
|
||||
selectedOption = $bindable('Text'),
|
||||
createMore = $bindable(false),
|
||||
onColumnsReorder = null
|
||||
}: {
|
||||
@@ -63,8 +63,8 @@
|
||||
default: null
|
||||
};
|
||||
|
||||
/* default to string */
|
||||
selectedOption = 'String';
|
||||
/* default to text */
|
||||
selectedOption = 'Text';
|
||||
$option = columnOptions[0];
|
||||
}
|
||||
|
||||
|
||||
+8
@@ -27,6 +27,10 @@
|
||||
| Models.ColumnInteger
|
||||
| Models.ColumnIp
|
||||
| Models.ColumnString
|
||||
| Models.ColumnText
|
||||
| Models.ColumnMediumtext
|
||||
| Models.ColumnLongtext
|
||||
| Models.ColumnVarchar
|
||||
| Models.ColumnDatetime
|
||||
| Models.ColumnUrl
|
||||
| Models.ColumnPoint
|
||||
@@ -35,6 +39,10 @@
|
||||
|
||||
const columnsTypeMap = {
|
||||
string: String,
|
||||
varchar: String,
|
||||
text: String,
|
||||
mediumtext: String,
|
||||
longtext: String,
|
||||
integer: Integer,
|
||||
double: Integer,
|
||||
boolean: Boolean,
|
||||
|
||||
+6
-2
@@ -20,6 +20,10 @@
|
||||
limited?: boolean;
|
||||
column:
|
||||
| Models.ColumnString
|
||||
| Models.ColumnVarchar
|
||||
| Models.ColumnText
|
||||
| Models.ColumnMediumtext
|
||||
| Models.ColumnLongtext
|
||||
| Models.ColumnInteger
|
||||
| Models.ColumnFloat
|
||||
| Models.ColumnBoolean
|
||||
@@ -32,8 +36,8 @@
|
||||
const maxlength = $derived(
|
||||
limited
|
||||
? undefined
|
||||
: column.type === 'string'
|
||||
? (column as Models.ColumnString).size
|
||||
: column.type === 'string' || column.type === 'varchar'
|
||||
? (column as Models.ColumnString | Models.ColumnVarchar).size
|
||||
: undefined
|
||||
);
|
||||
|
||||
|
||||
+6
@@ -30,6 +30,12 @@ export function isString(field: Field): field is Models.ColumnString | Models.At
|
||||
return field?.type === 'string';
|
||||
}
|
||||
|
||||
export function isTextType(field: Field): boolean {
|
||||
if (!field) return false;
|
||||
const textTypes = ['string', 'varchar', 'text', 'mediumtext', 'longtext'];
|
||||
return textTypes.includes(field?.type);
|
||||
}
|
||||
|
||||
export function isSpatialType(
|
||||
field: Columns | Attributes | Column
|
||||
): field is
|
||||
|
||||
+4
-1
@@ -21,7 +21,10 @@
|
||||
return {
|
||||
name: table.name,
|
||||
tableId: page.params.table,
|
||||
databaseId: page.params.database
|
||||
databaseId: page.params.database,
|
||||
rowSecurity: table.recordSecurity,
|
||||
enabled: table.enabled,
|
||||
permissions: table.$permissions
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { columns } from '../store';
|
||||
import { isTextType } from '../rows/store';
|
||||
import { preferences } from '$lib/stores/preferences';
|
||||
import { page } from '$app/state';
|
||||
import { Icon, Layout } from '@appwrite.io/pink-svelte';
|
||||
@@ -47,7 +48,7 @@
|
||||
|
||||
function getValidColumns() {
|
||||
return ($columns as Models.ColumnString[]).filter(
|
||||
(attr) => attr.type === 'string' && !attr?.array
|
||||
(attr) => isTextType(attr) && !attr?.array
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -14,6 +14,10 @@ export type Columns =
|
||||
| Models.ColumnInteger
|
||||
| Models.ColumnIp
|
||||
| Models.ColumnString
|
||||
| Models.ColumnText
|
||||
| Models.ColumnMediumtext
|
||||
| Models.ColumnLongtext
|
||||
| Models.ColumnVarchar
|
||||
| Models.ColumnUrl
|
||||
| Models.ColumnPoint
|
||||
| Models.ColumnLine
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@
|
||||
import { Fieldset, Layout, Icon, Input, Tag } from '@appwrite.io/pink-svelte';
|
||||
import { IconGithub, IconPencil } from '@appwrite.io/pink-icons-svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { ID, Runtime, TemplateReferenceType } from '@appwrite.io/console';
|
||||
import { ID, Runtime, TemplateReferenceType, type Scopes } from '@appwrite.io/console';
|
||||
import { CustomId } from '$lib/components';
|
||||
import { getIconFromRuntime } from '$lib/stores/runtimes';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
@@ -39,7 +39,7 @@
|
||||
let specification = $state('');
|
||||
let runtime = $state<Runtime>();
|
||||
let installCommand = $state('');
|
||||
let selectedScopes = $state<string[]>([]);
|
||||
let selectedScopes = $state<Scopes[]>([]);
|
||||
let rootDir = $state(data.repository?.rootDirectory);
|
||||
let variables = $state<Array<{ key: string; value: string; secret: boolean }>>([]);
|
||||
|
||||
|
||||
+8
-2
@@ -15,7 +15,13 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import ProductionBranch from '$lib/components/git/productionBranchFieldset.svelte';
|
||||
import Configuration from './configuration.svelte';
|
||||
import { ID, Runtime, TemplateReferenceType, type Models } from '@appwrite.io/console';
|
||||
import {
|
||||
ID,
|
||||
Runtime,
|
||||
TemplateReferenceType,
|
||||
type Models,
|
||||
type Scopes
|
||||
} from '@appwrite.io/console';
|
||||
import {
|
||||
ConnectBehaviour,
|
||||
NewRepository,
|
||||
@@ -62,7 +68,7 @@
|
||||
let showConfig = false;
|
||||
let silentMode = false;
|
||||
let entrypoint = '';
|
||||
let selectedScopes: string[] = [];
|
||||
let selectedScopes: Scopes[] = [];
|
||||
let execute = true;
|
||||
let variables: Partial<Models.TemplateVariable>[] = [];
|
||||
let specification = specificationOptions[0]?.value || '';
|
||||
|
||||
+4
-3
@@ -1,15 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { scopes } from '$lib/constants';
|
||||
import { Fieldset, Layout, Selector } from '@appwrite.io/pink-svelte';
|
||||
import type { Scopes } from '@appwrite.io/console';
|
||||
|
||||
export let templateScopes: string[];
|
||||
export let selectedScopes: string[];
|
||||
export let selectedScopes: Scopes[];
|
||||
export let execute = true;
|
||||
|
||||
let scopeList = scopes
|
||||
.filter((s) => templateScopes.includes(s.scope))
|
||||
.map((s) => {
|
||||
selectedScopes.push(s.scope);
|
||||
selectedScopes.push(s.scope as Scopes);
|
||||
return {
|
||||
value: s,
|
||||
checked: true
|
||||
@@ -37,7 +38,7 @@
|
||||
on:change={() => {
|
||||
selectedScopes = scopeList
|
||||
.filter((s) => s.checked)
|
||||
.map((s) => s.value.scope);
|
||||
.map((s) => s.value.scope as Scopes);
|
||||
}}>
|
||||
</Selector.Switch>
|
||||
{/each}
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { installation, repository, sortBranches } from '$lib/stores/vcs';
|
||||
import { Runtime, VCSReferenceType, type Models } from '@appwrite.io/console';
|
||||
import { Runtime, VCSReferenceType, type Models, type Scopes } from '@appwrite.io/console';
|
||||
import { IconGithub } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Icon, Input, Layout, Skeleton, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { func } from '../store';
|
||||
@@ -87,7 +87,7 @@
|
||||
logging: $func.logging || undefined,
|
||||
entrypoint: $func.entrypoint,
|
||||
commands: $func.commands || undefined,
|
||||
scopes: $func.scopes || undefined,
|
||||
scopes: ($func.scopes as Scopes[]) || undefined,
|
||||
installationId: $installation.$id || undefined,
|
||||
providerRepositoryId: selectedRepository || undefined,
|
||||
providerBranch: branch || undefined
|
||||
|
||||
+8
-2
@@ -10,7 +10,13 @@
|
||||
import { sortBranches } from '$lib/stores/vcs';
|
||||
import { IconInfo } from '@appwrite.io/pink-icons-svelte';
|
||||
import { LabelCard } from '$lib/components';
|
||||
import { type Models, ProxyResourceType, Runtime, StatusCode } from '@appwrite.io/console';
|
||||
import {
|
||||
type Models,
|
||||
ProxyResourceType,
|
||||
Runtime,
|
||||
StatusCode,
|
||||
type Scopes
|
||||
} from '@appwrite.io/console';
|
||||
import { statusCodeOptions } from '$lib/stores/domains';
|
||||
import { writable } from 'svelte/store';
|
||||
import { onMount } from 'svelte';
|
||||
@@ -140,7 +146,7 @@
|
||||
logging: data.func.logging || undefined,
|
||||
entrypoint: data.func.entrypoint,
|
||||
commands: data.func.commands || undefined,
|
||||
scopes: data.func.scopes || undefined,
|
||||
scopes: (data.func.scopes as Scopes[]) || undefined,
|
||||
installationId: selectedInstallationId,
|
||||
providerRepositoryId: selectedRepository,
|
||||
providerBranch: 'main'
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { func } from '../store';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import { Runtime } from '@appwrite.io/console';
|
||||
import { Runtime, type Scopes } from '@appwrite.io/console';
|
||||
|
||||
export let show = false;
|
||||
const functionId = page.params.function;
|
||||
@@ -35,7 +35,7 @@
|
||||
logging: $func.logging || undefined,
|
||||
entrypoint: $func.entrypoint,
|
||||
commands: $func.commands || undefined,
|
||||
scopes: $func.scopes || undefined,
|
||||
scopes: ($func.scopes as Scopes[]) || undefined,
|
||||
installationId: '',
|
||||
providerRepositoryId: '',
|
||||
providerBranch: '',
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import { Runtime, type Models } from '@appwrite.io/console';
|
||||
import { Runtime, type Models, type Scopes } from '@appwrite.io/console';
|
||||
import { page } from '$app/state';
|
||||
|
||||
export let func: Models.Function;
|
||||
@@ -30,7 +30,7 @@
|
||||
logging: func.logging || undefined,
|
||||
entrypoint: func.entrypoint || undefined,
|
||||
commands: buildCommand || undefined,
|
||||
scopes: func.scopes || undefined,
|
||||
scopes: (func.scopes as Scopes[]) || undefined,
|
||||
installationId: func.installationId || undefined,
|
||||
providerRepositoryId: func.providerRepositoryId || undefined,
|
||||
providerBranch: func.providerBranch || undefined,
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@
|
||||
import { EventModal } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import { Runtime } from '@appwrite.io/console';
|
||||
import { Runtime, type Scopes } from '@appwrite.io/console';
|
||||
import { IconPlus, IconX } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Icon, Layout, Link, Table, Typography } from '@appwrite.io/pink-svelte';
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
logging: $func.logging || undefined,
|
||||
entrypoint: $func.entrypoint || undefined,
|
||||
commands: $func.commands || undefined,
|
||||
scopes: $func.scopes || undefined,
|
||||
scopes: ($func.scopes as Scopes[]) || undefined,
|
||||
installationId: $func.installationId || undefined,
|
||||
providerRepositoryId: $func.providerRepositoryId || undefined,
|
||||
providerBranch: $func.providerBranch || undefined,
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import { Runtime, type Models } from '@appwrite.io/console';
|
||||
import { Runtime, type Models, type Scopes } from '@appwrite.io/console';
|
||||
import { Typography } from '@appwrite.io/pink-svelte';
|
||||
import { page } from '$app/state';
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
logging,
|
||||
entrypoint: func.entrypoint || undefined,
|
||||
commands: func.commands || undefined,
|
||||
scopes: func.scopes || undefined,
|
||||
scopes: (func.scopes as Scopes[]) || undefined,
|
||||
installationId: func.installationId || undefined,
|
||||
providerRepositoryId: func.providerRepositoryId || undefined,
|
||||
providerBranch: func.providerBranch || undefined,
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { func } from '../store';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import { Runtime } from '@appwrite.io/console';
|
||||
import { Runtime, type Scopes } from '@appwrite.io/console';
|
||||
|
||||
const functionId = page.params.function;
|
||||
let functionName: string = null;
|
||||
@@ -36,7 +36,7 @@
|
||||
logging: $func.logging || undefined,
|
||||
entrypoint: $func.entrypoint || undefined,
|
||||
commands: $func.commands || undefined,
|
||||
scopes: $func.scopes || undefined,
|
||||
scopes: ($func.scopes as Scopes[]) || undefined,
|
||||
installationId: $func.installationId || undefined,
|
||||
providerRepositoryId: $func.providerRepositoryId || undefined,
|
||||
providerBranch: $func.providerBranch || undefined,
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@
|
||||
import { Roles } from '$lib/components/permissions';
|
||||
import { symmetricDifference } from '$lib/helpers/array';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import { Runtime } from '@appwrite.io/console';
|
||||
import { Runtime, type Scopes } from '@appwrite.io/console';
|
||||
import { Link } from '$lib/elements';
|
||||
|
||||
const functionId = page.params.function;
|
||||
@@ -41,7 +41,7 @@
|
||||
logging: $func.logging || undefined,
|
||||
entrypoint: $func.entrypoint || undefined,
|
||||
commands: $func.commands || undefined,
|
||||
scopes: $func.scopes || undefined,
|
||||
scopes: ($func.scopes as Scopes[]) || undefined,
|
||||
installationId: $func.installationId || undefined,
|
||||
providerRepositoryId: $func.providerRepositoryId || undefined,
|
||||
providerBranch: $func.providerBranch || undefined,
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@
|
||||
import { Button, Form, InputText } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { Runtime, type Models } from '@appwrite.io/console';
|
||||
import { Runtime, type Models, type Scopes } from '@appwrite.io/console';
|
||||
import { onMount } from 'svelte';
|
||||
import DisconnectRepo from './disconnectRepo.svelte';
|
||||
import { installation, repository as repositoryStore, sortBranches } from '$lib/stores/vcs';
|
||||
@@ -86,7 +86,7 @@
|
||||
logging: func.logging || undefined,
|
||||
entrypoint: func.entrypoint || undefined,
|
||||
commands: func.commands || undefined,
|
||||
scopes: func.scopes || undefined,
|
||||
scopes: (func.scopes as Scopes[]) || undefined,
|
||||
installationId: func.installationId || undefined,
|
||||
providerRepositoryId: func.providerRepositoryId || undefined,
|
||||
providerBranch: selectedBranch,
|
||||
@@ -145,7 +145,7 @@
|
||||
logging: func.logging || undefined,
|
||||
entrypoint: func.entrypoint,
|
||||
commands: func.commands || undefined,
|
||||
scopes: func.scopes || undefined,
|
||||
scopes: (func.scopes as Scopes[]) || undefined,
|
||||
installationId: selectedInstallationId,
|
||||
providerRepositoryId: selectedRepository,
|
||||
providerBranch: 'main'
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import { Runtime, type Models } from '@appwrite.io/console';
|
||||
import { Runtime, type Models, type Scopes } from '@appwrite.io/console';
|
||||
import Link from '$lib/elements/link.svelte';
|
||||
import { Alert } from '@appwrite.io/pink-svelte';
|
||||
import { isStarterPlan, getChangePlanUrl } from '$lib/stores/billing';
|
||||
@@ -39,7 +39,7 @@
|
||||
logging: func.logging || undefined,
|
||||
entrypoint: func.entrypoint || undefined,
|
||||
commands: func.commands || undefined,
|
||||
scopes: func.scopes || undefined,
|
||||
scopes: (func.scopes as Scopes[]) || undefined,
|
||||
installationId: func.installationId || undefined,
|
||||
providerRepositoryId: func.providerRepositoryId || undefined,
|
||||
providerBranch: func.providerBranch || undefined,
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@
|
||||
import { func } from '../store';
|
||||
import InputSelect from '$lib/elements/forms/inputSelect.svelte';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import { Runtime, type Models } from '@appwrite.io/console';
|
||||
import { Runtime, type Models, type Scopes } from '@appwrite.io/console';
|
||||
import { Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
import Link from '$lib/elements/link.svelte';
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
logging: $func.logging || undefined,
|
||||
entrypoint: entrypoint || undefined,
|
||||
commands: $func.commands || undefined,
|
||||
scopes: $func.scopes || undefined,
|
||||
scopes: ($func.scopes as Scopes[]) || undefined,
|
||||
installationId: $func.installationId || undefined,
|
||||
providerRepositoryId: $func.providerRepositoryId || undefined,
|
||||
providerBranch: $func.providerBranch || undefined,
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { func } from '../store';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import { Runtime } from '@appwrite.io/console';
|
||||
import { Runtime, type Scopes } from '@appwrite.io/console';
|
||||
import { Link } from '$lib/elements';
|
||||
import { parseExpression } from 'cron-parser';
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
logging: $func.logging || undefined,
|
||||
entrypoint: $func.entrypoint || undefined,
|
||||
commands: $func.commands || undefined,
|
||||
scopes: $func.scopes || undefined,
|
||||
scopes: ($func.scopes as Scopes[]) || undefined,
|
||||
installationId: $func.installationId || undefined,
|
||||
providerRepositoryId: $func.providerRepositoryId || undefined,
|
||||
providerBranch: $func.providerBranch || undefined,
|
||||
|
||||
+4
-4
@@ -10,16 +10,16 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { func } from '../store';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import { Runtime } from '@appwrite.io/console';
|
||||
import { Runtime, type Scopes as ScopesType } from '@appwrite.io/console';
|
||||
import Scopes from '$routes/(console)/project-[region]-[project]/overview/api-keys/scopes.svelte';
|
||||
import { symmetricDifference } from '$lib/helpers/array';
|
||||
import { Link } from '$lib/elements';
|
||||
|
||||
const functionId = page.params.function;
|
||||
let functionScopes: string[] = null;
|
||||
let functionScopes: ScopesType[] = null;
|
||||
|
||||
onMount(async () => {
|
||||
functionScopes ??= $func.scopes;
|
||||
functionScopes ??= $func.scopes as ScopesType[];
|
||||
});
|
||||
|
||||
async function updateScopes() {
|
||||
@@ -39,7 +39,7 @@
|
||||
logging: $func.logging || undefined,
|
||||
entrypoint: $func.entrypoint || undefined,
|
||||
commands: $func.commands || undefined,
|
||||
scopes: functionScopes,
|
||||
scopes: functionScopes || undefined,
|
||||
installationId: $func.installationId || undefined,
|
||||
providerRepositoryId: $func.providerRepositoryId || undefined,
|
||||
providerBranch: $func.providerBranch || undefined,
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { func } from '../store';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import { Runtime } from '@appwrite.io/console';
|
||||
import { Runtime, type Scopes } from '@appwrite.io/console';
|
||||
|
||||
const functionId = page.params.function;
|
||||
let timeout: number = null;
|
||||
@@ -36,7 +36,7 @@
|
||||
logging: $func.logging || undefined,
|
||||
entrypoint: $func.entrypoint || undefined,
|
||||
commands: $func.commands || undefined,
|
||||
scopes: $func.scopes || undefined,
|
||||
scopes: ($func.scopes as Scopes[]) || undefined,
|
||||
installationId: $func.installationId || undefined,
|
||||
providerRepositoryId: $func.providerRepositoryId || undefined,
|
||||
providerBranch: $func.providerBranch || undefined,
|
||||
|
||||
@@ -137,7 +137,8 @@
|
||||
hideView
|
||||
hasSearch
|
||||
hasFilters
|
||||
bind:view={data.view}
|
||||
filtersStyle="dropdown"
|
||||
view={data.view}
|
||||
analyticsSource="messaging_messages"
|
||||
searchPlaceholder="Search by description, type, status, or ID">
|
||||
{#if $canWriteMessages}
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
view={View.Table}
|
||||
hideView
|
||||
hasFilters
|
||||
hasCustomFiltersOnly
|
||||
hasSearch
|
||||
filtersStyle="dropdown"
|
||||
analyticsSource="messaging_providers"
|
||||
searchPlaceholder="Search by name or ID">
|
||||
{#if $canWriteProviders}
|
||||
|
||||
@@ -70,8 +70,8 @@
|
||||
view={View.Table}
|
||||
hideView
|
||||
hasFilters
|
||||
hasCustomFiltersOnly
|
||||
hasSearch
|
||||
filtersStyle="dropdown"
|
||||
analyticsSource="messaging_topics_filter"
|
||||
searchPlaceholder="Search by name or ID">
|
||||
{#if $canWriteTopics}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { writable } from 'svelte/store';
|
||||
import Scopes from '../api-keys/scopes.svelte';
|
||||
import { type Scopes as ScopesType } from '@appwrite.io/console';
|
||||
import { page } from '$app/state';
|
||||
import { copy } from '$lib/helpers/copy';
|
||||
|
||||
@@ -23,7 +24,7 @@
|
||||
let formComponent: Form;
|
||||
let isSubmitting = writable(false);
|
||||
|
||||
let scopes: string[] = [];
|
||||
let scopes: ScopesType[] = [];
|
||||
let name = '';
|
||||
let expire: string | null = null;
|
||||
|
||||
|
||||
+3
-3
@@ -12,7 +12,7 @@
|
||||
import { project } from '../../store';
|
||||
import Delete from './delete.svelte';
|
||||
import UpdateExpirationDate from './updateExpirationDate.svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import type { Models, Scopes as ScopesType } from '@appwrite.io/console';
|
||||
import { symmetricDifference } from '$lib/helpers/array';
|
||||
import Scopes from '../api-keys/scopes.svelte';
|
||||
import { InteractiveText, Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
@@ -22,7 +22,7 @@
|
||||
export let keyType: 'api' | 'dev' = 'api';
|
||||
|
||||
let name: string = null;
|
||||
let scopes: string[] = null;
|
||||
let scopes: ScopesType[] = null;
|
||||
|
||||
let showDelete = false;
|
||||
const isApiKey = keyType === 'api';
|
||||
@@ -33,7 +33,7 @@
|
||||
onMount(() => {
|
||||
name ??= key.name;
|
||||
if (isApiKey) {
|
||||
scopes ??= (key as Models.Key).scopes;
|
||||
scopes ??= (key as Models.Key).scopes as ScopesType[];
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { Alert } from '@appwrite.io/pink-svelte';
|
||||
import { ExpirationInput } from '$lib/components';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import type { Models, Scopes } from '@appwrite.io/console';
|
||||
import { page } from '$app/state';
|
||||
|
||||
export let keyType: 'api' | 'dev' = 'api';
|
||||
@@ -32,7 +32,7 @@
|
||||
projectId,
|
||||
keyId: key.$id,
|
||||
name: key.name,
|
||||
scopes: (key as Models.Key).scopes,
|
||||
scopes: (key as Models.Key).scopes as Scopes[],
|
||||
expire: expiration
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -32,8 +32,9 @@
|
||||
import { symmetricDifference } from '$lib/helpers/array';
|
||||
import { scopes as allScopes, cloudOnlyBackupScopes } from '$lib/constants';
|
||||
import { Accordion, Divider, Layout, Selector } from '@appwrite.io/pink-svelte';
|
||||
import type { Scopes } from '@appwrite.io/console';
|
||||
|
||||
export let scopes: string[];
|
||||
export let scopes: Scopes[];
|
||||
|
||||
const baseFilteredScopes = allScopes.filter((scope) => {
|
||||
const val = scope.scope;
|
||||
@@ -159,7 +160,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
function generateSyncedScopes(activeScopesObj: Record<string, boolean>): string[] {
|
||||
function generateSyncedScopes(activeScopesObj: Record<string, boolean>): Scopes[] {
|
||||
const result = new Set<string>();
|
||||
|
||||
Object.entries(activeScopesObj).forEach(([scope, isActive]) => {
|
||||
@@ -173,7 +174,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(result);
|
||||
return Array.from(result) as Scopes[];
|
||||
}
|
||||
|
||||
$: {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
import Details from './details.svelte';
|
||||
import ExportModal from './exportModal.svelte';
|
||||
import { readOnly } from '$lib/stores/billing';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { Scopes, type Models } from '@appwrite.io/console';
|
||||
import { canWriteProjects } from '$lib/stores/roles';
|
||||
import {
|
||||
IconCloud,
|
||||
@@ -90,23 +90,23 @@
|
||||
projectId: $project.$id,
|
||||
name: `[AUTO-GENERATED] Migration ${new Date().toISOString()}`,
|
||||
scopes: [
|
||||
'users.read',
|
||||
'teams.read',
|
||||
'databases.read',
|
||||
'collections.read' /* legacy */,
|
||||
'attributes.read' /* legacy */,
|
||||
'indexes.read',
|
||||
'documents.read' /* legacy */,
|
||||
'tables.read',
|
||||
'columns.read',
|
||||
'rows.read',
|
||||
'files.read',
|
||||
'buckets.read',
|
||||
'functions.read',
|
||||
'execution.read',
|
||||
'locale.read',
|
||||
'avatars.read',
|
||||
'health.read'
|
||||
Scopes.UsersRead,
|
||||
Scopes.TeamsRead,
|
||||
Scopes.DatabasesRead,
|
||||
Scopes.CollectionsRead /* legacy */,
|
||||
Scopes.AttributesRead /* legacy */,
|
||||
Scopes.IndexesRead,
|
||||
Scopes.DocumentsRead /* legacy */,
|
||||
Scopes.TablesRead,
|
||||
Scopes.ColumnsRead,
|
||||
Scopes.RowsRead,
|
||||
Scopes.FilesRead,
|
||||
Scopes.BucketsRead,
|
||||
Scopes.FunctionsRead,
|
||||
Scopes.ExecutionRead,
|
||||
Scopes.LocaleRead,
|
||||
Scopes.AvatarsRead,
|
||||
Scopes.HealthRead
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
+18
-17
@@ -10,6 +10,7 @@
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { user } from '$lib/stores/user';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { Scopes } from '@appwrite.io/console';
|
||||
|
||||
export let show = false;
|
||||
|
||||
@@ -91,23 +92,23 @@
|
||||
projectId: $project.$id,
|
||||
name: `[AUTO-GENERATED] Migration ${new Date().toISOString()}`,
|
||||
scopes: [
|
||||
'users.read',
|
||||
'teams.read',
|
||||
'databases.read',
|
||||
'collections.read' /* legacy */,
|
||||
'attributes.read' /* legacy */,
|
||||
'indexes.read',
|
||||
'documents.read' /* legacy */,
|
||||
'tables.read',
|
||||
'columns.read',
|
||||
'rows.read',
|
||||
'files.read',
|
||||
'buckets.read',
|
||||
'functions.read',
|
||||
'execution.read',
|
||||
'locale.read',
|
||||
'avatars.read',
|
||||
'health.read'
|
||||
Scopes.UsersRead,
|
||||
Scopes.TeamsRead,
|
||||
Scopes.DatabasesRead,
|
||||
Scopes.CollectionsRead /* legacy */,
|
||||
Scopes.AttributesRead /* legacy */,
|
||||
Scopes.IndexesRead,
|
||||
Scopes.DocumentsRead /* legacy */,
|
||||
Scopes.TablesRead,
|
||||
Scopes.ColumnsRead,
|
||||
Scopes.RowsRead,
|
||||
Scopes.FilesRead,
|
||||
Scopes.BucketsRead,
|
||||
Scopes.FunctionsRead,
|
||||
Scopes.ExecutionRead,
|
||||
Scopes.LocaleRead,
|
||||
Scopes.AvatarsRead,
|
||||
Scopes.HealthRead
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<ResponsiveContainerHeader
|
||||
{columns}
|
||||
hasSearch
|
||||
bind:view={data.view}
|
||||
view={data.view}
|
||||
searchPlaceholder="Search by name or ID">
|
||||
{#if $canWriteBuckets}
|
||||
<Tooltip disabled={!isLimited} maxWidth="200px">
|
||||
|
||||
+6
-5
@@ -19,15 +19,15 @@
|
||||
const options = units.map((v) => ({ label: v.name, value: v.name }));
|
||||
$: selectedUnit = $unit;
|
||||
|
||||
const maxValue = function formMaxFileSize() {
|
||||
return (service * 1000 * 1000) / units.find((unit) => unit.name === selectedUnit).value;
|
||||
};
|
||||
$: maxValue = (service * 1000 * 1000) / units.find((unit) => unit.name === selectedUnit).value;
|
||||
|
||||
$: step = $unit === 'Gigabytes' ? 0.01 : 1;
|
||||
|
||||
function updateMaxSize() {
|
||||
updateBucket(
|
||||
bucket,
|
||||
{
|
||||
maximumFileSize: $baseValue
|
||||
maximumFileSize: Math.round($baseValue)
|
||||
},
|
||||
{
|
||||
trackEventName: Submit.BucketUpdateSize,
|
||||
@@ -80,7 +80,8 @@
|
||||
disabled={$readOnly && !GRACE_PERIOD_OVERRIDE}
|
||||
placeholder={bucket.maximumFileSize.toString()}
|
||||
min={0}
|
||||
max={isCloud ? maxValue() : Infinity}
|
||||
{step}
|
||||
max={isCloud ? maxValue : Infinity}
|
||||
bind:value={$value} />
|
||||
<InputSelect required id="bytes" label="Bytes" {options} bind:value={$unit} />
|
||||
</svelte:fragment>
|
||||
|
||||
Reference in New Issue
Block a user