mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge pull request #1834 from appwrite/data-page
Update page url for documents
This commit is contained in:
+106
-113
@@ -1,144 +1,137 @@
|
||||
<script lang="ts">
|
||||
import { CardGrid, BoxAvatar, Heading, Alert } from '$lib/components';
|
||||
import { Container } from '$lib/layout';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { doc } from './store';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import Delete from './delete.svelte';
|
||||
import { symmetricDifference } from '$lib/helpers/array';
|
||||
import { Permissions } from '$lib/components/permissions';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { collection } from '../store';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { page } from '$app/stores';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { writable } from 'svelte/store';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { doc } from './store';
|
||||
import { collection, type Attributes } from '../store';
|
||||
import { Container } from '$lib/layout';
|
||||
import AttributeItem from './attributeItem.svelte';
|
||||
import { symmetricDifference } from '$lib/helpers/array';
|
||||
import { isRelationship, isRelationshipToMany } from './attributes/store';
|
||||
import { deepClone } from '$lib/helpers/object';
|
||||
|
||||
let showDelete = false;
|
||||
let permissions = $doc?.$permissions;
|
||||
let arePermsDisabled = true;
|
||||
let showPermissionAlert = true;
|
||||
const databaseId = $page.params.database;
|
||||
const collectionId = $page.params.collection;
|
||||
const documentId = $page.params.document;
|
||||
const editing = true;
|
||||
|
||||
async function updatePermissions() {
|
||||
function initWork() {
|
||||
const prohibitedKeys = [
|
||||
'$id',
|
||||
'$collection',
|
||||
'$collectionId',
|
||||
'$databaseId',
|
||||
'$createdAt',
|
||||
'$updatedAt'
|
||||
];
|
||||
|
||||
const filteredKeys = Object.keys($doc).filter((key) => {
|
||||
return !prohibitedKeys.includes(key);
|
||||
});
|
||||
|
||||
const result = filteredKeys.reduce((obj, key) => {
|
||||
obj[key] = $doc[key];
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
return writable(deepClone(result as Models.Document));
|
||||
}
|
||||
|
||||
const work = initWork();
|
||||
|
||||
async function updateData() {
|
||||
try {
|
||||
await sdk
|
||||
.forProject($page.params.region, $page.params.project)
|
||||
.databases.updateDocument(
|
||||
$doc.$databaseId,
|
||||
$doc.$collectionId,
|
||||
$doc.$id,
|
||||
$doc.data,
|
||||
permissions
|
||||
databaseId,
|
||||
collectionId,
|
||||
documentId,
|
||||
$work,
|
||||
$work.$permissions
|
||||
);
|
||||
await invalidate(Dependencies.DOCUMENT);
|
||||
arePermsDisabled = true;
|
||||
|
||||
invalidate(Dependencies.DOCUMENT);
|
||||
trackEvent(Submit.DocumentUpdate);
|
||||
addNotification({
|
||||
message: 'Permissions have been updated',
|
||||
message: 'Document has been updated',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent(Submit.DocumentUpdatePermissions);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
trackError(error, Submit.DocumentUpdatePermissions);
|
||||
trackError(error, Submit.DocumentUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
$: if (permissions) {
|
||||
if (symmetricDifference(permissions, $doc.$permissions).length) {
|
||||
arePermsDisabled = false;
|
||||
} else arePermsDisabled = true;
|
||||
function compareAttributes(
|
||||
attribute: Attributes,
|
||||
$work: Models.Document,
|
||||
$doc: Models.Document
|
||||
) {
|
||||
if (!attribute) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const workAttribute = $work?.[attribute.key];
|
||||
const docAttribute = $doc?.[attribute.key];
|
||||
|
||||
if (attribute.array) {
|
||||
return !symmetricDifference(Array.from(workAttribute), Array.from(docAttribute)).length;
|
||||
}
|
||||
|
||||
if (isRelationship(attribute)) {
|
||||
if (isRelationshipToMany(attribute as Models.AttributeRelationship)) {
|
||||
const workIds = workAttribute.map((doc: string | Record<string, unknown>) =>
|
||||
typeof doc === 'string' ? doc : doc.$id
|
||||
);
|
||||
|
||||
const relatedIds = docAttribute.map((doc: string | Record<string, unknown>) =>
|
||||
typeof doc === 'string' ? doc : doc.$id
|
||||
);
|
||||
return !symmetricDifference(workIds, relatedIds).length;
|
||||
} else {
|
||||
const workId =
|
||||
typeof workAttribute === 'string' ? workAttribute : workAttribute?.$id;
|
||||
const relatedId =
|
||||
typeof docAttribute === 'string' ? docAttribute : docAttribute?.$id;
|
||||
|
||||
return workId === relatedId;
|
||||
}
|
||||
}
|
||||
return workAttribute === docAttribute;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Document - Appwrite</title>
|
||||
<title>Data - Appwrite</title>
|
||||
</svelte:head>
|
||||
|
||||
<Container>
|
||||
<CardGrid>
|
||||
<Heading tag="h2" size="7">Metadata</Heading>
|
||||
<svelte:fragment slot="aside">
|
||||
<div>
|
||||
<p>Created: {toLocaleDateTime($doc.$createdAt)}</p>
|
||||
<p>Last updated: {toLocaleDateTime($doc.$updatedAt)}</p>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
<CardGrid>
|
||||
<Heading tag="h6" size="7">Permissions</Heading>
|
||||
<p>
|
||||
A user requires appropriate permissions at either the <b>collection level</b> or
|
||||
<b>document level</b> to access a document. If no permissions are configured, no user
|
||||
can access the document
|
||||
<a
|
||||
href="https://appwrite.io/docs/products/databases/permissions"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="link">Learn more about database permissions</a
|
||||
>.
|
||||
</p>
|
||||
|
||||
<svelte:fragment slot="aside">
|
||||
{#if $collection.documentSecurity}
|
||||
{#if showPermissionAlert}
|
||||
<Alert type="info" dismissible on:dismiss={() => (showPermissionAlert = false)}>
|
||||
<svelte:fragment slot="title">Document security is enabled</svelte:fragment>
|
||||
<p class="text">
|
||||
Users will be able to access this document if they have been granted <b
|
||||
>either document or collection permissions.
|
||||
</b>
|
||||
</p>
|
||||
</Alert>
|
||||
{/if}
|
||||
{#if permissions}
|
||||
<Permissions bind:permissions />
|
||||
{/if}
|
||||
{:else}
|
||||
<Alert type="info">
|
||||
<svelte:fragment slot="title">Document security is disabled</svelte:fragment>
|
||||
<p class="text">
|
||||
If you want to assign document permissions. Go to Collection settings and
|
||||
enable document security. Otherwise, only collection permissions will be
|
||||
used.
|
||||
</p>
|
||||
</Alert>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button
|
||||
disabled={arePermsDisabled}
|
||||
on:click={() => {
|
||||
updatePermissions();
|
||||
}}>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
|
||||
<CardGrid danger>
|
||||
<Heading tag="h6" size="7">Delete document</Heading>
|
||||
<p>
|
||||
The document will be permanently deleted, including all the data within it. This action
|
||||
is irreversible.
|
||||
</p>
|
||||
<svelte:fragment slot="aside">
|
||||
<BoxAvatar>
|
||||
<svelte:fragment slot="title">
|
||||
<h6 class="u-bold u-trim-1">{$doc.$id}</h6>
|
||||
{#if $collection?.attributes?.length}
|
||||
{#each $collection.attributes as attribute}
|
||||
{@const label = attribute.key}
|
||||
<CardGrid>
|
||||
<Heading tag="h6" size="7">{label}</Heading>
|
||||
<svelte:fragment slot="aside">
|
||||
<AttributeItem {attribute} bind:formValues={$work} {label} {editing} />
|
||||
</svelte:fragment>
|
||||
<p>
|
||||
Last updated: {toLocaleDateTime($doc.$updatedAt)}
|
||||
</p>
|
||||
</BoxAvatar>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button secondary on:click={() => (showDelete = true)}>Delete</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
<svelte:fragment slot="actions">
|
||||
<Button
|
||||
disabled={compareAttributes(attribute, $work, $doc)}
|
||||
on:click={() => updateData()}>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
{/each}
|
||||
{/if}
|
||||
</Container>
|
||||
|
||||
<Delete bind:showDelete />
|
||||
|
||||
-137
@@ -1,137 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { page } from '$app/stores';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { writable } from 'svelte/store';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { doc } from '../store';
|
||||
import { collection, type Attributes } from '../../store';
|
||||
import { Container } from '$lib/layout';
|
||||
import AttributeItem from '../attributeItem.svelte';
|
||||
import { symmetricDifference } from '$lib/helpers/array';
|
||||
import { isRelationship, isRelationshipToMany } from '../attributes/store';
|
||||
import { deepClone } from '$lib/helpers/object';
|
||||
|
||||
const databaseId = $page.params.database;
|
||||
const collectionId = $page.params.collection;
|
||||
const documentId = $page.params.document;
|
||||
const editing = true;
|
||||
|
||||
function initWork() {
|
||||
const prohibitedKeys = [
|
||||
'$id',
|
||||
'$collection',
|
||||
'$collectionId',
|
||||
'$databaseId',
|
||||
'$createdAt',
|
||||
'$updatedAt'
|
||||
];
|
||||
|
||||
const filteredKeys = Object.keys($doc).filter((key) => {
|
||||
return !prohibitedKeys.includes(key);
|
||||
});
|
||||
|
||||
const result = filteredKeys.reduce((obj, key) => {
|
||||
obj[key] = $doc[key];
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
return writable(deepClone(result as Models.Document));
|
||||
}
|
||||
|
||||
const work = initWork();
|
||||
|
||||
async function updateData() {
|
||||
try {
|
||||
await sdk
|
||||
.forProject($page.params.region, $page.params.project)
|
||||
.databases.updateDocument(
|
||||
databaseId,
|
||||
collectionId,
|
||||
documentId,
|
||||
$work,
|
||||
$work.$permissions
|
||||
);
|
||||
|
||||
invalidate(Dependencies.DOCUMENT);
|
||||
trackEvent(Submit.DocumentUpdate);
|
||||
addNotification({
|
||||
message: 'Document has been updated',
|
||||
type: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
trackError(error, Submit.DocumentUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
function compareAttributes(
|
||||
attribute: Attributes,
|
||||
$work: Models.Document,
|
||||
$doc: Models.Document
|
||||
) {
|
||||
if (!attribute) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const workAttribute = $work?.[attribute.key];
|
||||
const docAttribute = $doc?.[attribute.key];
|
||||
|
||||
if (attribute.array) {
|
||||
return !symmetricDifference(Array.from(workAttribute), Array.from(docAttribute)).length;
|
||||
}
|
||||
|
||||
if (isRelationship(attribute)) {
|
||||
if (isRelationshipToMany(attribute as Models.AttributeRelationship)) {
|
||||
const workIds = workAttribute.map((doc: string | Record<string, unknown>) =>
|
||||
typeof doc === 'string' ? doc : doc.$id
|
||||
);
|
||||
|
||||
const relatedIds = docAttribute.map((doc: string | Record<string, unknown>) =>
|
||||
typeof doc === 'string' ? doc : doc.$id
|
||||
);
|
||||
return !symmetricDifference(workIds, relatedIds).length;
|
||||
} else {
|
||||
const workId =
|
||||
typeof workAttribute === 'string' ? workAttribute : workAttribute?.$id;
|
||||
const relatedId =
|
||||
typeof docAttribute === 'string' ? docAttribute : docAttribute?.$id;
|
||||
|
||||
return workId === relatedId;
|
||||
}
|
||||
}
|
||||
return workAttribute === docAttribute;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Data - Appwrite</title>
|
||||
</svelte:head>
|
||||
|
||||
<Container>
|
||||
{#if $collection?.attributes?.length}
|
||||
{#each $collection.attributes as attribute}
|
||||
{@const label = attribute.key}
|
||||
<CardGrid>
|
||||
<Heading tag="h6" size="7">{label}</Heading>
|
||||
<svelte:fragment slot="aside">
|
||||
<AttributeItem {attribute} bind:formValues={$work} {label} {editing} />
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button
|
||||
disabled={compareAttributes(attribute, $work, $doc)}
|
||||
on:click={() => updateData()}>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
{/each}
|
||||
{/if}
|
||||
</Container>
|
||||
+7
-7
@@ -14,20 +14,20 @@
|
||||
const tabs = [
|
||||
{
|
||||
href: path,
|
||||
title: 'Overview',
|
||||
event: 'overview'
|
||||
},
|
||||
{
|
||||
href: `${path}/data`,
|
||||
title: 'Data',
|
||||
event: 'data',
|
||||
hasChildren: true
|
||||
event: 'data'
|
||||
},
|
||||
{
|
||||
href: `${path}/activity`,
|
||||
title: 'Activity',
|
||||
event: 'activity',
|
||||
hasChildren: true
|
||||
},
|
||||
{
|
||||
href: `${path}/settings`,
|
||||
title: 'Settings',
|
||||
event: 'settings',
|
||||
hasChildren: true
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
<script lang="ts">
|
||||
import { CardGrid, BoxAvatar, Heading, Alert } from '$lib/components';
|
||||
import { Container } from '$lib/layout';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { doc } from '../store';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import Delete from '../delete.svelte';
|
||||
import { symmetricDifference } from '$lib/helpers/array';
|
||||
import { Permissions } from '$lib/components/permissions';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { collection } from '../../store';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
let showDelete = false;
|
||||
let permissions = $doc?.$permissions;
|
||||
let arePermsDisabled = true;
|
||||
let showPermissionAlert = true;
|
||||
|
||||
async function updatePermissions() {
|
||||
try {
|
||||
await sdk
|
||||
.forProject($page.params.region, $page.params.project)
|
||||
.databases.updateDocument(
|
||||
$doc.$databaseId,
|
||||
$doc.$collectionId,
|
||||
$doc.$id,
|
||||
$doc.data,
|
||||
permissions
|
||||
);
|
||||
await invalidate(Dependencies.DOCUMENT);
|
||||
arePermsDisabled = true;
|
||||
addNotification({
|
||||
message: 'Permissions have been updated',
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent(Submit.DocumentUpdatePermissions);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
trackError(error, Submit.DocumentUpdatePermissions);
|
||||
}
|
||||
}
|
||||
|
||||
$: if (permissions) {
|
||||
arePermsDisabled = !symmetricDifference(permissions, $doc.$permissions).length;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Document - Appwrite</title>
|
||||
</svelte:head>
|
||||
|
||||
<Container>
|
||||
<CardGrid>
|
||||
<Heading tag="h2" size="7">Metadata</Heading>
|
||||
<svelte:fragment slot="aside">
|
||||
<div>
|
||||
<p>Created: {toLocaleDateTime($doc.$createdAt)}</p>
|
||||
<p>Last updated: {toLocaleDateTime($doc.$updatedAt)}</p>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
<CardGrid>
|
||||
<Heading tag="h6" size="7">Permissions</Heading>
|
||||
<p>
|
||||
A user requires appropriate permissions at either the <b>collection level</b> or
|
||||
<b>document level</b> to access a document. If no permissions are configured, no user
|
||||
can access the document
|
||||
<a
|
||||
href="https://appwrite.io/docs/products/databases/permissions"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="link">Learn more about database permissions</a
|
||||
>.
|
||||
</p>
|
||||
|
||||
<svelte:fragment slot="aside">
|
||||
{#if $collection.documentSecurity}
|
||||
{#if showPermissionAlert}
|
||||
<Alert type="info" dismissible on:dismiss={() => (showPermissionAlert = false)}>
|
||||
<svelte:fragment slot="title">Document security is enabled</svelte:fragment>
|
||||
<p class="text">
|
||||
Users will be able to access this document if they have been granted <b
|
||||
>either document or collection permissions.
|
||||
</b>
|
||||
</p>
|
||||
</Alert>
|
||||
{/if}
|
||||
{#if permissions}
|
||||
<Permissions bind:permissions />
|
||||
{/if}
|
||||
{:else}
|
||||
<Alert type="info">
|
||||
<svelte:fragment slot="title">Document security is disabled</svelte:fragment>
|
||||
<p class="text">
|
||||
If you want to assign document permissions. Go to Collection settings and
|
||||
enable document security. Otherwise, only collection permissions will be
|
||||
used.
|
||||
</p>
|
||||
</Alert>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button
|
||||
disabled={arePermsDisabled}
|
||||
on:click={() => {
|
||||
updatePermissions();
|
||||
}}>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
|
||||
<CardGrid danger>
|
||||
<Heading tag="h6" size="7">Delete document</Heading>
|
||||
<p>
|
||||
The document will be permanently deleted, including all the data within it. This action
|
||||
is irreversible.
|
||||
</p>
|
||||
<svelte:fragment slot="aside">
|
||||
<BoxAvatar>
|
||||
<svelte:fragment slot="title">
|
||||
<h6 class="u-bold u-trim-1">{$doc.$id}</h6>
|
||||
</svelte:fragment>
|
||||
<p>
|
||||
Last updated: {toLocaleDateTime($doc.$updatedAt)}
|
||||
</p>
|
||||
</BoxAvatar>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button secondary on:click={() => (showDelete = true)}>Delete</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Container>
|
||||
|
||||
<Delete bind:showDelete />
|
||||
Reference in New Issue
Block a user