mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
updates.
This commit is contained in:
+1
-1
@@ -26,7 +26,7 @@
|
||||
"@appwrite.io/pink-icons": "0.25.0",
|
||||
"@appwrite.io/pink-icons-svelte": "^2.0.0-RC.1",
|
||||
"@appwrite.io/pink-legacy": "^1.0.3",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@7f8f433",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@8057bf6",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@sentry/sveltekit": "^8.38.0",
|
||||
"@stripe/stripe-js": "^3.5.0",
|
||||
|
||||
Generated
+9
-10
@@ -24,8 +24,8 @@ importers:
|
||||
specifier: ^1.0.3
|
||||
version: 1.0.3
|
||||
'@appwrite.io/pink-svelte':
|
||||
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@7f8f433
|
||||
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@7f8f433(svelte@5.25.3)
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@8057bf6
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@8057bf6(svelte@5.25.3)
|
||||
'@popperjs/core':
|
||||
specifier: ^2.11.8
|
||||
version: 2.11.8
|
||||
@@ -261,9 +261,8 @@ packages:
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@8836b0c}
|
||||
version: 1.9.0
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@7f8f43343777058cd7f5801b32e05a6917918d44':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@7f8f43343777058cd7f5801b32e05a6917918d44}
|
||||
version: 2.0.0-RC.1
|
||||
'@appwrite.io/pink-icons-svelte@2.0.0-RC.1':
|
||||
resolution: {integrity: sha512-iLFlV55hj8mGuAbmxJGenxN5RaZMmVT4GJb9dv/MP1xBAtYibFq7JvBcxm18qV2KU8c31Rntf+Ub4GL7HwqTYg==}
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
|
||||
@@ -282,8 +281,8 @@ packages:
|
||||
'@appwrite.io/pink-legacy@1.0.3':
|
||||
resolution: {integrity: sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ==}
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@7f8f433':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@7f8f433}
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@8057bf6':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@8057bf6}
|
||||
version: 2.0.0-RC.2
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
@@ -3663,7 +3662,7 @@ snapshots:
|
||||
|
||||
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@8836b0c': {}
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@7f8f43343777058cd7f5801b32e05a6917918d44(svelte@5.25.3)':
|
||||
'@appwrite.io/pink-icons-svelte@2.0.0-RC.1(svelte@5.25.3)':
|
||||
dependencies:
|
||||
svelte: 5.25.3
|
||||
|
||||
@@ -3680,9 +3679,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@7f8f433(svelte@5.25.3)':
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@8057bf6(svelte@5.25.3)':
|
||||
dependencies:
|
||||
'@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@7f8f43343777058cd7f5801b32e05a6917918d44(svelte@5.25.3)
|
||||
'@appwrite.io/pink-icons-svelte': 2.0.0-RC.1(svelte@5.25.3)
|
||||
'@floating-ui/dom': 1.6.13
|
||||
'@melt-ui/pp': 0.3.2(@melt-ui/svelte@0.86.6(svelte@5.25.3))(svelte@5.25.3)
|
||||
'@melt-ui/svelte': 0.86.6(svelte@5.25.3)
|
||||
|
||||
@@ -4,23 +4,39 @@
|
||||
import { preferences } from '$lib/stores/preferences';
|
||||
import { onMount, type Snippet } from 'svelte';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import { ActionMenu, Layout, Popover, Selector } from '@appwrite.io/pink-svelte';
|
||||
import { ActionMenu, Divider, Layout, Popover, Selector } from '@appwrite.io/pink-svelte';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
|
||||
let {
|
||||
columns,
|
||||
isCustomCollection = false,
|
||||
ui = 'legacy',
|
||||
allowNoColumns = false,
|
||||
children
|
||||
}: {
|
||||
columns: Writable<Column[]>;
|
||||
isCustomCollection?: boolean;
|
||||
allowNoColumns?: boolean;
|
||||
ui?: 'legacy' | 'new';
|
||||
children: Snippet<[toggle: () => void, selectedColumnsNumber: number]>;
|
||||
} = $props();
|
||||
|
||||
let search = $state('');
|
||||
let filteredColumns = $derived(
|
||||
$columns
|
||||
.filter((column) => !column.isAction)
|
||||
.filter(
|
||||
(col) =>
|
||||
!col.exclude &&
|
||||
(col.title?.toLowerCase().includes(search.toLowerCase()) ||
|
||||
col.id?.toLowerCase().includes(search.toLowerCase()))
|
||||
)
|
||||
);
|
||||
|
||||
let maxHeight = $state('none');
|
||||
let containerRef = $state<HTMLElement>(null);
|
||||
const collectionId = $derived(page.params.collection);
|
||||
const isNewStyle = ui === 'new';
|
||||
|
||||
const calcMaxHeight = () => {
|
||||
if (containerRef) {
|
||||
@@ -78,38 +94,122 @@
|
||||
|
||||
function toggleColumn(column: Column) {
|
||||
columns.update((cols) =>
|
||||
cols.map((col) => {
|
||||
if (col.id === column.id) {
|
||||
column.hide = !column.hide;
|
||||
}
|
||||
return col;
|
||||
})
|
||||
cols.map((col) => (col.id === column.id ? { ...col, hide: !col.hide } : col))
|
||||
);
|
||||
|
||||
saveColumnPreferences();
|
||||
}
|
||||
|
||||
function selectAll() {
|
||||
columns.update((cols) =>
|
||||
cols.map((col) =>
|
||||
col.exclude
|
||||
? col
|
||||
: filteredColumns.some((fc) => fc.id === col.id)
|
||||
? { ...col, hide: false }
|
||||
: col
|
||||
)
|
||||
);
|
||||
saveColumnPreferences();
|
||||
}
|
||||
|
||||
function deselectAll() {
|
||||
columns.update((cols) => {
|
||||
const realColumns = cols.filter((col) => !col.exclude && !col.isAction);
|
||||
const filtered = filteredColumns.filter((col) => !col.exclude && !col.isAction);
|
||||
|
||||
if (filtered.length === 0) return cols;
|
||||
|
||||
const visibleRealColumns = realColumns.filter((col) => !col.hide);
|
||||
|
||||
if (!allowNoColumns && visibleRealColumns.length <= 1) {
|
||||
return cols;
|
||||
}
|
||||
|
||||
const willHideCount = filtered.filter((col) => !col.hide).length;
|
||||
|
||||
if (!allowNoColumns && visibleRealColumns.length - willHideCount < 1) {
|
||||
const [keep] = filtered;
|
||||
return cols.map((col) => {
|
||||
if (col.exclude || col.isAction) return col;
|
||||
if (!filtered.some((fc) => fc.id === col.id)) return col;
|
||||
|
||||
if (col.id === keep.id) {
|
||||
const stillVisible = cols
|
||||
.filter((c) => !c.exclude && !c.isAction)
|
||||
.filter((c) =>
|
||||
c.id === keep.id
|
||||
? true
|
||||
: filtered.some((fc) => fc.id === c.id)
|
||||
? false
|
||||
: !c.hide
|
||||
);
|
||||
if (stillVisible.length === 1 && typeof col.width === 'number') {
|
||||
return { ...col, hide: false, width: { min: col.width } };
|
||||
}
|
||||
return { ...col, hide: false };
|
||||
}
|
||||
|
||||
return { ...col, hide: true };
|
||||
});
|
||||
}
|
||||
|
||||
return cols.map((col) =>
|
||||
col.exclude || col.isAction
|
||||
? col
|
||||
: filtered.some((fc) => fc.id === col.id)
|
||||
? { ...col, hide: true }
|
||||
: col
|
||||
);
|
||||
});
|
||||
saveColumnPreferences();
|
||||
}
|
||||
|
||||
let visibleRealColumns = $derived(
|
||||
$columns.filter((col) => !col.exclude && !col.isAction && !col.hide)
|
||||
);
|
||||
</script>
|
||||
|
||||
<svelte:window on:resize={calcMaxHeight} />
|
||||
|
||||
{#if $columns?.length}
|
||||
<Popover let:toggle placement="bottom-end" padding="none">
|
||||
{@const placement = isNewStyle ? undefined : 'bottom-end'}
|
||||
<Popover let:toggle {placement} padding="none">
|
||||
{@render children(toggle, selectedColumnsNumber)}
|
||||
<svelte:fragment slot="tooltip">
|
||||
<div style:max-height={maxHeight} style:overflow="scroll" bind:this={containerRef}>
|
||||
<ActionMenu.Root>
|
||||
{#each $columns as column}
|
||||
{#if isNewStyle}
|
||||
<Layout.Stack gap="m">
|
||||
<ActionMenu.Item.Input
|
||||
id="columns"
|
||||
placeholder="Search"
|
||||
bind:value={search} />
|
||||
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
style="padding-block-end: 0.5rem">
|
||||
<Button icon extraCompact on:click={selectAll}>Select all</Button>
|
||||
|
||||
<div style:height="1rem">
|
||||
<Divider vertical />
|
||||
</div>
|
||||
|
||||
<Button icon extraCompact on:click={deselectAll}
|
||||
>Deselect all</Button>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
|
||||
{#each filteredColumns as column}
|
||||
{#if !column?.exclude}
|
||||
<ActionMenu.Item.Button
|
||||
on:click={() => toggleColumn(column)}
|
||||
disabled={allowNoColumns
|
||||
? false
|
||||
: selectedColumnsNumber <= 1 && column.hide !== true}>
|
||||
: visibleRealColumns.length <= 1 && !column.hide}>
|
||||
<Layout.Stack direction="row" gap="s">
|
||||
<Selector.Checkbox
|
||||
checked={!column.hide}
|
||||
size="s"
|
||||
on:click={() => toggleColumn(column)} />
|
||||
<Selector.Checkbox size="s" checked={!column.hide} />
|
||||
{column.title}
|
||||
</Layout.Stack>
|
||||
</ActionMenu.Item.Button>
|
||||
|
||||
@@ -221,8 +221,7 @@
|
||||
}
|
||||
|
||||
.upload-box-content {
|
||||
min-width: 400px;
|
||||
max-width: 100vw;
|
||||
width: 304px;
|
||||
}
|
||||
|
||||
.upload-box-button {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import { Click, Submit, trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
export let query = '[]';
|
||||
export let onlyIcon = false;
|
||||
export let columns: Writable<Column[]>;
|
||||
export let disabled = false;
|
||||
export let fullWidthMobile = false;
|
||||
@@ -124,62 +125,73 @@
|
||||
<Popover let:toggle placement="bottom-start">
|
||||
<Button
|
||||
secondary
|
||||
icon={onlyIcon}
|
||||
on:click={(event) => {
|
||||
toggle(event);
|
||||
trackEvent(Click.FilterApplyClick, { source: analyticsSource });
|
||||
}}
|
||||
{disabled}>
|
||||
<Icon icon={IconFilterLine} slot="start" size="s" />
|
||||
Filters
|
||||
{#if applied > 0}
|
||||
<span class="inline-tag">
|
||||
{applied}
|
||||
</span>
|
||||
<Icon icon={IconFilterLine} slot="start" size={onlyIcon ? 'm' : 's'} />
|
||||
{#if !onlyIcon}
|
||||
Filters
|
||||
{#if applied > 0}
|
||||
<span class="inline-tag">
|
||||
{applied}
|
||||
</span>
|
||||
{/if}
|
||||
{/if}
|
||||
</Button>
|
||||
<svelte:fragment slot="tooltip">
|
||||
<div style:width="420px">
|
||||
<Layout.Stack gap="s">
|
||||
{#if displayQuickFilters}
|
||||
<slot name="quick" />
|
||||
{:else}
|
||||
<p>Apply filter rules to refine the table view</p>
|
||||
<Content
|
||||
bind:columnId={selectedColumn}
|
||||
bind:operatorKey
|
||||
bind:value
|
||||
bind:arrayValues
|
||||
{columns}
|
||||
{singleCondition}
|
||||
on:apply={afterApply}
|
||||
on:clear={() => (applied = 0)} />
|
||||
{/if}
|
||||
<div
|
||||
class="u-flex u-cross-center u-margin-block-start-16"
|
||||
class:u-main-end={!quickFilters}
|
||||
class:u-main-space-between={quickFilters}>
|
||||
{#if quickFilters}
|
||||
<Button
|
||||
text
|
||||
on:click={() => (displayQuickFilters = !displayQuickFilters)}
|
||||
class="u-margin-block-end-auto">
|
||||
{displayQuickFilters ? 'Advanced filters' : 'Quick filters'}
|
||||
</Button>
|
||||
|
||||
<svelte:fragment slot="tooltip" let:showing>
|
||||
{#if showing}
|
||||
<div style:width="420px">
|
||||
<Layout.Stack gap="s">
|
||||
{#if displayQuickFilters}
|
||||
<slot name="quick" />
|
||||
{:else}
|
||||
<p>Apply filter rules to refine the table view</p>
|
||||
<!-- this needs to be portalled -->
|
||||
<Content
|
||||
bind:columnId={selectedColumn}
|
||||
bind:operatorKey
|
||||
bind:value
|
||||
bind:arrayValues
|
||||
{columns}
|
||||
{singleCondition}
|
||||
on:apply={afterApply}
|
||||
on:clear={() => (applied = 0)} />
|
||||
{/if}
|
||||
<div class="u-flex u-gap-8">
|
||||
{#if singleCondition}
|
||||
<Button size="s" text on:click={toggleDropdown}>Cancel</Button>
|
||||
{:else}
|
||||
<Button size="s" disabled={applied === 0} text on:click={clearAll}>
|
||||
Clear all
|
||||
<div
|
||||
class="u-flex u-cross-center u-margin-block-start-16"
|
||||
class:u-main-end={!quickFilters}
|
||||
class:u-main-space-between={quickFilters}>
|
||||
{#if quickFilters}
|
||||
<Button
|
||||
text
|
||||
on:click={() => (displayQuickFilters = !displayQuickFilters)}
|
||||
class="u-margin-block-end-auto">
|
||||
{displayQuickFilters ? 'Advanced filters' : 'Quick filters'}
|
||||
</Button>
|
||||
{/if}
|
||||
<Button size="s" on:click={apply} disabled={isButtonDisabled}
|
||||
>Apply</Button>
|
||||
<div class="u-flex u-gap-8">
|
||||
{#if singleCondition}
|
||||
<Button size="s" text on:click={toggleDropdown}>Cancel</Button>
|
||||
{:else}
|
||||
<Button
|
||||
size="s"
|
||||
disabled={applied === 0}
|
||||
text
|
||||
on:click={clearAll}>
|
||||
Clear all
|
||||
</Button>
|
||||
{/if}
|
||||
<Button size="s" on:click={apply} disabled={isButtonDisabled}
|
||||
>Apply</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</div>
|
||||
|
||||
@@ -12,11 +12,13 @@
|
||||
let {
|
||||
column,
|
||||
state,
|
||||
onSort = () => {}
|
||||
onSort = () => {},
|
||||
disabled = false
|
||||
}: {
|
||||
column: string;
|
||||
onSort: (query: string[]) => void | Promise<void>;
|
||||
state: Writable<{ column: string | null; direction: SortDirection }>;
|
||||
disabled?: boolean;
|
||||
onSort?: (query: string[]) => void | Promise<void>;
|
||||
state?: Writable<{ column: string | null; direction: SortDirection }>;
|
||||
} = $props();
|
||||
|
||||
function sort() {
|
||||
@@ -29,27 +31,27 @@
|
||||
? 'default'
|
||||
: 'asc';
|
||||
|
||||
state.set({
|
||||
state?.set({
|
||||
direction: nextDir,
|
||||
column: nextDir === 'default' ? null : column
|
||||
});
|
||||
|
||||
if (nextDir === 'default') {
|
||||
onSort([]);
|
||||
onSort?.([]);
|
||||
} else if (nextDir === 'asc') {
|
||||
onSort([Query.orderAsc(column)]);
|
||||
onSort?.([Query.orderAsc(column)]);
|
||||
} else {
|
||||
onSort([Query.orderDesc(column)]);
|
||||
onSort?.([Query.orderDesc(column)]);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Button extraCompact on:click={sort} class="hoverable-compact">
|
||||
<Button extraCompact on:click={sort} class="hoverable-compact" {disabled}>
|
||||
<Icon
|
||||
size="s"
|
||||
icon={$state.column !== column || $state.direction === 'default'
|
||||
icon={$state?.column !== column || $state?.direction === 'default'
|
||||
? IconSelector
|
||||
: $state.direction === 'asc'
|
||||
: $state?.direction === 'asc'
|
||||
? IconChevronUp
|
||||
: IconChevronDown}
|
||||
color="--fgcolor-neutral-weak" />
|
||||
@@ -74,5 +76,11 @@
|
||||
background-color: var(--bgcolor-neutral-tertiary);
|
||||
border: var(--border-width-s) solid var(--bgcolor-neutral-tertiary);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
& :global(i) {
|
||||
--icon-fill: var(--fgcolor-neutral-secondary) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,26 +4,13 @@
|
||||
</script>
|
||||
|
||||
{#if $uploader?.isOpen}
|
||||
<div class="upload-box-wrapper">
|
||||
<Upload.Box
|
||||
files={$uploader.files.map((file) => {
|
||||
return {
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
status: file.status
|
||||
};
|
||||
})}
|
||||
on:close={() => uploader.close()} />
|
||||
</div>
|
||||
<Upload.Box
|
||||
files={$uploader.files.map((file) => {
|
||||
return {
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
status: file.status
|
||||
};
|
||||
})}
|
||||
on:close={() => uploader.close()} />
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.upload-box-wrapper {
|
||||
min-width: 400px;
|
||||
max-width: 100vw;
|
||||
|
||||
& :global(.upload-box) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
import ViewToggle from './viewToggle.svelte';
|
||||
import ColumnSelector from './columnSelector.svelte';
|
||||
|
||||
export let onlyIcon = false;
|
||||
export let ui: 'legacy' | 'new' = 'legacy';
|
||||
export let columns: Writable<Column[]>;
|
||||
export let view: View;
|
||||
export let isCustomCollection = false;
|
||||
@@ -16,12 +18,13 @@
|
||||
</script>
|
||||
|
||||
{#if !hideColumns && view === View.Table}
|
||||
<ColumnSelector {columns} {isCustomCollection} {allowNoColumns}>
|
||||
<ColumnSelector {columns} {isCustomCollection} {allowNoColumns} {ui}>
|
||||
{#snippet children(toggle, selectedColumnsNumber)}
|
||||
<Button.Button
|
||||
size="s"
|
||||
icon={onlyIcon}
|
||||
variant="secondary"
|
||||
badge={selectedColumnsNumber.toString()}
|
||||
badge={onlyIcon ? undefined : selectedColumnsNumber.toString()}
|
||||
on:click={toggle}>
|
||||
<Icon slot="start" icon={IconViewBoards} />
|
||||
</Button.Button>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { get } from 'svelte/store';
|
||||
import { user } from '$lib/stores/user';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { page } from '$app/state';
|
||||
|
||||
const userPreferences = () => get(user)?.prefs;
|
||||
|
||||
@@ -18,6 +19,10 @@ export function updateSidebarState(state: 'closed' | 'open' | 'icons') {
|
||||
}
|
||||
|
||||
export function getSidebarState(): 'open' | 'icons' {
|
||||
// for spreadsheet
|
||||
const isInDatabases = page.route.id?.includes('database-[database]');
|
||||
if (isInDatabases) return 'icons';
|
||||
|
||||
const currentPrefs = userPreferences();
|
||||
if (currentPrefs && currentPrefs.sidebarState) {
|
||||
if (currentPrefs.sidebarState === 'open' || currentPrefs.sidebarState === 'icons') {
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
margin-block: var(--base-32);
|
||||
|
||||
&.expanded {
|
||||
max-width: unset;
|
||||
margin-inline: 2.75rem;
|
||||
padding-inline-end: 0 !important;
|
||||
margin-block: var(--base-8) !important;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
export let size: 'small' | 'medium' | 'large' | 'xl' = null;
|
||||
export let blocksize = '152px';
|
||||
export let expanded: boolean = true;
|
||||
|
||||
$: style = size
|
||||
? `--p-container-max-size: var(--container-max-size, var(--container-size-${size}))`
|
||||
@@ -10,15 +11,17 @@
|
||||
</script>
|
||||
|
||||
<div class="top-cover-console" style:block-size={blocksize}>
|
||||
<div class="cover-container" {style}>
|
||||
<div class="cover-container" {style} class:expanded>
|
||||
<Layout.Stack direction="row" alignItems="baseline">
|
||||
<slot name="header" />
|
||||
</Layout.Stack>
|
||||
<slot />
|
||||
<div class:expanded-slot={expanded}>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
<style lang="scss">
|
||||
.top-cover-console {
|
||||
container-type: inline-size;
|
||||
padding-block-start: var(--base-32);
|
||||
@@ -55,5 +58,13 @@
|
||||
margin-inline: auto;
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
margin-inline: 1rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
.expanded-slot {
|
||||
margin-inline: 3rem !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { isSmallViewport, isTabletViewport } from '$lib/stores/viewport';
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import { IconChevronLeft } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Typography, Button, Icon, Layout } from '@appwrite.io/pink-svelte';
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
<Layout.Stack justifyContent="center" alignItems="center" direction="row" gap="xs" inline>
|
||||
{#if href}
|
||||
<span
|
||||
style={$isTabletViewport ? 'position: relative' : 'position: absolute; left: -2.75rem'}>
|
||||
<span style:position="relative">
|
||||
<Button.Anchor size="s" icon variant="text" {href} aria-label="page back">
|
||||
<Icon icon={IconChevronLeft} />
|
||||
</Button.Anchor>
|
||||
|
||||
+2
@@ -3,6 +3,7 @@ import { Dependencies } from '$lib/constants';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import Breadcrumbs from './breadcrumbs.svelte';
|
||||
import Header from './header.svelte';
|
||||
import SubNavigation from './subNavigation.svelte';
|
||||
|
||||
export const load: LayoutLoad = async ({ params, depends }) => {
|
||||
depends(Dependencies.DATABASE);
|
||||
@@ -10,6 +11,7 @@ export const load: LayoutLoad = async ({ params, depends }) => {
|
||||
return {
|
||||
header: Header,
|
||||
breadcrumbs: Breadcrumbs,
|
||||
subNavigation: SubNavigation,
|
||||
database: await sdk.forProject(params.region, params.project).databases.get(params.database)
|
||||
};
|
||||
};
|
||||
|
||||
+2
-4
@@ -3,13 +3,12 @@ import { Dependencies } from '$lib/constants';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import Breadcrumbs from './breadcrumbs.svelte';
|
||||
import Header from './header.svelte';
|
||||
import SubNavigation from './subNavigation.svelte';
|
||||
import { Query } from '@appwrite.io/console';
|
||||
|
||||
export const load: LayoutLoad = async ({ params, depends }) => {
|
||||
depends(Dependencies.COLLECTION);
|
||||
|
||||
const [collection, allCollections] = await Promise.all([
|
||||
const [collection, collections] = await Promise.all([
|
||||
sdk
|
||||
.forProject(params.region, params.project)
|
||||
.databases.getCollection(params.database, params.collection),
|
||||
@@ -21,8 +20,7 @@ export const load: LayoutLoad = async ({ params, depends }) => {
|
||||
return {
|
||||
header: Header,
|
||||
breadcrumbs: Breadcrumbs,
|
||||
subNavigation: SubNavigation,
|
||||
collection,
|
||||
allCollections
|
||||
collections
|
||||
};
|
||||
};
|
||||
|
||||
+19
-8
@@ -18,7 +18,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Click, Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
import { IconPlus, IconSearch } from '@appwrite.io/pink-icons-svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import EmptySheet from './layout/emptySheet.svelte';
|
||||
import CreateRecord from './createRecord.svelte';
|
||||
@@ -83,17 +83,28 @@
|
||||
<Container expanded style="background: var(--bgcolor-neutral-primary)">
|
||||
<Layout.Stack direction="column" gap="xl">
|
||||
<Layout.Stack direction="row" justifyContent="space-between">
|
||||
<Filters
|
||||
query={data.query}
|
||||
columns={filterColumns}
|
||||
disabled={!(hasAttributes && hasValidAttributes)}
|
||||
analyticsSource="database_documents" />
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<Layout.Stack direction="row" gap="s">
|
||||
<ViewSelector
|
||||
onlyIcon
|
||||
ui="new"
|
||||
view={data.view}
|
||||
columns={filterColumns}
|
||||
{columns}
|
||||
hideView
|
||||
isCustomCollection />
|
||||
|
||||
<Filters
|
||||
onlyIcon
|
||||
query={data.query}
|
||||
columns={filterColumns}
|
||||
disabled={!(hasAttributes && hasValidAttributes)}
|
||||
analyticsSource="database_documents" />
|
||||
|
||||
<!-- TODO: not yet implemented -->
|
||||
<Button icon secondary>
|
||||
<Icon icon={IconSearch} slot="start" />
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<Button
|
||||
secondary
|
||||
event={Click.DatabaseImportCsv}
|
||||
|
||||
+42
-40
@@ -103,45 +103,47 @@
|
||||
|
||||
{#if $createDocument}
|
||||
<div class="sheet-container">
|
||||
<!-- TODO: add a ID badge-->
|
||||
<SideSheet
|
||||
spaced
|
||||
title={`${existingData ? 'Duplicate' : 'Create'} record`}
|
||||
bind:show={showSheet}
|
||||
closeOnBlur={false}
|
||||
submit={{
|
||||
text: 'Create',
|
||||
disabled: isSubmitting,
|
||||
onClick: () => create()
|
||||
}}>
|
||||
<Layout.Stack gap="xxl">
|
||||
<AttributeForm
|
||||
bind:formValues={$createDocument.document}
|
||||
attributes={$createDocument.attributes}
|
||||
bind:customId={$createDocument.id} />
|
||||
<!-- TODO: add a ID badge-->
|
||||
<SideSheet
|
||||
spaced
|
||||
title={`${existingData ? 'Duplicate' : 'Create'} record`}
|
||||
bind:show={showSheet}
|
||||
closeOnBlur={false}
|
||||
submit={{
|
||||
text: 'Create',
|
||||
disabled: isSubmitting,
|
||||
onClick: () => create()
|
||||
}}>
|
||||
<Layout.Stack gap="xxl">
|
||||
<AttributeForm
|
||||
bind:formValues={$createDocument.document}
|
||||
attributes={$createDocument.attributes}
|
||||
bind:customId={$createDocument.id} />
|
||||
|
||||
<Layout.Stack gap="xl">
|
||||
<Typography.Text>
|
||||
Choose which permission scopes to grant your application. It is best practice to
|
||||
allow only the permissions you need to meet your project goals.
|
||||
</Typography.Text>
|
||||
{#if collection.documentSecurity}
|
||||
<Alert.Inline status="info">
|
||||
<svelte:fragment slot="title">Document security is enabled</svelte:fragment>
|
||||
Users will be able to access this document if they have been granted
|
||||
<b>either document or collection permissions</b>.
|
||||
</Alert.Inline>
|
||||
<Permissions bind:permissions={$createDocument.permissions} />
|
||||
{:else}
|
||||
<Alert.Inline status="info">
|
||||
<svelte:fragment slot="title"
|
||||
>Document security is disabled</svelte:fragment>
|
||||
If you want to assign document permissions, navigate to Collection settings and
|
||||
enable document security. Otherwise, only collection permissions will be used.
|
||||
</Alert.Inline>
|
||||
{/if}
|
||||
<Layout.Stack gap="xl">
|
||||
<Typography.Text>
|
||||
Choose which permission scopes to grant your application. It is best
|
||||
practice to allow only the permissions you need to meet your project goals.
|
||||
</Typography.Text>
|
||||
{#if collection.documentSecurity}
|
||||
<Alert.Inline status="info">
|
||||
<svelte:fragment slot="title"
|
||||
>Document security is enabled</svelte:fragment>
|
||||
Users will be able to access this document if they have been granted
|
||||
<b>either document or collection permissions</b>.
|
||||
</Alert.Inline>
|
||||
<Permissions bind:permissions={$createDocument.permissions} />
|
||||
{:else}
|
||||
<Alert.Inline status="info">
|
||||
<svelte:fragment slot="title"
|
||||
>Document security is disabled</svelte:fragment>
|
||||
If you want to assign document permissions, navigate to Collection settings
|
||||
and enable document security. Otherwise, only collection permissions will
|
||||
be used.
|
||||
</Alert.Inline>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</SideSheet>
|
||||
</div>
|
||||
{/if}
|
||||
</SideSheet>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@
|
||||
</script>
|
||||
|
||||
<div style:margin-top={$isTabletViewport ? '48px' : 0}>
|
||||
<Cover>
|
||||
<Cover expanded>
|
||||
<svelte:fragment slot="header">
|
||||
<CoverTitle
|
||||
href={`${base}/project-${page.params.region}-${page.params.project}/databases/database-${databaseId}`}>
|
||||
|
||||
+24
-12
@@ -10,6 +10,7 @@
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import { IconCalendar, IconFingerPrint, IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import { SortButton } from '$lib/components';
|
||||
|
||||
type Mode = 'records' | 'columns' | 'indexes';
|
||||
|
||||
@@ -37,14 +38,14 @@
|
||||
id: '$id',
|
||||
title: 'ID',
|
||||
type: 'string',
|
||||
width: 200,
|
||||
width: 180,
|
||||
icon: IconFingerPrint
|
||||
},
|
||||
...middle,
|
||||
{
|
||||
id: 'actions',
|
||||
title: '',
|
||||
width: 555,
|
||||
width: 555 * 1.5,
|
||||
draggable: false,
|
||||
type: 'string',
|
||||
icon: IconPlus
|
||||
@@ -52,9 +53,6 @@
|
||||
{
|
||||
id: 'empty',
|
||||
title: '',
|
||||
width: 40,
|
||||
isAction: true,
|
||||
draggable: false,
|
||||
type: 'string'
|
||||
}
|
||||
];
|
||||
@@ -66,7 +64,7 @@
|
||||
id: '$createdAt',
|
||||
title: 'Created',
|
||||
type: 'datetime',
|
||||
width: { min: 180 },
|
||||
width: 180,
|
||||
draggable: false,
|
||||
icon: IconCalendar
|
||||
},
|
||||
@@ -74,7 +72,7 @@
|
||||
id: '$updatedAt',
|
||||
title: 'Updated',
|
||||
type: 'datetime',
|
||||
width: { min: 180 },
|
||||
width: 180,
|
||||
draggable: false,
|
||||
icon: IconCalendar
|
||||
}
|
||||
@@ -84,7 +82,7 @@
|
||||
id: 'indexed',
|
||||
title: 'Indexed',
|
||||
type: 'boolean',
|
||||
width: { min: 180 },
|
||||
width: 180,
|
||||
draggable: false,
|
||||
isAction: false
|
||||
},
|
||||
@@ -92,7 +90,7 @@
|
||||
id: 'default',
|
||||
title: 'Default',
|
||||
type: 'string',
|
||||
width: { min: 180 },
|
||||
width: 180,
|
||||
draggable: false,
|
||||
isAction: false
|
||||
}
|
||||
@@ -102,7 +100,7 @@
|
||||
id: 'type',
|
||||
title: 'Type',
|
||||
type: 'string',
|
||||
width: { min: 150 },
|
||||
width: 180,
|
||||
draggable: false,
|
||||
isAction: false
|
||||
},
|
||||
@@ -110,7 +108,7 @@
|
||||
id: 'attributes',
|
||||
title: 'Columns',
|
||||
type: 'string',
|
||||
width: { min: 200 },
|
||||
width: 180,
|
||||
draggable: false,
|
||||
isAction: false
|
||||
}
|
||||
@@ -133,8 +131,17 @@
|
||||
<Button.Button icon variant="extra-compact">
|
||||
<Icon icon={IconPlus} color="--fgcolor-neutral-primary" />
|
||||
</Button.Button>
|
||||
{:else}
|
||||
{:else if column.id === 'actions' || column.id === 'empty'}
|
||||
{column.title}
|
||||
{:else}
|
||||
<Layout.Stack
|
||||
gap="xs"
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
alignContent="center">
|
||||
{column.title}
|
||||
<SortButton disabled column={column.id} />
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
</Spreadsheet.Header.Cell>
|
||||
{/each}
|
||||
@@ -194,6 +201,11 @@
|
||||
& :global(.spreadsheet-container) {
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
|
||||
// hides the last columns left border, as per design
|
||||
& :global([role='rowheader'] [role='cell']:nth-last-child(2) [role='presentation']) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
& :global([data-select='true']) {
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@
|
||||
].filter((tab) => !tab.disabled);
|
||||
</script>
|
||||
|
||||
<Cover>
|
||||
<Cover expanded>
|
||||
<svelte:fragment slot="header">
|
||||
<CoverTitle href={`${base}/project-${page.params.region}-${projectId}/databases`}>
|
||||
{$database.name}
|
||||
|
||||
+33
-15
@@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/state';
|
||||
import { showCreate } from '../store';
|
||||
import { showCreate } from './store';
|
||||
import type { PageData } from './$types';
|
||||
import { showSubNavigation } from '$lib/stores/layout';
|
||||
import { Icon, Sidebar, Navbar, Layout, Link, ActionMenu } from '@appwrite.io/pink-svelte';
|
||||
import { Icon, Sidebar, Navbar, Layout, Link } from '@appwrite.io/pink-svelte';
|
||||
import {
|
||||
IconChevronDown,
|
||||
IconDatabase,
|
||||
@@ -13,6 +13,8 @@
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import { isTabletViewport } from '$lib/stores/viewport';
|
||||
import { BottomSheet } from '$lib/components';
|
||||
import Button from '$lib/elements/forms/button.svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
let data = $derived(page.data) as PageData;
|
||||
let region = $derived(page.params.region);
|
||||
@@ -20,12 +22,14 @@
|
||||
let databaseId = $derived(page.params.database);
|
||||
let collectionId = $derived(page.params.collection);
|
||||
|
||||
const collections = $derived(data.collections);
|
||||
|
||||
const sortedCollections = $derived.by(() =>
|
||||
data?.allCollections?.collections?.slice().sort((a, b) => a.name.localeCompare(b.name))
|
||||
collections?.collections?.slice().sort((a, b) => a.name.localeCompare(b.name))
|
||||
);
|
||||
|
||||
const selectedCollection = $derived.by(() =>
|
||||
sortedCollections?.find((collection) => collection.$id === collectionId)
|
||||
sortedCollections?.find((collection: Models.Collection) => collection.$id === collectionId)
|
||||
);
|
||||
|
||||
let openBottomSheet = $state(false);
|
||||
@@ -48,7 +52,7 @@
|
||||
{data.database?.name}
|
||||
</a>
|
||||
<div class="collection-content">
|
||||
{#if data?.allCollections?.total}
|
||||
{#if collections?.total}
|
||||
<ul class="drop-list u-margin-inline-start-8 u-margin-block-start-8">
|
||||
{#each sortedCollections as collection}
|
||||
{@const href = `${base}/project-${region}-${project}/databases/database-${databaseId}/collection-${collection.$id}`}
|
||||
@@ -69,18 +73,32 @@
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{:else}
|
||||
<Layout.Stack gap="m">
|
||||
<Layout.Stack
|
||||
gap="m"
|
||||
direction="row"
|
||||
class="u-margin-inline-start-8 u-margin-block-start-8">
|
||||
<div style="border-left: 1px solid var(--border-neutral, #ededf0)">
|
||||
</div>
|
||||
No tables yet
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
|
||||
<Layout.Stack gap="m" alignItems="center" direction="row">
|
||||
<Icon icon={IconPlus} size="s" />
|
||||
|
||||
<Button
|
||||
compact
|
||||
on:click={() => {
|
||||
$showCreate = true;
|
||||
$showSubNavigation = false;
|
||||
}}>
|
||||
Create collection
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
<ActionMenu.Root noPadding width="167">
|
||||
<ActionMenu.Item.Button
|
||||
on:click={() => {
|
||||
$showCreate = true;
|
||||
$showSubNavigation = false;
|
||||
}}
|
||||
leadingIcon={IconPlus}>
|
||||
Create collection
|
||||
</ActionMenu.Item.Button>
|
||||
</ActionMenu.Root>
|
||||
</section>
|
||||
</Sidebar.Base>
|
||||
{:else}
|
||||
Reference in New Issue
Block a user