feat: some work on relationship modal

This commit is contained in:
Arman
2023-03-01 17:58:46 +01:00
parent f7b4a3c367
commit c0c8dd657e
8 changed files with 247 additions and 188 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
import { writable, readable } from 'svelte/store';
import { writable } from 'svelte/store';
import type { SvelteComponent } from 'svelte';
import { browser } from '$app/environment';
import { PAGE_LIMIT } from '$lib/constants';
@@ -0,0 +1,15 @@
import { writable } from 'svelte/store';
export type Column = {
id: string;
name: string;
show: boolean;
width?: number;
};
export const columns = writable<Column[]>([
{ id: '$id', name: 'Database ID', show: true, width: 50 },
{ id: 'name', name: 'Name', show: true, width: 120 },
{ id: '$createdAt', name: 'Created', show: true, width: 120 },
{ id: '$updatedAt', name: 'Updated', show: true, width: 120 }
]);
@@ -18,9 +18,11 @@
import { collection } from '../store';
import { page } from '$app/stores';
import { PAGE_LIMIT } from '$lib/constants';
import CreateAttribute from '../createAttribute.svelte';
import { tooltip } from '$lib/actions/tooltip';
export let data: PageData;
let showCreateAttribute = false;
function openWizard() {
wizard.start(Create);
@@ -85,56 +87,66 @@
</Button>
</div>
{#if data.documents.total}
<TableScroll isSticky>
<TableHeader>
<TableCellHead eyebrow={false}>Document ID</TableCellHead>
{#each columns as column}
<TableCellHead eyebrow={false}>{column.title}</TableCellHead>
{/each}
</TableHeader>
<TableBody>
{#each data.documents.documents as document}
<TableRowLink
href={`${base}/console/project-${$page.params.project}/databases/database-${$page.params.database}/collection-${$collection.$id}/document-${document.$id}`}>
<TableCell width={230}>
<Copy value={document.$id}>
<Pill button>
<span class="icon-duplicate" aria-hidden="true" />
<span class="text u-trim-start">{document.$id}</span>
</Pill>
</Copy>
</TableCell>
{#each columns as column}
{@const formatted = formatColumn(document[column.key])}
<TableCell>
<div
use:tooltip={{
content: formatted.whole,
disabled: !formatted.truncated
}}>
{formatted.value}
</div>
{#if $collection?.attributes?.length}
{#if data.documents.total}
<TableScroll isSticky>
<TableHeader>
<TableCellHead eyebrow={false}>Document ID</TableCellHead>
{#each columns as column}
<TableCellHead eyebrow={false}>{column.title}</TableCellHead>
{/each}
</TableHeader>
<TableBody>
{#each data.documents.documents as document}
<TableRowLink
href={`${base}/console/project-${$page.params.project}/databases/database-${$page.params.database}/collection-${$collection.$id}/document-${document.$id}`}>
<TableCell width={230}>
<Copy value={document.$id}>
<Pill button>
<span class="icon-duplicate" aria-hidden="true" />
<span class="text u-trim-start">{document.$id}</span>
</Pill>
</Copy>
</TableCell>
{/each}
</TableRowLink>
{/each}
</TableBody>
</TableScroll>
{#each columns as column}
{@const formatted = formatColumn(document[column.key])}
<TableCell>
<div
use:tooltip={{
content: formatted.whole,
disabled: !formatted.truncated
}}>
{formatted.value}
</div>
</TableCell>
{/each}
</TableRowLink>
{/each}
</TableBody>
</TableScroll>
<div class="u-flex common-section u-main-space-between">
<p class="text">Total results: {data.documents.total}</p>
<Pagination
limit={PAGE_LIMIT}
path={`/console/project-${$page.params.project}/databases/database-${$page.params.database}/collection-${$page.params.collection}`}
offset={data.offset}
sum={data.documents.total} />
</div>
<div class="u-flex common-section u-main-space-between">
<p class="text">Total results: {data.documents.total}</p>
<Pagination
limit={PAGE_LIMIT}
path={`/console/project-${$page.params.project}/databases/database-${$page.params.database}/collection-${$page.params.collection}`}
offset={data.offset}
sum={data.documents.total} />
</div>
{:else}
<Empty
single
href="https://appwrite.io/docs/databases#create-documents"
target="document"
on:click={openWizard} />
{/if}
{:else}
<Empty
single
href="https://appwrite.io/docs/databases#create-documents"
target="document"
on:click={openWizard} />
href="https://appwrite.io/docs/databases#attributes"
target="attribute"
on:click={() => (showCreateAttribute = true)} />
{/if}
</Container>
<CreateAttribute bind:showCreate={showCreateAttribute} />
@@ -17,6 +17,7 @@
import CreateIndex from '../indexes/createIndex.svelte';
import Delete from './deleteAttribute.svelte';
import Overview from './overview.svelte';
import { options } from './store';
let showCreateDropdown = false;
let showDropdown = [];
@@ -26,19 +27,6 @@
let showDelete = false;
let showOverview = false;
let showCreateIndex = false;
const attributesList = [
{ name: 'String', icon: 'text' },
{ name: 'Integer', icon: 'hashtag' },
{ name: 'Float', icon: 'hashtag' },
{ name: 'Boolean', icon: 'toggle' },
{ name: 'Datetime', icon: 'calendar' },
{ name: 'Email', icon: 'mail' },
{ name: 'IP', icon: 'location-marker' },
{ name: 'URL', icon: 'link' },
{ name: 'Enum', icon: 'list' },
{ name: 'Relationship', icon: 'relationship' }
];
</script>
<Container>
@@ -53,7 +41,7 @@
<span class="text">Create attribute</span>
</Button>
<svelte:fragment slot="list">
{#each attributesList as attribute}
{#each options as attribute}
<DropListItem
icon={attribute.icon}
on:click={() => {
@@ -21,7 +21,9 @@
</script>
<script lang="ts">
import { InputNumber, InputText, InputChoice } from '$lib/elements/forms';
import { InputText, InputChoice, InputSelect } from '$lib/elements/forms';
import { onMount } from 'svelte';
import { database } from '../../store';
export let selectedAttribute: Models.AttributeString;
export let data: Partial<Models.AttributeString> = {
@@ -31,6 +33,12 @@
array: false
};
let isOneWay = true;
onMount(async () => {
console.log($database);
});
$: if (selectedAttribute) {
({
required: data.required,
@@ -44,30 +52,33 @@
}
</script>
<InputNumber
id="size"
label="Size"
bind:value={data.size}
required
readonly={!!selectedAttribute} />
<InputText
id="default"
label="Default value"
bind:value={data.default}
maxlength={data.size}
disabled={data.required || data.array}
readonly={!!selectedAttribute} />
<InputChoice
id="required"
label="Required"
bind:value={data.required}
disabled={!!selectedAttribute || data.array}>
Indicate whether this is a required attribute
</InputChoice>
<InputChoice
id="array"
label="Array"
bind:value={data.array}
disabled={!!selectedAttribute || data.required}>
Indicate whether this attribute should act as an array
</InputChoice>
{#if isOneWay}
<!-- <InputSelect id="related" label="Size" bind:value={data.size} required /> -->
<InputText
id="default"
label="Default value"
bind:value={data.default}
maxlength={data.size}
disabled={data.required || data.array}
readonly={!!selectedAttribute} />
<div>
<InputText
id="key"
label="Attribute Key"
placeholder="Enter Key"
bind:value={data.key}
autofocus
required />
<div class="u-flex u-gap-4 u-margin-block-start-8 u-small">
<span
class="icon-info u-cross-center u-margin-block-start-2 u-line-height-1 u-icon-small"
aria-hidden="true" />
<span class="text u-line-height-1-5">
Allowed characters: alphanumeric, hyphen, non-leading underscore, period
</span>
</div>
</div>
{:else}
2
{/if}
@@ -23,6 +23,7 @@ export type Option = {
data: Partial<Attributes>
) => Promise<void>;
format?: 'email' | 'ip' | 'url' | 'enum' | 'relationship';
icon: string;
};
export const options: Option[] = [
@@ -30,66 +31,76 @@ export const options: Option[] = [
name: 'String',
component: String,
type: 'string',
func: submitString
func: submitString,
icon: 'text'
},
{
name: 'Integer',
component: Integer,
type: 'integer',
func: submitInteger
func: submitInteger,
icon: 'hashtag'
},
{
name: 'Float',
component: Float,
type: 'double',
func: submitFloat
func: submitFloat,
icon: 'hashtag'
},
{
name: 'Boolean',
component: Boolean,
type: 'boolean',
func: submitBoolean
func: submitBoolean,
icon: 'toggle'
},
{
name: 'Datetime',
component: Datetime,
type: 'datetime',
func: submitDatetime
func: submitDatetime,
icon: 'calendar'
},
{
name: 'Email',
component: Email,
type: 'string',
format: 'email',
func: submitEmail
func: submitEmail,
icon: 'mail'
},
{
name: 'IP',
component: Ip,
type: 'string',
format: 'ip',
func: submitIp
func: submitIp,
icon: 'location-marker'
},
{
name: 'URL',
component: Url,
type: 'string',
format: 'url',
func: submitUrl
func: submitUrl,
icon: 'link'
},
{
name: 'Enum',
component: Enum,
type: 'string',
format: 'enum',
func: submitEnum
func: submitEnum,
icon: 'list'
},
{
name: 'Relationship',
component: Relationship,
type: 'relationship',
format: 'relationship',
func: submitRelationship
func: submitRelationship,
icon: 'link'
}
];
@@ -9,9 +9,10 @@
import { base } from '$app/paths';
import type { Attributes } from './store';
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
import { Pill } from '$lib/elements';
export let showCreate = false;
export let selectedOption = null;
export let selectedOption: string = null;
let key: string = null;
let data: Partial<Attributes> = {
@@ -62,28 +63,34 @@
}
</script>
<Modal size="big" bind:show={showCreate} on:submit={submit}>
<svelte:fragment slot="header">Create Attribute</svelte:fragment>
<Modal size="big" bind:show={showCreate} on:submit={submit} icon={$option?.icon}>
<svelte:fragment slot="header"
>{selectedOption}
{#if selectedOption === 'Relationship'}
<Pill>BETA</Pill>
{/if}
</svelte:fragment>
<FormList>
<div>
<InputText
id="key"
label="Attribute Key"
placeholder="Enter Key"
bind:value={key}
autofocus
required />
{#if selectedOption !== 'Relationship'}
<div>
<InputText
id="key"
label="Attribute Key"
placeholder="Enter Key"
bind:value={key}
autofocus
required />
<div class="u-flex u-gap-4 u-margin-block-start-8 u-small">
<span
class="icon-info u-cross-center u-margin-block-start-2 u-line-height-1 u-icon-small"
aria-hidden="true" />
<span class="text u-line-height-1-5">
Allowed characters: alphanumeric, hyphen, non-leading underscore, period
</span>
<div class="u-flex u-gap-4 u-margin-block-start-8 u-small">
<span
class="icon-info u-cross-center u-margin-block-start-2 u-line-height-1 u-icon-small"
aria-hidden="true" />
<span class="text u-line-height-1-5">
Allowed characters: alphanumeric, hyphen, non-leading underscore, period
</span>
</div>
</div>
</div>
{/if}
{#if selectedOption}
<svelte:component
this={$option.component}
@@ -15,6 +15,7 @@
import Delete from './deleteIndex.svelte';
import Create from './createIndex.svelte';
import Overview from './overviewIndex.svelte';
import CreateAttribute from '../createAttribute.svelte';
import type { Models } from '@aw-labs/appwrite-console';
import { invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
@@ -25,6 +26,7 @@
let showCreateIndex = false;
let showOverview = false;
let showDelete = false;
let showCreateAttribute = false;
const handleDelete = async () => {
invalidate(Dependencies.COLLECTION);
@@ -43,79 +45,90 @@
<span class="text">Create index</span>
</Button>
</div>
{#if $indexes.length}
<Table>
<TableHeader>
<TableCellHead>Key</TableCellHead>
<TableCellHead onlyDesktop>Type</TableCellHead>
<TableCellHead onlyDesktop>Attributes</TableCellHead>
<TableCellHead onlyDesktop>Asc/Desc</TableCellHead>
<TableCellHead width={30} />
</TableHeader>
<TableBody>
{#each $indexes as index, i}
<TableRow>
<TableCell title="Key">
<div class="u-flex u-main-space-between">
<span class="text u-trim"> {index.key}</span>
{#if index.status !== 'available'}
<Pill
warning={index.status === 'processing'}
danger={['deleting', 'stuck', 'failed'].includes(
index.status
)}>
{index.status}
</Pill>
{/if}
</div>
</TableCell>
<TableCellText title="Type" onlyDesktop>{index.type}</TableCellText>
<TableCellText title="Attributes" onlyDesktop>
{index.attributes}
</TableCellText>
<TableCellText title="ASC/DESC" onlyDesktop>
{index.orders}
</TableCellText>
<TableCell showOverflow>
<DropList bind:show={showDropdown[i]} placement="bottom-start" noArrow>
<button
class="button is-only-icon is-text"
aria-label="More options"
on:click|preventDefault={() => {
showDropdown[i] = !showDropdown[i];
}}>
<span class="icon-dots-horizontal" aria-hidden="true" />
</button>
<svelte:fragment slot="list">
<DropListItem
icon="eye"
on:click={() => {
selectedIndex = index;
showOverview = true;
}}>Overview</DropListItem>
{#if $collection?.attributes?.length}
{#if $indexes.length}
<Table>
<TableHeader>
<TableCellHead>Key</TableCellHead>
<TableCellHead onlyDesktop>Type</TableCellHead>
<TableCellHead onlyDesktop>Attributes</TableCellHead>
<TableCellHead onlyDesktop>Asc/Desc</TableCellHead>
<TableCellHead width={30} />
</TableHeader>
<TableBody>
{#each $indexes as index, i}
<TableRow>
<TableCell title="Key">
<div class="u-flex u-main-space-between">
<span class="text u-trim"> {index.key}</span>
{#if index.status !== 'available'}
<Pill
warning={index.status === 'processing'}
danger={['deleting', 'stuck', 'failed'].includes(
index.status
)}>
{index.status}
</Pill>
{/if}
</div>
</TableCell>
<TableCellText title="Type" onlyDesktop>{index.type}</TableCellText>
<TableCellText title="Attributes" onlyDesktop>
{index.attributes}
</TableCellText>
<TableCellText title="ASC/DESC" onlyDesktop>
{index.orders}
</TableCellText>
<TableCell showOverflow>
<DropList
bind:show={showDropdown[i]}
placement="bottom-start"
noArrow>
<button
class="button is-only-icon is-text"
aria-label="More options"
on:click|preventDefault={() => {
showDropdown[i] = !showDropdown[i];
}}>
<span class="icon-dots-horizontal" aria-hidden="true" />
</button>
<svelte:fragment slot="list">
<DropListItem
icon="eye"
on:click={() => {
selectedIndex = index;
showOverview = true;
}}>Overview</DropListItem>
<DropListItem
icon="trash"
on:click={() => {
selectedIndex = index;
showDelete = true;
}}>Delete</DropListItem>
</svelte:fragment>
</DropList>
</TableCell>
</TableRow>
{/each}
</TableBody>
</Table>
<div class="u-flex u-margin-block-start-32 u-main-space-between">
<p class="text">Total results: {$indexes.length}</p>
</div>
<DropListItem
icon="trash"
on:click={() => {
selectedIndex = index;
showDelete = true;
}}>Delete</DropListItem>
</svelte:fragment>
</DropList>
</TableCell>
</TableRow>
{/each}
</TableBody>
</Table>
<div class="u-flex u-margin-block-start-32 u-main-space-between">
<p class="text">Total results: {$indexes.length}</p>
</div>
{:else}
<Empty
single
href="https://appwrite.io/docs/databases#indexes"
target="index"
on:click={() => (showCreateIndex = true)} />
{/if}
{:else}
<Empty
single
href="https://appwrite.io/docs/databases#indexes"
target="index"
on:click={() => (showCreateIndex = true)} />
target="attribute"
href="https://appwrite.io/docs/databases#attributes"
on:click={() => (showCreateAttribute = true)} />
{/if}
</Container>
@@ -125,3 +138,5 @@
<Delete bind:showDelete {selectedIndex} on:deleted={handleDelete} />
<Overview bind:showOverview {selectedIndex} />
{/if}
<CreateAttribute bind:showCreate={showCreateAttribute} />