mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
feat: permissions input
This commit is contained in:
@@ -4,6 +4,8 @@ export { default as InputPassword } from './inputPassword.svelte';
|
||||
export { default as InputText } from './inputText.svelte';
|
||||
export { default as InputNumber } from './inputNumber.svelte';
|
||||
export { default as InputBoolean } from './inputBoolean.svelte';
|
||||
export { default as InputTags } from './inputTags.svelte';
|
||||
export { default as Modal } from './modal.svelte';
|
||||
export { default as Table } from './table.svelte';
|
||||
export { default as Table } from './table/table.svelte';
|
||||
export { default as Pagination } from './pagination.svelte';
|
||||
export * from "./table";
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let label: string;
|
||||
export let tags: string[] = [];
|
||||
export let placeholder = '';
|
||||
export let autofocus = false;
|
||||
|
||||
let element: HTMLInputElement;
|
||||
let value: string;
|
||||
|
||||
onMount(() => {
|
||||
if (element && autofocus) {
|
||||
element.focus();
|
||||
}
|
||||
});
|
||||
|
||||
const handleInput = (e: KeyboardEvent) => {
|
||||
if (['Enter', 'Tab', ' '].includes(e.key)) {
|
||||
e.preventDefault();
|
||||
addValue();
|
||||
}
|
||||
if (['Backspace', 'Delete'].includes(e.key)) {
|
||||
if (value.length === 0) {
|
||||
removeValue(tags[tags.length - 1]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
addValue();
|
||||
};
|
||||
|
||||
const addValue = () => {
|
||||
let tag = value.trim();
|
||||
if (tag.length === 0 || tags.includes(tag)) return;
|
||||
|
||||
tags = [...tags, tag];
|
||||
value = '';
|
||||
};
|
||||
|
||||
const removeValue = (value: string) => {
|
||||
tags = tags.filter((tag) => tag !== value);
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
<label>
|
||||
<span>{label}</span>
|
||||
<div class="tags">
|
||||
<ul>
|
||||
{#each tags as tag}
|
||||
<li on:click={() => removeValue(tag)}>{tag}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<input
|
||||
type="text"
|
||||
on:keydown={handleInput}
|
||||
on:blur={handleBlur}
|
||||
{placeholder}
|
||||
bind:value
|
||||
bind:this={element}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<style lang="scss">
|
||||
.tags {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
box-sizing: content-box;
|
||||
color: #313131;
|
||||
height: 1.5rem;
|
||||
line-height: 1.5rem;
|
||||
border: solid 1px black;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
display: block;
|
||||
width: calc(100% - 2rem);
|
||||
margin-bottom: 2rem;
|
||||
background: white;
|
||||
height: auto;
|
||||
cursor: text;
|
||||
|
||||
ul {
|
||||
display: inline;
|
||||
white-space: pre-line;
|
||||
li {
|
||||
display: inline-block;
|
||||
margin-right: 0.5rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
display: inline-block;
|
||||
border: none;
|
||||
padding: 0;
|
||||
width: auto;
|
||||
margin: 0;
|
||||
max-width: 100%;
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,53 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let columns: {
|
||||
key: string;
|
||||
title: string;
|
||||
}[] = [];
|
||||
|
||||
export let data = [];
|
||||
export let anchor = '';
|
||||
export let anchorReplace: {
|
||||
[key: string]: string;
|
||||
} = null;
|
||||
|
||||
const createLink = (entry) => {
|
||||
let link = anchor;
|
||||
for (const key in anchorReplace) {
|
||||
link = link.replace(anchorReplace[key], entry[key]);
|
||||
}
|
||||
|
||||
return link;
|
||||
};
|
||||
</script>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
{#each columns as column}
|
||||
<td>{column.title}</td>
|
||||
{/each}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each data as entry}
|
||||
<tr>
|
||||
{#each columns as column}
|
||||
<td>
|
||||
{#if anchor}
|
||||
<a href={createLink(entry)}>
|
||||
{entry[column.key] ?? 'n/a'}
|
||||
</a>
|
||||
{:else}
|
||||
{entry[column.key] ?? 'n/a'}
|
||||
{/if}
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
{:else}
|
||||
nothing found
|
||||
{/each}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr />
|
||||
</tfoot>
|
||||
</table>
|
||||
@@ -0,0 +1,3 @@
|
||||
<tbody>
|
||||
<slot />
|
||||
</tbody>
|
||||
@@ -0,0 +1,9 @@
|
||||
<td>
|
||||
<slot />
|
||||
</td>
|
||||
|
||||
<style>
|
||||
td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,5 @@
|
||||
<tfoot>
|
||||
<tr>
|
||||
<slot />
|
||||
</tr>
|
||||
</tfoot>
|
||||
@@ -0,0 +1,5 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<slot />
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -0,0 +1,6 @@
|
||||
export { default as Table } from "./table.svelte";
|
||||
export { default as TableBody } from "./body.svelte";
|
||||
export { default as TableHeader } from "./header.svelte";
|
||||
export { default as TableFooter } from "./footer.svelte";
|
||||
export { default as TableCell } from "./cell.svelte";
|
||||
export { default as TableRow } from "./row.svelte";
|
||||
@@ -0,0 +1,3 @@
|
||||
<tr>
|
||||
<slot />
|
||||
</tr>
|
||||
@@ -0,0 +1,15 @@
|
||||
<script lang="ts">
|
||||
export let horizontalScroll = false;
|
||||
</script>
|
||||
|
||||
{#if horizontalScroll}
|
||||
<figure>
|
||||
<table>
|
||||
<slot />
|
||||
</table>
|
||||
</figure>
|
||||
{:else}
|
||||
<table>
|
||||
<slot />
|
||||
</table>
|
||||
{/if}
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Table, Button } from '$lib/components';
|
||||
import { Table, TableCell, TableHeader, TableBody, TableRow, Button } from '$lib/components';
|
||||
import Create from './attributes/_create.svelte';
|
||||
import { collection } from './store';
|
||||
|
||||
@@ -7,14 +7,22 @@
|
||||
</script>
|
||||
|
||||
<section>
|
||||
<Table
|
||||
columns={[
|
||||
{ key: 'status', title: 'Status' },
|
||||
{ key: 'key', title: 'Key' },
|
||||
{ key: 'type', title: 'Type' }
|
||||
]}
|
||||
data={$collection.attributes}
|
||||
/>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableCell>Status</TableCell>
|
||||
<TableCell>Key</TableCell>
|
||||
<TableCell>Type</TableCell>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each $collection.attributes as attribute}
|
||||
<TableRow>
|
||||
<TableCell>{attribute.status}</TableCell>
|
||||
<TableCell>{attribute.key}</TableCell>
|
||||
<TableCell>{attribute.type}</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</section>
|
||||
|
||||
<Button on:click={() => (showCreate = true)}>Create Attribute</Button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { Table, Pagination } from '$lib/components';
|
||||
import { Table, TableHeader, TableRow, TableBody, TableCell, Pagination } from '$lib/components';
|
||||
import { collection } from './store';
|
||||
|
||||
let offset = 0;
|
||||
@@ -11,10 +11,13 @@
|
||||
const getDocuments = () =>
|
||||
sdkForProject.database.listDocuments($collection.$id, [], limit, offset);
|
||||
|
||||
const columns = $collection.attributes.map((attribute) => ({
|
||||
key: attribute.key,
|
||||
title: attribute.key
|
||||
}));
|
||||
const columns = [
|
||||
{ key: '$id', title: '#' },
|
||||
...$collection.attributes.map((attribute) => ({
|
||||
key: attribute.key,
|
||||
title: attribute.key
|
||||
}))
|
||||
];
|
||||
|
||||
let request = getDocuments();
|
||||
</script>
|
||||
@@ -23,16 +26,27 @@
|
||||
{#await request}
|
||||
<div aria-busy="true" />
|
||||
{:then response}
|
||||
<figure>
|
||||
<Table
|
||||
columns={[{ key: '$id', title: '#' }, ...columns]}
|
||||
data={response.documents}
|
||||
anchor={`/console/${project}/database/${$collection.$id}/:$id`}
|
||||
anchorReplace={{
|
||||
$id: ':$id'
|
||||
}}
|
||||
/>
|
||||
</figure>
|
||||
<Table horizontalScroll={true}>
|
||||
<TableHeader>
|
||||
{#each columns as column}
|
||||
<TableCell>{column.title}</TableCell>
|
||||
{/each}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each response.documents as document}
|
||||
<TableRow>
|
||||
{#each columns as column}
|
||||
<TableCell>
|
||||
<a href={`/console/${project}/database/${$collection.$id}/${document.$id}`}>
|
||||
{document[column.key] ?? 'n/a'}
|
||||
</a>
|
||||
</TableCell>
|
||||
{/each}
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<Pagination
|
||||
{limit}
|
||||
bind:offset
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { Table, Button, Pagination } from '$lib/components';
|
||||
import {
|
||||
Table,
|
||||
Button,
|
||||
Pagination,
|
||||
TableRow,
|
||||
TableBody,
|
||||
TableHeader,
|
||||
TableCell
|
||||
} from '$lib/components';
|
||||
import Create from './_create.svelte';
|
||||
|
||||
let search = '';
|
||||
@@ -26,17 +34,22 @@
|
||||
{#await request}
|
||||
<div aria-busy="true" />
|
||||
{:then response}
|
||||
<Table
|
||||
columns={[
|
||||
{ key: '$id', title: 'ID' },
|
||||
{ key: 'name', title: 'Name' }
|
||||
]}
|
||||
data={response.collections}
|
||||
anchor={`/console/${project}/database/:$id`}
|
||||
anchorReplace={{
|
||||
$id: ':$id'
|
||||
}}
|
||||
/>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableCell>#</TableCell>
|
||||
<TableCell>Name</TableCell>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each response.collections as collection}
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<a href={`/console/${project}/database/${collection.$id}`}>{collection.$id}</a>
|
||||
</TableCell>
|
||||
<TableCell>{collection.name}</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<Pagination
|
||||
{limit}
|
||||
bind:offset
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import Button from '$lib/components/button.svelte';
|
||||
import InputFile from '$lib/components/inputFile.svelte';
|
||||
import InputTags from '$lib/components/inputTags.svelte';
|
||||
import Modal from '$lib/components/modal.svelte';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
@@ -10,10 +11,12 @@
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let files: FileList;
|
||||
let read: string[] = [];
|
||||
let write: string[] = [];
|
||||
|
||||
const create = async () => {
|
||||
try {
|
||||
await sdkForProject.storage.createFile('unique()', files[0]);
|
||||
await sdkForProject.storage.createFile('unique()', files[0], read, write);
|
||||
files = null;
|
||||
showCreate = false;
|
||||
dispatch('created');
|
||||
@@ -27,6 +30,8 @@
|
||||
<Modal bind:show={showCreate}>
|
||||
<span slot="header">Upload File</span>
|
||||
<InputFile label="Name" bind:files required />
|
||||
<InputTags label="Read" bind:tags={read} />
|
||||
<InputTags label="Write" bind:tags={write} />
|
||||
<footer>
|
||||
<Button secondary on:click={() => (showCreate = false)}>Cancel</Button>
|
||||
<Button submit>Upload</Button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { Table, Button, Pagination } from '$lib/components';
|
||||
import { Table, Button, Pagination, TableHeader, TableCell, TableBody, TableRow } from '$lib/components';
|
||||
import Create from './_create.svelte';
|
||||
|
||||
let search = '';
|
||||
@@ -26,19 +26,28 @@
|
||||
{#await request}
|
||||
<div aria-busy="true" />
|
||||
{:then response}
|
||||
<Table
|
||||
columns={[
|
||||
{ key: 'name', title: 'Name' },
|
||||
{ key: 'mimeType', title: 'Type' },
|
||||
{ key: 'sizeOriginal', title: 'Size' },
|
||||
{ key: 'dateCreated', title: 'Created' }
|
||||
]}
|
||||
data={response.files}
|
||||
anchor={`/console/${project}/storage/:$id`}
|
||||
anchorReplace={{
|
||||
$id: ':$id'
|
||||
}}
|
||||
/>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableCell>#</TableCell>
|
||||
<TableCell>Name</TableCell>
|
||||
<TableCell>E-Mail</TableCell>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each response.files as file}
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<a href={`/console/${project}/storage/${file.$id}`}>
|
||||
{file.$id}
|
||||
</a>
|
||||
</TableCell>
|
||||
<TableCell>{file.mimeType}</TableCell>
|
||||
<TableCell>{file.sizeOriginal}</TableCell>
|
||||
<TableCell>{file.dateCreated}</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<Pagination {limit} bind:offset sum={response.sum} on:change={() => (request = getUsers())} />
|
||||
{/await}
|
||||
</section>
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { Table, Button, Pagination } from '$lib/components';
|
||||
import {
|
||||
Table,
|
||||
Button,
|
||||
Pagination,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableRow,
|
||||
TableCell
|
||||
} from '$lib/components';
|
||||
import Create from './_create.svelte';
|
||||
import { user } from '$lib/stores/user';
|
||||
|
||||
let search = '';
|
||||
let showCreate = false;
|
||||
@@ -25,17 +34,26 @@
|
||||
{#await request}
|
||||
<div aria-busy="true" />
|
||||
{:then response}
|
||||
<Table
|
||||
columns={[
|
||||
{ key: 'name', title: 'Name' },
|
||||
{ key: 'email', title: 'E-Mail' }
|
||||
]}
|
||||
data={response.users}
|
||||
anchor={`/console/${project}/users/user/:$id`}
|
||||
anchorReplace={{
|
||||
$id: ':$id'
|
||||
}}
|
||||
/>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableCell>#</TableCell>
|
||||
<TableCell>Name</TableCell>
|
||||
<TableCell>E-Mail</TableCell>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each response.users as user}
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<a href={`/console/${project}/users/user/${user.$id}`}>
|
||||
{user.$id}
|
||||
</a>
|
||||
</TableCell>
|
||||
<TableCell>{user.name}</TableCell>
|
||||
<TableCell>{user.email}</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<Pagination {limit} bind:offset sum={response.sum} on:change={() => (request = getUsers())} />
|
||||
{/await}
|
||||
</section>
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
import { Table, Button, Pagination } from '$lib/components';
|
||||
import {
|
||||
Table,
|
||||
Button,
|
||||
Pagination,
|
||||
TableHeader,
|
||||
TableCell,
|
||||
TableBody,
|
||||
TableRow
|
||||
} from '$lib/components';
|
||||
import Create from './_create.svelte';
|
||||
|
||||
let search = '';
|
||||
@@ -25,17 +33,27 @@
|
||||
{#await request}
|
||||
<div aria-busy="true" />
|
||||
{:then response}
|
||||
<Table
|
||||
columns={[
|
||||
{ key: 'name', title: 'Name' },
|
||||
{ key: 'sum', title: 'Members' }
|
||||
]}
|
||||
data={response.teams}
|
||||
anchor={`/console/${project}/users/:$id`}
|
||||
anchorReplace={{
|
||||
$id: ':$id'
|
||||
}}
|
||||
/>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableCell>#</TableCell>
|
||||
<TableCell>Name</TableCell>
|
||||
<TableCell>Members</TableCell>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each response.teams as team}
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<a href={`/console/${project}/users/team/${team.$id}`}>
|
||||
{team.$id}
|
||||
</a>
|
||||
</TableCell>
|
||||
<TableCell>{team.name}</TableCell>
|
||||
<TableCell>{team.sum}</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<Pagination {limit} bind:offset sum={response.sum} on:change={() => (request = getTeams())} />
|
||||
{/await}
|
||||
</section>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Pagination, Table } from '$lib/components';
|
||||
import { Pagination, Table, TableBody, TableHeader, TableRow, TableCell } from '$lib/components';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
|
||||
const getLogs = () => sdkForProject.users.getLogs($page.params.user);
|
||||
@@ -8,19 +8,28 @@
|
||||
let offset = 0;
|
||||
const limit = 25;
|
||||
|
||||
let request = getLogs();
|
||||
let request = getLogs();
|
||||
</script>
|
||||
|
||||
{#await request}
|
||||
<div aria-busy="true" />
|
||||
{:then response}
|
||||
<Table
|
||||
columns={[
|
||||
{ key: 'event', title: 'Event' },
|
||||
{ key: 'time', title: 'Time' },
|
||||
{ key: 'ip', title: 'IP' }
|
||||
]}
|
||||
data={response.logs}
|
||||
/>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableCell>Event</TableCell>
|
||||
<TableCell>Time</TableCell>
|
||||
<TableCell>IP</TableCell>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each response.logs as log}
|
||||
<TableRow>
|
||||
<TableCell>{log.event}</TableCell>
|
||||
<TableCell>{log.time}</TableCell>
|
||||
<TableCell>{log.ip}</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<Pagination {limit} bind:offset sum={response.sum} on:change={() => (request = getLogs())} />
|
||||
{/await}
|
||||
|
||||
@@ -1,15 +1,35 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Pagination, Table, TableCell, TableHeader, TableBody, TableRow } from '$lib/components';
|
||||
import { sdkForProject } from '$lib/stores/sdk';
|
||||
|
||||
const request = sdkForProject.users.getSessions($page.params.user);
|
||||
const getSessions = () => sdkForProject.users.getSessions($page.params.user);
|
||||
|
||||
let offset = 0;
|
||||
const limit = 25;
|
||||
|
||||
let request = getSessions();
|
||||
</script>
|
||||
|
||||
{#await request}
|
||||
<div aria-busy="true" />
|
||||
{:then user}
|
||||
<div>
|
||||
<p>Name: {user.name}</p>
|
||||
<p>Mail: {user.email}</p>
|
||||
</div>
|
||||
{:then response}
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableCell>Country</TableCell>
|
||||
<TableCell>OS</TableCell>
|
||||
<TableCell>IP</TableCell>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each response.sessions as session}
|
||||
<TableRow>
|
||||
<TableCell>{session.countryName}</TableCell>
|
||||
<TableCell>{session.osName}</TableCell>
|
||||
<TableCell>{session.ip}</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<Pagination {limit} bind:offset sum={response.sum} on:change={() => (request = getSessions())} />
|
||||
{/await}
|
||||
|
||||
Reference in New Issue
Block a user