feat: permissions input

This commit is contained in:
Torsten Dittmann
2022-01-09 02:03:07 +01:00
parent d9ac864749
commit 513b631434
19 changed files with 364 additions and 145 deletions
+3 -1
View File
@@ -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";
+110
View File
@@ -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>
-53
View File
@@ -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>
+3
View File
@@ -0,0 +1,3 @@
<tbody>
<slot />
</tbody>
+9
View File
@@ -0,0 +1,9 @@
<td>
<slot />
</td>
<style>
td {
white-space: nowrap;
}
</style>
+5
View File
@@ -0,0 +1,5 @@
<tfoot>
<tr>
<slot />
</tr>
</tfoot>
+5
View File
@@ -0,0 +1,5 @@
<thead>
<tr>
<slot />
</tr>
</thead>
+6
View File
@@ -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";
+3
View File
@@ -0,0 +1,3 @@
<tr>
<slot />
</tr>
+15
View File
@@ -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>
+30 -12
View File
@@ -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}