mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
fix: various issues
This commit is contained in:
+2
-3
@@ -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",
|
||||
|
||||
Generated
+14
-17
@@ -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)
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user