mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
+1
-1
@@ -1,5 +1,5 @@
|
||||
PUBLIC_CONSOLE_MODE=self-hosted
|
||||
PUBLIC_CONSOLE_FEATURE_FLAGS=
|
||||
PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import,attribute-encrypt
|
||||
PUBLIC_APPWRITE_MULTI_REGION=false
|
||||
PUBLIC_APPWRITE_ENDPOINT=http://localhost/v1
|
||||
PUBLIC_STRIPE_KEY=
|
||||
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
"PUBLIC_CONSOLE_MODE=cloud"
|
||||
"PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import"
|
||||
"PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import,attribute-encrypt"
|
||||
"PUBLIC_APPWRITE_MULTI_REGION=true"
|
||||
"PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}"
|
||||
"PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY_STAGE }}"
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
build-args: |
|
||||
"PUBLIC_CONSOLE_MODE=self-hosted"
|
||||
"PUBLIC_APPWRITE_MULTI_REGION=false"
|
||||
"PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import"
|
||||
"PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import,attribute-encrypt"
|
||||
"PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}"
|
||||
|
||||
publish-cloud-no-regions:
|
||||
@@ -156,6 +156,6 @@ jobs:
|
||||
build-args: |
|
||||
"PUBLIC_CONSOLE_MODE=cloud"
|
||||
"PUBLIC_APPWRITE_MULTI_REGION=false"
|
||||
"PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import"
|
||||
"PUBLIC_CONSOLE_FEATURE_FLAGS=sites,csv-import,attribute-encrypt"
|
||||
"PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY_STAGE }}"
|
||||
"PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}"
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/svelte": "^1.1.24",
|
||||
"@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@a5e5564",
|
||||
"@appwrite.io/console": "^1.9.0",
|
||||
"@appwrite.io/pink-icons": "0.25.0",
|
||||
"@appwrite.io/pink-icons-svelte": "^2.0.0-RC.1",
|
||||
"@appwrite.io/pink-legacy": "^1.0.3",
|
||||
|
||||
Generated
+9
-10
@@ -12,8 +12,8 @@ importers:
|
||||
specifier: ^1.1.24
|
||||
version: 1.1.24(svelte@5.25.3)(zod@3.24.3)
|
||||
'@appwrite.io/console':
|
||||
specifier: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@a5e5564
|
||||
version: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@a5e5564
|
||||
specifier: ^1.9.0
|
||||
version: 1.9.0
|
||||
'@appwrite.io/pink-icons':
|
||||
specifier: 0.25.0
|
||||
version: 0.25.0
|
||||
@@ -257,9 +257,8 @@ packages:
|
||||
'@analytics/type-utils@0.6.2':
|
||||
resolution: {integrity: sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg==}
|
||||
|
||||
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@a5e5564':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@a5e5564}
|
||||
version: 1.8.0
|
||||
'@appwrite.io/console@1.9.0':
|
||||
resolution: {integrity: sha512-g8+zfdBF8mz7tRUER4CGVe5FHWQVLp8TlY/UOGCZ2TgUF1qnpNu/DhiQgph8uF+QZ9jDKCLAAZlP5//9t7ckWA==}
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@ee1b7788cd3f877c9aa1b6487976bd63a6196870':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@ee1b7788cd3f877c9aa1b6487976bd63a6196870}
|
||||
@@ -1341,8 +1340,8 @@ packages:
|
||||
'@types/prop-types@15.7.14':
|
||||
resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==}
|
||||
|
||||
'@types/react@18.3.22':
|
||||
resolution: {integrity: sha512-vUhG0YmQZ7kL/tmKLrD3g5zXbXXreZXB3pmROW8bg3CnLnpjkRVwUlLne7Ufa2r9yJ8+/6B73RzhAek5TBKh2Q==}
|
||||
'@types/react@18.3.23':
|
||||
resolution: {integrity: sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==}
|
||||
|
||||
'@types/remarkable@2.0.8':
|
||||
resolution: {integrity: sha512-eKXqPZfpQl1kOADjdKchHrp2gwn9qMnGXhH/AtZe0UrklzhGJkawJo/Y/D0AlWcdWoWamFNIum8+/nkAISQVGg==}
|
||||
@@ -3636,7 +3635,7 @@ snapshots:
|
||||
|
||||
'@analytics/type-utils@0.6.2': {}
|
||||
|
||||
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@a5e5564': {}
|
||||
'@appwrite.io/console@1.9.0': {}
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@ee1b7788cd3f877c9aa1b6487976bd63a6196870(svelte@5.25.3)':
|
||||
dependencies:
|
||||
@@ -4792,7 +4791,7 @@ snapshots:
|
||||
|
||||
'@types/prop-types@15.7.14': {}
|
||||
|
||||
'@types/react@18.3.22':
|
||||
'@types/react@18.3.23':
|
||||
dependencies:
|
||||
'@types/prop-types': 15.7.14
|
||||
csstype: 3.1.3
|
||||
@@ -6753,7 +6752,7 @@ snapshots:
|
||||
|
||||
svelte-motion@0.12.2(svelte@5.25.3):
|
||||
dependencies:
|
||||
'@types/react': 18.3.22
|
||||
'@types/react': 18.3.23
|
||||
framesync: 6.1.2
|
||||
popmotion: 11.0.5
|
||||
style-value-types: 5.1.2
|
||||
|
||||
+2
-1
@@ -20,5 +20,6 @@ function isFlagEnabled(name: string) {
|
||||
|
||||
export const flags = {
|
||||
showSites: isFlagEnabled('sites'),
|
||||
showCsvImport: isFlagEnabled('csv-import')
|
||||
showCsvImport: isFlagEnabled('csv-import'),
|
||||
showAttributeEncrypt: isFlagEnabled('attribute-encrypt')
|
||||
};
|
||||
|
||||
@@ -44,6 +44,7 @@ export type Column = PinkColumn & {
|
||||
format?: string;
|
||||
exclude?: boolean;
|
||||
elements?: string[] | { value: string | number; label: string }[];
|
||||
encrypt?: boolean;
|
||||
};
|
||||
|
||||
export function isValueOfStringEnum<T extends Record<string, string>>(
|
||||
|
||||
@@ -310,6 +310,7 @@ export type Plan = {
|
||||
users: number;
|
||||
teams: number;
|
||||
databases: number;
|
||||
databasesAllowEncrypt: boolean;
|
||||
buckets: number;
|
||||
fileSize: number;
|
||||
functions: number;
|
||||
|
||||
+35
-20
@@ -13,10 +13,11 @@
|
||||
Link,
|
||||
Popover,
|
||||
Table,
|
||||
Tooltip,
|
||||
Typography
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import Create from '../createAttribute.svelte';
|
||||
import { isRelationship } from '../document-[document]/attributes/store';
|
||||
import { isRelationship, isString } from '../document-[document]/attributes/store';
|
||||
import FailedModal from '../failedModal.svelte';
|
||||
import CreateIndex from '../indexes/createIndex.svelte';
|
||||
import { attributes, type Attributes, isCsvImportInProgress } from '../store';
|
||||
@@ -33,7 +34,8 @@
|
||||
IconPlus,
|
||||
IconSwitchHorizontal,
|
||||
IconTrash,
|
||||
IconViewList
|
||||
IconViewList,
|
||||
IconLockClosed
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import type { ComponentProps } from 'svelte';
|
||||
import { Click, trackEvent } from '$lib/actions/analytics';
|
||||
@@ -112,25 +114,38 @@
|
||||
{:else}
|
||||
<Icon icon={option.icon} size="s" />
|
||||
{/if}
|
||||
<span class="text u-trim-1" data-private>{attribute.key}</span>
|
||||
{#if attribute.status !== 'available'}
|
||||
<Badge
|
||||
size="s"
|
||||
variant="secondary"
|
||||
content={attribute.status}
|
||||
type={getAttributeStatusBadge(attribute.status)} />
|
||||
{#if attribute.error}
|
||||
<Link.Button
|
||||
variant="muted"
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
error = attribute.error;
|
||||
showFailed = true;
|
||||
}}>Details</Link.Button>
|
||||
<Layout.Stack direction="row" alignItems="center" gap="s">
|
||||
<Layout.Stack inline direction="row" alignItems="center" gap="xxs">
|
||||
<span class="text u-trim-1" data-private>{attribute.key}</span>
|
||||
{#if isString(attribute) && attribute.encrypt}
|
||||
<Tooltip>
|
||||
<Icon
|
||||
size="s"
|
||||
icon={IconLockClosed}
|
||||
color="--fgcolor-neutral-tertiary" />
|
||||
<div slot="tooltip">Encrypted</div>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{#if attribute.status !== 'available'}
|
||||
<Badge
|
||||
size="s"
|
||||
variant="secondary"
|
||||
content={attribute.status}
|
||||
type={getAttributeStatusBadge(attribute.status)} />
|
||||
{#if attribute.error}
|
||||
<Link.Button
|
||||
variant="muted"
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
error = attribute.error;
|
||||
showFailed = true;
|
||||
}}>Details</Link.Button>
|
||||
{/if}
|
||||
{:else if attribute.required}
|
||||
<Badge size="xs" variant="secondary" content="required" />
|
||||
{/if}
|
||||
{:else if attribute.required}
|
||||
<Badge size="s" variant="secondary" content="required" />
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Table.Cell>
|
||||
<Table.Cell column="type" {root}>
|
||||
|
||||
+1
-2
@@ -100,5 +100,4 @@
|
||||
label="Array"
|
||||
bind:checked={data.array}
|
||||
disabled={data.required || editing}
|
||||
description="Indicate whether this attribute should act as an array, with the default value set as an empty
|
||||
array." />
|
||||
description="Indicate whether this attribute is an array. Defaults to an empty array." />
|
||||
|
||||
+1
-2
@@ -92,5 +92,4 @@
|
||||
label="Array"
|
||||
bind:checked={data.array}
|
||||
disabled={data.required || editing}
|
||||
description="Indicate whether this attribute should act as an array, with the default value set as an empty
|
||||
array." />
|
||||
description="Indicate whether this attribute is an array. Defaults to an empty array." />
|
||||
|
||||
+1
-2
@@ -93,5 +93,4 @@
|
||||
label="Array"
|
||||
bind:checked={data.array}
|
||||
disabled={data.required || editing}
|
||||
description="Indicate whether this attribute should act as an array, with the default value set as an empty
|
||||
array." />
|
||||
description="Indicate whether this attribute is an array. Defaults to an empty array." />
|
||||
|
||||
+1
-2
@@ -124,5 +124,4 @@
|
||||
label="Array"
|
||||
bind:checked={data.array}
|
||||
disabled={data.required || editing}
|
||||
description="Indicate whether this attribute should act as an array, with the default value set as an empty
|
||||
array." />
|
||||
description="Indicate whether this attribute is an array. Defaults to an empty array." />
|
||||
|
||||
+1
-2
@@ -122,5 +122,4 @@
|
||||
label="Array"
|
||||
bind:checked={data.array}
|
||||
disabled={data.required || editing}
|
||||
description="Indicate whether this attribute should act as an array, with the default value set as an empty
|
||||
array." />
|
||||
description="Indicate whether this attribute is an array. Defaults to an empty array." />
|
||||
|
||||
+1
-2
@@ -120,5 +120,4 @@
|
||||
label="Array"
|
||||
bind:checked={data.array}
|
||||
disabled={data.required || editing}
|
||||
description="Indicate whether this attribute should act as an array, with the default value set as an empty
|
||||
array." />
|
||||
description="Indicate whether this attribute is an array. Defaults to an empty array." />
|
||||
|
||||
+1
-2
@@ -92,5 +92,4 @@
|
||||
label="Array"
|
||||
bind:checked={data.array}
|
||||
disabled={data.required || editing}
|
||||
description="Indicate whether this attribute should act as an array, with the default value set as an empty
|
||||
array." />
|
||||
description="Indicate whether this attribute is an array. Defaults to an empty array." />
|
||||
|
||||
+98
-27
@@ -18,7 +18,8 @@
|
||||
data.size,
|
||||
data.required,
|
||||
data.default,
|
||||
data.array
|
||||
data.array,
|
||||
data.encrypt
|
||||
);
|
||||
}
|
||||
export async function updateString(
|
||||
@@ -42,20 +43,28 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { Selector } from '@appwrite.io/pink-svelte';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { upgradeURL } from '$lib/stores/billing';
|
||||
import { currentPlan } from '$lib/stores/organization';
|
||||
import { createConservative } from '$lib/helpers/stores';
|
||||
import { ActionMenu, Selector } from '@appwrite.io/pink-svelte';
|
||||
import { InputNumber, InputText, InputTextarea } from '$lib/elements/forms';
|
||||
import { Popover, Layout, Tag, Typography, Link } from '@appwrite.io/pink-svelte';
|
||||
import { flags } from '$lib/flags';
|
||||
|
||||
export let data: Partial<Models.AttributeString> = {
|
||||
required: false,
|
||||
size: 0,
|
||||
default: null,
|
||||
array: false
|
||||
array: false,
|
||||
encrypt: false
|
||||
};
|
||||
|
||||
export let editing = false;
|
||||
|
||||
let savedDefault = data.default;
|
||||
|
||||
let showEncrypt = flags.showAttributeEncrypt(page.data);
|
||||
|
||||
function handleDefaultState(hideDefault: boolean) {
|
||||
if (hideDefault) {
|
||||
savedDefault = data.default;
|
||||
@@ -73,48 +82,110 @@
|
||||
array: false,
|
||||
...data
|
||||
});
|
||||
|
||||
$: listen(data);
|
||||
|
||||
$: handleDefaultState($required || $array);
|
||||
|
||||
// Check plan on cloud, always allow on self-hosted
|
||||
$: supportsStringEncryption = isCloud ? $currentPlan?.databasesAllowEncrypt : true;
|
||||
</script>
|
||||
|
||||
<InputNumber
|
||||
id="size"
|
||||
label="Size"
|
||||
required
|
||||
placeholder="Enter size"
|
||||
bind:value={data.size}
|
||||
required={true} />
|
||||
{#if data.size >= 50}
|
||||
<InputTextarea
|
||||
id="default"
|
||||
label="Default"
|
||||
placeholder="Enter string"
|
||||
disabled={data.required || data.array}
|
||||
nullable={!data.required && !data.array}
|
||||
maxlength={data.size}
|
||||
bind:value={data.default} />
|
||||
{:else}
|
||||
<InputText
|
||||
id="default"
|
||||
label="Default"
|
||||
placeholder="Enter string"
|
||||
disabled={data.required || data.array}
|
||||
nullable={!data.required && !data.array}
|
||||
maxlength={data.size}
|
||||
bind:value={data.default} />
|
||||
{/if}
|
||||
min={supportsStringEncryption && data.encrypt ? 150 : undefined}
|
||||
helper={supportsStringEncryption && data.encrypt
|
||||
? 'Encrypted string attributes require a minimum size of 150.'
|
||||
: undefined} />
|
||||
|
||||
<svelte:component
|
||||
this={data.size >= 50 ? InputTextarea : InputText}
|
||||
id="default"
|
||||
label="Default"
|
||||
placeholder="Enter string"
|
||||
maxlength={data.size}
|
||||
bind:value={data.default}
|
||||
disabled={data.required || data.array}
|
||||
nullable={!data.required && !data.array} />
|
||||
|
||||
<Selector.Checkbox
|
||||
size="s"
|
||||
id="required"
|
||||
label="Required"
|
||||
bind:checked={data.required}
|
||||
disabled={data.array}
|
||||
description="Indicate whether this attribute is required" />
|
||||
description="Indicate whether this attribute is required." />
|
||||
|
||||
<Selector.Checkbox
|
||||
size="s"
|
||||
id="array"
|
||||
label="Array"
|
||||
bind:checked={data.array}
|
||||
disabled={data.required || editing}
|
||||
description="Indicate whether this attribute should act as an array, with the default value set as an empty
|
||||
array." />
|
||||
description="Indicate whether this attribute is an array. Defaults to an empty array." />
|
||||
|
||||
{#if showEncrypt}
|
||||
<Layout.Stack gap="xs" direction="column">
|
||||
<div class="popover-holder" class:disabled-checkbox={!supportsStringEncryption || editing}>
|
||||
<Layout.Stack inline gap="s" alignItems="flex-start" direction="row">
|
||||
<Popover let:toggle placement="bottom-start">
|
||||
<Selector.Checkbox
|
||||
size="s"
|
||||
id="encrypt"
|
||||
bind:checked={data.encrypt}
|
||||
disabled={!supportsStringEncryption || editing} />
|
||||
|
||||
<Layout.Stack gap="xxs" direction="column">
|
||||
<button
|
||||
type="button"
|
||||
class="u-cursor-pointer"
|
||||
on:click={(e) => {
|
||||
if (!supportsStringEncryption) {
|
||||
toggle(e);
|
||||
} else {
|
||||
data.encrypt = !data.encrypt;
|
||||
}
|
||||
}}>
|
||||
<Layout.Stack inline gap="xxs" direction="row" alignItems="center">
|
||||
<Typography.Text variant="m-500">Encrypted</Typography.Text>
|
||||
{#if !supportsStringEncryption}
|
||||
<Tag variant="default" size="xs" on:click={toggle}>Pro</Tag>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</button>
|
||||
<Typography.Text color="--fgcolor-neutral-tertiary">
|
||||
Indicate whether this attribute is encrypted. Encrypted attributes
|
||||
cannot be queried.
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
|
||||
<ActionMenu.Root width="180px" slot="tooltip">
|
||||
<Typography.Text variant="m-500">
|
||||
Available on Pro plan. <Link.Anchor href={$upgradeURL}
|
||||
>Upgrade</Link.Anchor>
|
||||
to enable encrypted attributes.
|
||||
</Typography.Text>
|
||||
</ActionMenu.Root>
|
||||
</Popover>
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.popover-holder {
|
||||
& :global([role='tooltip']) {
|
||||
margin-top: 4px;
|
||||
left: 8rem !important;
|
||||
}
|
||||
|
||||
// no cursor when disabled
|
||||
&.disabled-checkbox :global(button) {
|
||||
cursor: unset !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+1
-2
@@ -93,5 +93,4 @@
|
||||
label="Array"
|
||||
bind:checked={data.array}
|
||||
disabled={data.required || editing}
|
||||
description="Indicate whether this attribute should act as an array, with the default value set as an empty
|
||||
array." />
|
||||
description="Indicate whether this attribute is an array. Defaults to an empty array." />
|
||||
|
||||
+5
@@ -15,3 +15,8 @@ export function isRelationship(attribute: Attributes): attribute is Models.Attri
|
||||
if (!attribute) return false;
|
||||
return attribute?.type === 'relationship';
|
||||
}
|
||||
|
||||
export function isString(attribute: Attributes): attribute is Models.AttributeString {
|
||||
if (!attribute) return false;
|
||||
return attribute?.type === 'string';
|
||||
}
|
||||
|
||||
+6
-1
@@ -79,13 +79,15 @@
|
||||
{ id: 'type' },
|
||||
{ id: 'attributes' },
|
||||
{ id: 'orders' },
|
||||
{ id: 'lengths' },
|
||||
{ id: 'actions', width: 40 }
|
||||
]}>
|
||||
<svelte:fragment slot="header" let:root>
|
||||
<Table.Header.Cell column="key" {root}>Key</Table.Header.Cell>
|
||||
<Table.Header.Cell column="type" {root}>Type</Table.Header.Cell>
|
||||
<Table.Header.Cell column="attributes" {root}>Attributes</Table.Header.Cell>
|
||||
<Table.Header.Cell column="orders" {root}>Asc/Desc</Table.Header.Cell>
|
||||
<Table.Header.Cell column="orders" {root}>Orders</Table.Header.Cell>
|
||||
<Table.Header.Cell column="lengths" {root}>Lengths</Table.Header.Cell>
|
||||
<Table.Header.Cell column="actions" {root} />
|
||||
</svelte:fragment>
|
||||
{#each data.collection.indexes as index}
|
||||
@@ -118,6 +120,9 @@
|
||||
<Table.Cell column="orders" {root}>
|
||||
{index.orders}
|
||||
</Table.Cell>
|
||||
<Table.Cell column="lengths" {root}>
|
||||
{index.lengths}
|
||||
</Table.Cell>
|
||||
<Table.Cell column="actions" {root}>
|
||||
<Popover let:toggle padding="none" placement="bottom-end">
|
||||
<Button text icon ariaLabel="more options" on:click={toggle}>
|
||||
|
||||
+27
-16
@@ -5,7 +5,7 @@
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Modal } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, InputSelect, InputText } from '$lib/elements/forms';
|
||||
import { Button, InputNumber, InputSelect, InputText } from '$lib/elements/forms';
|
||||
import { remove } from '$lib/helpers/array';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
@@ -36,7 +36,7 @@
|
||||
label: attribute.key
|
||||
}));
|
||||
|
||||
let attributeList = [{ value: '', order: '' }];
|
||||
let attributeList = [{ value: '', order: '', length: null }];
|
||||
|
||||
function generateIndexKey() {
|
||||
let indexKeys = $indexes.map((index) => index.key);
|
||||
@@ -51,8 +51,8 @@
|
||||
|
||||
function initialize() {
|
||||
attributeList = externalAttribute
|
||||
? [{ value: externalAttribute.key, order: 'ASC' }]
|
||||
: [{ value: '', order: 'ASC' }];
|
||||
? [{ value: externalAttribute.key, order: 'ASC', length: null }]
|
||||
: [{ value: '', order: 'ASC', length: null }];
|
||||
selectedType = IndexType.Key;
|
||||
key = `index_${$indexes.length + 1}`;
|
||||
}
|
||||
@@ -78,8 +78,10 @@
|
||||
key,
|
||||
selectedType,
|
||||
attributeList.map((a) => a.value),
|
||||
attributeList.map((a) => a.order)
|
||||
attributeList.map((a) => a.order),
|
||||
attributeList.map((a) => (a.length ? Number(a.length) : null))
|
||||
);
|
||||
|
||||
await Promise.allSettled([
|
||||
invalidate(Dependencies.COLLECTION),
|
||||
invalidate(Dependencies.DATABASE)
|
||||
@@ -105,7 +107,7 @@
|
||||
if (addAttributeDisabled) return;
|
||||
|
||||
// We assign instead of pushing to trigger Svelte's reactivity
|
||||
attributeList = [...attributeList, { value: '', order: '' }];
|
||||
attributeList = [...attributeList, { value: '', order: '', length: null }];
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -134,17 +136,26 @@
|
||||
label={i === 0 ? 'Attribute' : undefined}
|
||||
placeholder="Select Attribute"
|
||||
bind:value={attribute.value} />
|
||||
|
||||
<InputSelect
|
||||
options={[
|
||||
{ value: 'ASC', label: 'ASC' },
|
||||
{ value: 'DESC', label: 'DESC' }
|
||||
]}
|
||||
required
|
||||
id={`order-${i}`}
|
||||
label={i === 0 ? 'Order' : undefined}
|
||||
bind:value={attribute.order}
|
||||
placeholder="Select Order" />
|
||||
|
||||
<Layout.Stack direction="row" alignItems="flex-end" gap="xs">
|
||||
<InputSelect
|
||||
options={[
|
||||
{ value: 'ASC', label: 'ASC' },
|
||||
{ value: 'DESC', label: 'DESC' }
|
||||
]}
|
||||
required
|
||||
id={`order-${i}`}
|
||||
label={i === 0 ? 'Order' : undefined}
|
||||
bind:value={attribute.order}
|
||||
placeholder="Select Order" />
|
||||
{#if selectedType === IndexType.Key}
|
||||
<InputNumber
|
||||
id={`length-${i}`}
|
||||
label={i === 0 ? 'Length' : undefined}
|
||||
placeholder="Enter Length"
|
||||
bind:value={attribute.length} />
|
||||
{/if}
|
||||
<Button
|
||||
icon
|
||||
compact
|
||||
|
||||
+6
@@ -39,6 +39,12 @@
|
||||
id={`value-${selectedIndex.orders[i]}`}
|
||||
value={selectedIndex.orders[i]}
|
||||
readonly />
|
||||
<InputText
|
||||
required
|
||||
label={i === 0 ? 'Length' : ''}
|
||||
id={`value-${selectedIndex.lengths[i]}`}
|
||||
value={selectedIndex.lengths[i]?.toString() ?? null}
|
||||
readonly />
|
||||
</Layout.Stack>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
+17
-2
@@ -12,7 +12,11 @@
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { afterUpdate, onMount } from 'svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { isRelationship, isRelationshipToMany } from './document-[document]/attributes/store';
|
||||
import {
|
||||
isRelationship,
|
||||
isRelationshipToMany,
|
||||
isString
|
||||
} from './document-[document]/attributes/store';
|
||||
import RelationshipsModal from './relationshipsModal.svelte';
|
||||
import { attributes, collection, columns } from './store';
|
||||
import type { ColumnType } from '$lib/helpers/types';
|
||||
@@ -23,11 +27,12 @@
|
||||
Link,
|
||||
Badge,
|
||||
FloatingActionBar,
|
||||
InteractiveText,
|
||||
Typography
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import DualTimeView from '$lib/components/dualTimeView.svelte';
|
||||
|
||||
import { flags } from '$lib/flags';
|
||||
export let data: PageData;
|
||||
|
||||
const databaseId = page.params.database;
|
||||
@@ -163,6 +168,8 @@
|
||||
) as Models.AttributeRelationship[];
|
||||
|
||||
let checked = false;
|
||||
|
||||
let showEncrypt = flags.showAttributeEncrypt(data);
|
||||
</script>
|
||||
|
||||
<Table.Root
|
||||
@@ -251,6 +258,14 @@
|
||||
<span slot="title">Timestamp</span>
|
||||
{toLocaleDateTime(formatted.whole, true, 'UTC')}
|
||||
</DualTimeView>
|
||||
{:else if isString(attr) && attr.encrypt && showEncrypt}
|
||||
<button on:click={(e) => e.preventDefault()}>
|
||||
<InteractiveText
|
||||
copy={false}
|
||||
variant="secret"
|
||||
isVisible={false}
|
||||
text={formatted.value} />
|
||||
</button>
|
||||
{:else if formatted.truncated}
|
||||
<Tooltip placement="bottom" disabled={!formatted.truncated}>
|
||||
<Typography.Text truncate>{formatted.value}</Typography.Text>
|
||||
|
||||
Reference in New Issue
Block a user