fix: various issues

This commit is contained in:
Torsten Dittmann
2025-01-30 21:13:17 +01:00
parent ee807c4a67
commit dd48b12bb5
12 changed files with 331 additions and 279 deletions
+2 -3
View File
@@ -21,9 +21,9 @@
"dependencies": {
"@appwrite.io/console": "https://pkg.pr.new/appwrite/appwrite/@appwrite.io/console@9193",
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@5312d5d",
"@appwrite.io/pink-icons-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@cc346f7d",
"@appwrite.io/pink-legacy": "^1.0.0",
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@5312d5d",
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@cc346f7d",
"@popperjs/core": "^2.11.8",
"@sentry/sveltekit": "^8.38.0",
"@stripe/stripe-js": "^3.5.0",
@@ -37,7 +37,6 @@
"ignore": "^6.0.2",
"nanoid": "^5.0.8",
"nanotar": "^0.1.1",
"pink-icons-svelte@3db2a4f": "link:ttps:/pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@3db2a4f",
"plausible-tracker": "^0.3.9",
"pretty-bytes": "^6.1.1",
"prismjs": "^1.29.0",
+14 -17
View File
@@ -15,14 +15,14 @@ importers:
specifier: 0.25.0
version: 0.25.0
'@appwrite.io/pink-icons-svelte':
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@5312d5d
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@5312d5d(svelte@4.2.19)
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@cc346f7d
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@cc346f7d(svelte@4.2.19)
'@appwrite.io/pink-legacy':
specifier: ^1.0.0
version: 1.0.0
'@appwrite.io/pink-svelte':
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@5312d5d
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@5312d5d(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@cc346f7d
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@cc346f7d(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)
'@popperjs/core':
specifier: ^2.11.8
version: 2.11.8
@@ -62,9 +62,6 @@ importers:
nanotar:
specifier: ^0.1.1
version: 0.1.1
pink-icons-svelte@3db2a4f:
specifier: link:ttps:/pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@3db2a4f
version: link:ttps:/pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@3db2a4f
plausible-tracker:
specifier: ^0.3.9
version: 0.3.9
@@ -221,14 +218,14 @@ packages:
resolution: {tarball: https://pkg.pr.new/appwrite/appwrite/@appwrite.io/console@9193}
version: 1.2.1
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@5312d5d':
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@5312d5d}
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@cc346f7d':
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@cc346f7d}
version: 1.0.0-next.7
peerDependencies:
svelte: ^4.0.0
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@5312d5d32ffe17459f863a0e37d55e097ccf1cfb':
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@5312d5d32ffe17459f863a0e37d55e097ccf1cfb}
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@cc346f7db23371e0bda04d58f48a2f8e9d6db1e8':
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@cc346f7db23371e0bda04d58f48a2f8e9d6db1e8}
version: 1.0.0-next.7
peerDependencies:
svelte: ^4.0.0
@@ -242,8 +239,8 @@ packages:
'@appwrite.io/pink-legacy@1.0.0':
resolution: {integrity: sha512-EZk/wX8oDhIsCXf2A0oQjdNOaHvh5MnWbLQI4pAiGrdy5qQGPoU19AK6iVX6z3QoOq7geoEu0zVwAowBy5hZbQ==}
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@5312d5d':
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@5312d5d}
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@cc346f7d':
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@cc346f7d}
version: 1.0.0-next.85
peerDependencies:
react-dom: ^18.0.0
@@ -4092,11 +4089,11 @@ snapshots:
'@appwrite.io/console@https://pkg.pr.new/appwrite/appwrite/@appwrite.io/console@9193': {}
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@5312d5d(svelte@4.2.19)':
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@cc346f7d(svelte@4.2.19)':
dependencies:
svelte: 4.2.19
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@5312d5d32ffe17459f863a0e37d55e097ccf1cfb(svelte@4.2.19)':
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@cc346f7db23371e0bda04d58f48a2f8e9d6db1e8(svelte@4.2.19)':
dependencies:
svelte: 4.2.19
@@ -4109,9 +4106,9 @@ snapshots:
'@appwrite.io/pink-icons': 1.0.0
the-new-css-reset: 1.11.3
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@5312d5d(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)':
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@cc346f7d(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)':
dependencies:
'@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@5312d5d32ffe17459f863a0e37d55e097ccf1cfb(svelte@4.2.19)
'@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@cc346f7db23371e0bda04d58f48a2f8e9d6db1e8(svelte@4.2.19)
'@floating-ui/dom': 1.6.13
'@melt-ui/pp': 0.3.2(@melt-ui/svelte@0.86.2(svelte@4.2.19))(svelte@4.2.19)
'@melt-ui/svelte': 0.86.2(svelte@4.2.19)
+1 -1
View File
@@ -20,7 +20,7 @@
</script>
<ul
class="grid-box common-section u-margin-block-start-32"
class="grid-box"
style={`--grid-gap:1.5rem; --grid-item-size-small-screens: 18rem; --grid-item-size:${total > 3 ? '22rem' : '25rem'};`}
data-private>
<slot />
+7 -1
View File
@@ -59,7 +59,13 @@
</script>
<div style:max-width="360px" style:width="100%">
<Input.Text {placeholder} {disabled} {required} type="search" bind:value={search}>
<Input.Text
{placeholder}
{disabled}
{required}
type="search"
bind:value={search}
--color-bgcolor-neutral-default="var(--color-bgcolor-neutral-primary)">
<svelte:fragment slot="start">
<Icon icon={IconSearch} />
</svelte:fragment>
+27 -38
View File
@@ -1,12 +1,14 @@
<script lang="ts">
import { Button, InputCheckbox, InputChoice } from '$lib/elements/forms';
import { Button, InputCheckbox } from '$lib/elements/forms';
import { page } from '$app/stores';
import type { Writable } from 'svelte/store';
import { preferences } from '$lib/stores/preferences';
import { onMount } from 'svelte';
import { View } from '$lib/helpers/load';
import type { Column } from '$lib/helpers/types';
import { ActionMenu, Layout, Popover, Tooltip } from '@appwrite.io/pink-svelte';
import { ActionMenu, Layout, Popover, ToggleButton } from '@appwrite.io/pink-svelte';
import { IconViewGrid, IconViewList } from '@appwrite.io/pink-icons-svelte';
import { goto } from '$app/navigation';
export let columns: Writable<Column[]>;
export let view: View;
@@ -18,8 +20,6 @@
export let fullWidthMobile = false;
export let hideText = false;
let showSelectColumns = false;
onMount(async () => {
if (isCustomCollection) {
const prefs = preferences.getCustomCollectionColumns($page.params.collection);
@@ -64,6 +64,13 @@
preferences.setView(view);
}
function onViewChange(event: CustomEvent<View>) {
updateViewPreferences(event.detail);
goto(getViewLink(event.detail), {
replaceState: true
});
}
$: selectedColumnsNumber = $columns.reduce((acc, column) => {
if (column.show) {
acc++;
@@ -105,38 +112,20 @@
{/if}
{#if !hideView}
<div class="grid-header-col-3 toggle-button">
<ul class="toggle-button-list">
{#key $page.url}
<li class="toggle-button-item">
<Tooltip>
<a
href={getViewLink(View.Table)}
on:click={() => updateViewPreferences(View.Table)}
class="toggle-button-element"
aria-label="List View"
type="button"
class:is-selected={view === View.Table}>
<span class="icon-view-list" aria-hidden="true" />
</a>
<span slot="tooltip">List View</span>
</Tooltip>
</li>
<li class="toggle-button-item">
<Tooltip>
<a
href={getViewLink(View.Grid)}
on:click={() => updateViewPreferences(View.Grid)}
class="toggle-button-element"
aria-label="Grid View"
type="button"
class:is-selected={view === View.Grid}>
<span class="icon-view-grid" aria-hidden="true" />
</a>
<span slot="tooltip">Grid View</span>
</Tooltip>
</li>
{/key}
</ul>
</div>
<ToggleButton
--color-bgcolor-neutral-default="var(--color-bgcolor-neutral-primary)"
on:change={onViewChange}
active={view}
buttons={[
{
id: 'table',
label: 'table view',
icon: IconViewList
},
{
id: 'grid',
label: 'grid view',
icon: IconViewGrid
}
]} />
{/if}
@@ -15,7 +15,7 @@
import type { PageData } from './$types';
import { columns } from './store';
import Cell from '$lib/elements/table/cell.svelte';
import { Tooltip } from '@appwrite.io/pink-svelte';
import { Tooltip, Table } from '@appwrite.io/pink-svelte';
export let data: PageData;
const projectId = $page.params.project;
@@ -30,61 +30,59 @@
}
</script>
<TableScroll>
<TableHeader>
<Table.Root>
<svelte:fragment slot="header">
{#each $columns as column}
{#if column.show}
<TableCellHead width={column.width}>{column.title}</TableCellHead>
<Table.Header.Cell width={column.width}>{column.title}</Table.Header.Cell>
{/if}
{/each}
</TableHeader>
<TableBody>
{#each data.databases.databases as database (database.$id)}
<TableRowLink href={`${base}/project-${projectId}/databases/database-${database.$id}`}>
{#each $columns as column}
{#if column.show}
{#if column.id === '$id'}
{#key $columns}
<TableCell width={column.width} title={column.title}>
<Id value={database.$id}>
{database.$id}
</Id>
</TableCell>
{/key}
{:else if column.id === 'name'}
<TableCellText width={column.width} title={column.title}>
{database.name}
</TableCellText>
{:else if column.id === 'backup'}
{@const policies = data.policies?.[database.$id] ?? null}
{@const lastBackup = data.lastBackups?.[database.$id] ?? null}
{@const description = policies
?.map((policy) => getPolicyDescription(policy.schedule))
.join(', ')}
</svelte:fragment>
{#each data.databases.databases as database (database.$id)}
<Table.Link href={`${base}/project-${projectId}/databases/database-${database.$id}`}>
{#each $columns as column}
{#if column.show}
{#if column.id === '$id'}
{#key $columns}
<Table.Cell width={column.width + 'px'}>
<Id value={database.$id}>
{database.$id}
</Id>
</Table.Cell>
{/key}
{:else if column.id === 'name'}
<Table.Cell width={column.width + 'px'}>
{database.name}
</Table.Cell>
{:else if column.id === 'backup'}
{@const policies = data.policies?.[database.$id] ?? null}
{@const lastBackup = data.lastBackups?.[database.$id] ?? null}
{@const description = policies
?.map((policy) => getPolicyDescription(policy.schedule))
.join(', ')}
<Cell title={column.title} width={column.width}>
<Tooltip placement="bottom" disabled={!policies || !lastBackup}>
<span class="u-trim">
{#if !policies}
<span class="icon-exclamation" /> No backup policies
{:else}
{description}
{/if}
</span>
<span slot="tooltip">{`Last backup: ${lastBackup}`}</span>
</Tooltip>
</Cell>
{:else}
<TableCellText width={column.width} title={column.title}>
{toLocaleDateTime(database[column.id])}
</TableCellText>
{/if}
<Table.Cell width={column.width + 'px'}>
<Tooltip placement="bottom" disabled={!policies || !lastBackup}>
<span class="u-trim">
{#if !policies}
<span class="icon-exclamation" /> No backup policies
{:else}
{description}
{/if}
</span>
<span slot="tooltip">{`Last backup: ${lastBackup}`}</span>
</Tooltip>
</Table.Cell>
{:else}
<Table.Cell width={column.width + 'px'}>
{toLocaleDateTime(database[column.id])}
</Table.Cell>
{/if}
{/each}
</TableRowLink>
{/each}
</TableBody>
</TableScroll>
{/if}
{/each}
</Table.Link>
{/each}
</Table.Root>
<style lang="scss">
.icon-exclamation {
@@ -4,32 +4,28 @@
<script lang="ts">
import { goto } from '$app/navigation';
import {
Empty,
GridItem1,
CardContainer,
PaginationWithLimit,
Id,
SearchQuery
} from '$lib/components';
import { Pill } from '$lib/elements';
import { Empty, PaginationWithLimit, SearchQuery } from '$lib/components';
import Create from './create.svelte';
import { Container, ContainerHeader } from '$lib/layout';
import { Container } from '$lib/layout';
import { base } from '$app/paths';
import { page } from '$app/stores';
import type { Models } from '@appwrite.io/console';
import { writable } from 'svelte/store';
import type { PageData } from './$types';
import { canWriteBuckets } from '$lib/stores/roles';
import { Layout, Tooltip } from '@appwrite.io/pink-svelte';
import { Layout } from '@appwrite.io/pink-svelte';
import { Button } from '$lib/elements/forms';
import { columns } from './store';
import Grid from './grid.svelte';
import Table from './table.svelte';
import ViewSelector from '$lib/components/viewSelector.svelte';
export let data: PageData;
const project = $page.params.project;
async function bucketCreated(event: CustomEvent<Models.Bucket>) {
$showCreateBucket = false;
showCreateBucket.set(false);
await goto(`${base}/project-${project}/storage/bucket-${event.detail.$id}`);
}
</script>
@@ -40,6 +36,11 @@
<SearchQuery search={data.search} placeholder="Search buckets" />
</Layout.Stack>
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
<ViewSelector
{columns}
view={data.view}
hideColumns={!data.buckets.total}
hideView={!data.buckets.total} />
{#if $canWriteBuckets}
<Button
on:mousedown={() => ($showCreateBucket = true)}
@@ -51,61 +52,15 @@
{/if}
</Layout.Stack>
</Layout.Stack>
{#if data.buckets.total}
<CardContainer
showEmpty={$canWriteBuckets}
total={data.buckets.total}
offset={data.offset}
event="bucket"
service="buckets"
on:click={() => ($showCreateBucket = true)}>
{#each data.buckets.buckets as bucket}
<GridItem1 href={`${base}/project-${project}/storage/bucket-${bucket.$id}`}>
<svelte:fragment slot="title">{bucket.name}</svelte:fragment>
<svelte:fragment slot="status">
{#if !bucket.enabled}
<Pill>Disabled</Pill>
{/if}
</svelte:fragment>
<Id value={bucket.$id}>{bucket.$id}</Id>
<svelte:fragment slot="icons">
<li>
<Tooltip>
<span
class:u-opacity-20={!bucket.encryption}
class="icon-lock-closed"
aria-hidden="true" />
<span slot="tooltip"
>{bucket.encryption
? 'Encryption enabled'
: 'Encryption disabled'}</span>
</Tooltip>
</li>
<li>
<Tooltip>
<span
class:u-opacity-20={!bucket.antivirus}
class="icon-shield-check"
aria-hidden="true" />
<span slot="tooltip"
>{bucket.antivirus
? 'Antivirus enabled'
: 'Antivirus disabled'}</span>
</Tooltip>
</li>
</svelte:fragment>
</GridItem1>
{/each}
<svelte:fragment slot="empty">
<p>Create a bucket</p>
</svelte:fragment>
</CardContainer>
{#if data.view === 'grid'}
<Grid {data} bind:showCreate={$showCreateBucket} />
{:else}
<Table {data} />
{/if}
<PaginationWithLimit
name="Buckets"
name="Databases"
limit={data.limit}
offset={data.offset}
total={data.buckets.total} />
@@ -114,7 +69,7 @@
single
href="https://appwrite.io/docs/products/storage"
target="bucket"
on:click={() => ($showCreateBucket = true)} />
on:click={() => (showCreateBucket = true)} />
{/if}
</Container>
@@ -1,18 +1,20 @@
import { Query } from '@appwrite.io/console';
import { sdk } from '$lib/stores/sdk';
import { getLimit, getPage, getSearch, pageToOffset } from '$lib/helpers/load';
import { getLimit, getPage, getSearch, getView, pageToOffset, View } from '$lib/helpers/load';
import { CARD_LIMIT } from '$lib/constants';
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ url, route }) => {
const page = getPage(url);
const search = getSearch(url);
const view = getView(url, route, View.Grid);
const limit = getLimit(url, route, CARD_LIMIT);
const offset = pageToOffset(page, limit);
return {
offset,
limit,
view,
search,
buckets: await sdk.forProject.storage.listBuckets(
[Query.limit(limit), Query.offset(offset), Query.orderDesc('')],
@@ -22,16 +22,6 @@
import { BillingPlan, Dependencies } from '$lib/constants';
import { Pill } from '$lib/elements';
import { Button } from '$lib/elements/forms';
import {
Table,
TableBody,
TableCell,
TableCellHead,
TableCellText,
TableHeader,
TableRow,
TableRowLink
} from '$lib/elements/table';
import { toLocaleDate } from '$lib/helpers/date';
import {
bytesToSize,
@@ -49,7 +39,7 @@
import Create from './create-file/create.svelte';
import DeleteFile from './deleteFile.svelte';
import { isCloud } from '$lib/system';
import { Layout, Tooltip } from '@appwrite.io/pink-svelte';
import { Layout, Tooltip, Table } from '@appwrite.io/pink-svelte';
import { onMount } from 'svelte';
export let data;
@@ -130,99 +120,96 @@
</Layout.Stack>
{#if data.files.total}
<Table>
<TableHeader>
<TableCellHead>Filename</TableCellHead>
<TableCellHead onlyDesktop width={140}>Type</TableCellHead>
<TableCellHead onlyDesktop width={100}>Size</TableCellHead>
<TableCellHead onlyDesktop width={120}>Created</TableCellHead>
<TableCellHead width={40} />
</TableHeader>
<TableBody>
{#each data.files.files as file, index}
{#if file.chunksTotal / file.chunksUploaded !== 1}
<TableRow>
<TableCell title="Name">
<div class="u-flex u-gap-12 u-cross-center">
<span class="avatar is-size-small is-color-empty" />
<span class="text u-trim">{file.name}</span>
<div>
<Pill warning>Pending</Pill>
</div>
<Table.Root>
<svelte:fragment slot="header">
<Table.Header.Cell>Filename</Table.Header.Cell>
<Table.Header.Cell width="140px">Type</Table.Header.Cell>
<Table.Header.Cell width="100px">Size</Table.Header.Cell>
<Table.Header.Cell width="120px">Created</Table.Header.Cell>
<Table.Header.Cell width="40px" />
</svelte:fragment>
{#each data.files.files as file, index}
{#if file.chunksTotal / file.chunksUploaded !== 1}
<Table.Row>
<Table.Cell>
<Layout.Stack direction="row" alignItems="center">
<span class="avatar is-size-small is-color-empty" />
<span class="text u-trim">{file.name}</span>
<div>
<Pill warning>Pending</Pill>
</div>
</TableCell>
<TableCellText onlyDesktop title="Type">{file.mimeType}</TableCellText>
<TableCellText onlyDesktop title="Size">
{calculateSize(file.sizeOriginal)}
</TableCellText>
<TableCellText onlyDesktop title="Date Created">
{toLocaleDate(file.$createdAt)}
</TableCellText>
<TableCell>
<div class="u-flex u-main-center">
<button
class="button is-only-icon is-text"
aria-label="Delete item"
on:click|preventDefault={() => {
deleteFile(file);
}}>
<span class="icon-trash" aria-hidden="true" />
</button>
</div>
</TableCell>
</TableRow>
{:else}
<TableRowLink
href={`${base}/project-${projectId}/storage/bucket-${bucketId}/file-${file.$id}`}>
<TableCell title="Name">
<div class="u-flex u-gap-12 u-cross-center">
<Avatar size={32} src={getPreview(file.$id)} name={file.name} />
<span class="text u-trim">{file.name}</span>
</div>
</TableCell>
<TableCellText onlyDesktop title="Type">{file.mimeType}</TableCellText>
<TableCellText onlyDesktop title="Size">
{calculateSize(file.sizeOriginal)}
</TableCellText>
<TableCellText onlyDesktop title="Date Created">
{toLocaleDate(file.$createdAt)}
</TableCellText>
<TableCell showOverflow>
<DropList
bind:show={showDropdown[index]}
placement="bottom-start"
noArrow>
<button
class="button is-only-icon is-text"
aria-label="More options"
on:click|preventDefault={() => {
showDropdown[index] = !showDropdown[index];
}}>
<span class="icon-dots-horizontal" aria-hidden="true" />
</button>
<svelte:fragment slot="list">
<DropListLink
on:click={() => {
showDropdown[index] = false;
}}
href={`${base}/project-${projectId}/storage/bucket-${bucketId}/file-${file.$id}`}
icon="pencil">Update</DropListLink>
<DropListItem
icon="trash"
on:click={() => {
showDropdown[index] = false;
selectedFile = file;
showDelete = true;
}}>Delete</DropListItem>
</svelte:fragment>
</DropList>
</TableCell>
</TableRowLink>
{/if}
{/each}
</TableBody>
</Table>
</Layout.Stack>
</Table.Cell>
<Table.Cell>{file.mimeType}</Table.Cell>
<Table.Cell>
{calculateSize(file.sizeOriginal)}
</Table.Cell>
<Table.Cell>
{toLocaleDate(file.$createdAt)}
</Table.Cell>
<Table.Cell>
<div class="u-flex u-main-center">
<button
class="button is-only-icon is-text"
aria-label="Delete item"
on:click|preventDefault={() => {
deleteFile(file);
}}>
<span class="icon-trash" aria-hidden="true" />
</button>
</div>
</Table.Cell>
</Table.Row>
{:else}
<Table.Link
href={`${base}/project-${projectId}/storage/bucket-${bucketId}/file-${file.$id}`}>
<Table.Cell>
<div class="u-flex u-gap-12 u-cross-center">
<Avatar size={32} src={getPreview(file.$id)} name={file.name} />
<span class="text u-trim">{file.name}</span>
</div>
</Table.Cell>
<Table.Cell>{file.mimeType}</Table.Cell>
<Table.Cell>
{calculateSize(file.sizeOriginal)}
</Table.Cell>
<Table.Cell>
{toLocaleDate(file.$createdAt)}
</Table.Cell>
<Table.Cell>
<DropList
bind:show={showDropdown[index]}
placement="bottom-start"
noArrow>
<button
class="button is-only-icon is-text"
aria-label="More options"
on:click|preventDefault={() => {
showDropdown[index] = !showDropdown[index];
}}>
<span class="icon-dots-horizontal" aria-hidden="true" />
</button>
<svelte:fragment slot="list">
<DropListLink
on:click={() => {
showDropdown[index] = false;
}}
href={`${base}/project-${projectId}/storage/bucket-${bucketId}/file-${file.$id}`}
icon="pencil">Update</DropListLink>
<DropListItem
icon="trash"
on:click={() => {
showDropdown[index] = false;
selectedFile = file;
showDelete = true;
}}>Delete</DropListItem>
</svelte:fragment>
</DropList>
</Table.Cell>
</Table.Link>
{/if}
{/each}
</Table.Root>
<PaginationWithLimit
name="Files"
@@ -0,0 +1,63 @@
<script lang="ts">
import { base } from '$app/paths';
import { page } from '$app/stores';
import { CardContainer, GridItem1, Id } from '$lib/components';
import Pill from '$lib/elements/pill.svelte';
import { canWriteBuckets } from '$lib/stores/roles';
import { Tooltip } from '@appwrite.io/pink-svelte';
import type { PageData } from './$types';
export let data: PageData;
export let showCreate = false;
const project = $page.params.project;
</script>
<CardContainer
showEmpty={$canWriteBuckets}
total={data.buckets.total}
offset={data.offset}
event="bucket"
service="buckets"
on:click={() => (showCreate = true)}>
{#each data.buckets.buckets as bucket}
<GridItem1 href={`${base}/project-${project}/storage/bucket-${bucket.$id}`}>
<svelte:fragment slot="title">{bucket.name}</svelte:fragment>
<svelte:fragment slot="status">
{#if !bucket.enabled}
<Pill>Disabled</Pill>
{/if}
</svelte:fragment>
<Id value={bucket.$id}>{bucket.$id}</Id>
<svelte:fragment slot="icons">
<li>
<Tooltip>
<span
class:u-opacity-20={!bucket.encryption}
class="icon-lock-closed"
aria-hidden="true" />
<span slot="tooltip"
>{bucket.encryption
? 'Encryption enabled'
: 'Encryption disabled'}</span>
</Tooltip>
</li>
<li>
<Tooltip>
<span
class:u-opacity-20={!bucket.antivirus}
class="icon-shield-check"
aria-hidden="true" />
<span slot="tooltip"
>{bucket.antivirus ? 'Antivirus enabled' : 'Antivirus disabled'}</span>
</Tooltip>
</li>
</svelte:fragment>
</GridItem1>
{/each}
<svelte:fragment slot="empty">
<p>Create a bucket</p>
</svelte:fragment>
</CardContainer>
@@ -0,0 +1,9 @@
import type { Column } from '$lib/helpers/types';
import { writable } from 'svelte/store';
export const columns = writable<Column[]>([
{ id: '$id', title: 'Database ID', type: 'string', show: true, width: 150 },
{ id: 'name', title: 'Name', type: 'string', show: true, width: 120 },
{ id: '$createdAt', title: 'Created', type: 'datetime', show: true, width: 120 },
{ id: '$updatedAt', title: 'Updated', type: 'datetime', show: true, width: 120 }
]);
@@ -0,0 +1,47 @@
<script lang="ts">
import { base } from '$app/paths';
import { page } from '$app/stores';
import { Id } from '$lib/components';
import { toLocaleDateTime } from '$lib/helpers/date';
import type { PageData } from './$types';
import { columns } from './store';
import { Table } from '@appwrite.io/pink-svelte';
export let data: PageData;
const projectId = $page.params.project;
</script>
<Table.Root>
<svelte:fragment slot="header">
{#each $columns as column}
{#if column.show}
<Table.Header.Cell width={column.width + 'px'}>{column.title}</Table.Header.Cell>
{/if}
{/each}
</svelte:fragment>
{#each data.buckets.buckets as bucket (bucket.$id)}
<Table.Link href={`${base}/project-${projectId}/storage/bucket-${bucket.$id}`}>
{#each $columns as column}
{#if column.show}
{#if column.id === '$id'}
{#key $columns}
<Table.Cell width={column.width + 'px'}>
<Id value={bucket.$id}>
{bucket.$id}
</Id>
</Table.Cell>
{/key}
{:else if column.id === 'name'}
<Table.Cell width={column.width + 'px'}>
{bucket.name}
</Table.Cell>
{:else}
<Table.Cell width={column.width + 'px'}>
{toLocaleDateTime(bucket[column.id])}
</Table.Cell>
{/if}
{/if}
{/each}
</Table.Link>
{/each}
</Table.Root>