mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
fix: stuff
This commit is contained in:
@@ -281,14 +281,6 @@
|
||||
--logo-bg: #f2f2f8;
|
||||
}
|
||||
|
||||
:global(.theme-dark) .footer {
|
||||
--sep-clr: hsl(var(--color-neutral-150));
|
||||
}
|
||||
|
||||
:global(.theme-light) .footer {
|
||||
--sep-clr: hsl(var(--color-neutral-30));
|
||||
}
|
||||
|
||||
.content {
|
||||
overflow: auto;
|
||||
padding: 1rem;
|
||||
@@ -324,14 +316,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
.sep {
|
||||
width: 1px;
|
||||
height: 1.5rem;
|
||||
background-color: var(--sep-clr);
|
||||
}
|
||||
}
|
||||
|
||||
.experimental {
|
||||
display: flex;
|
||||
padding: 0.09375rem 0.25rem;
|
||||
|
||||
@@ -122,9 +122,7 @@
|
||||
options={column?.elements?.map((e) => ({
|
||||
label: e?.label ?? e,
|
||||
value: e?.value ?? e
|
||||
}))}
|
||||
label="Value"
|
||||
showLabel={false} />
|
||||
}))} />
|
||||
{:else if column.type === 'integer' || column.type === 'double'}
|
||||
<InputNumber id="value" bind:value placeholder="Enter value" />
|
||||
{:else if column.type === 'boolean'}
|
||||
@@ -139,12 +137,7 @@
|
||||
bind:value />
|
||||
{:else if column.type === 'datetime'}
|
||||
{#key value}
|
||||
<InputDateTime
|
||||
id="value"
|
||||
bind:value
|
||||
label="value"
|
||||
showLabel={false}
|
||||
step={60} />
|
||||
<InputDateTime id="value" bind:value step={60} />
|
||||
{/key}
|
||||
{:else}
|
||||
<InputText id="value" bind:value placeholder="Enter value" />
|
||||
@@ -166,17 +159,3 @@
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.selects {
|
||||
:global(> *) {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
:global(b) {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -225,24 +225,3 @@
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.dropped {
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0px 16px 32px 0px rgba(55, 59, 77, 0.04);
|
||||
|
||||
padding: 1rem;
|
||||
margin-top: 0.5rem;
|
||||
|
||||
width: 37.5rem;
|
||||
|
||||
hr {
|
||||
height: 1px;
|
||||
width: calc(100% + 2rem);
|
||||
background-color: hsl(var(--border));
|
||||
|
||||
margin-block-start: 1rem;
|
||||
margin-inline: -1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,36 +1,30 @@
|
||||
<script>
|
||||
import { Card, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { Card, Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
export let href;
|
||||
</script>
|
||||
|
||||
<Card.Link class="card" {href}>
|
||||
<div class="grid-item-1">
|
||||
<div class="grid-item-1-start-start">
|
||||
<div class="eyebrow-heading-3"><slot name="eyebrow" /></div>
|
||||
<Typography.Title size="s"><slot name="title" /></Typography.Title>
|
||||
<div class="u-padding-block-start-4"><slot name="subtitle" /></div>
|
||||
</div>
|
||||
|
||||
<div class="grid-item-1-start-end">
|
||||
<slot name="status" />
|
||||
</div>
|
||||
|
||||
<div class="grid-item-1-end-start">
|
||||
<div class="u-flex u-gap-16 u-flex-wrap">
|
||||
<Layout.Stack height="calc(182 / 16 * 1rem)" justifyContent="space-between">
|
||||
<Layout.Stack direction="row">
|
||||
<Layout.Stack gap="xs">
|
||||
<Typography.Caption color="--fgcolor-neutral-tertiary"
|
||||
><slot name="eyebrow" /></Typography.Caption>
|
||||
<Typography.Title size="s"><slot name="title" /></Typography.Title>
|
||||
<div>
|
||||
<slot name="subtitle" />
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" justifyContent="flex-end" alignItems="center">
|
||||
<slot name="status" />
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row">
|
||||
<Layout.Stack direction="row">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-item-1-end-end">
|
||||
<ul class="icons u-flex u-gap-8">
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" justifyContent="flex-end">
|
||||
<slot name="icons" />
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Card.Link>
|
||||
|
||||
<style>
|
||||
/* TODO: remove this when ui library is updated*/
|
||||
.grid-item-1 {
|
||||
min-block-size: calc(182 / 16 * 1rem);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
|
||||
export let factors: Models.MfaFactors & { recoveryCode: boolean };
|
||||
/** If true, the form will be submitted automatically when the code is entered. */
|
||||
export let autoSubmit: boolean = true;
|
||||
export let showVerifyButton: boolean = true;
|
||||
export let disabled: boolean = false;
|
||||
export let challenge: Models.MfaChallenge;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
export let show = false;
|
||||
export let error: string = null;
|
||||
export let closable = true;
|
||||
export let dismissible = true;
|
||||
export let onSubmit: (e: SubmitEvent) => Promise<void> | void = function () {
|
||||
return;
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
<script lang="ts">
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { tick } from 'svelte';
|
||||
import { AvatarInitials } from '../';
|
||||
import Output from '../output.svelte';
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
Icon,
|
||||
Layout,
|
||||
Link,
|
||||
Popover,
|
||||
Spinner,
|
||||
Typography
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import Avatar from '../avatar.svelte';
|
||||
import { IconAnonymous, IconExternalLink, IconMinusSm } from '@appwrite.io/pink-icons-svelte';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
export let role: string;
|
||||
|
||||
let content: HTMLDivElement;
|
||||
let data = null;
|
||||
let isFetching = false;
|
||||
|
||||
async function getData(
|
||||
permission: string
|
||||
): Promise<
|
||||
@@ -20,139 +27,85 @@
|
||||
const role = permission.split(':')[0];
|
||||
const id = permission.split(':')[1].split('/')[0];
|
||||
if (role === 'user') {
|
||||
const user = await sdk.forProject.users.get(id);
|
||||
return user;
|
||||
return await sdk.forProject.users.get(id);
|
||||
}
|
||||
if (role === 'team') {
|
||||
const team = await sdk.forProject.teams.get(id);
|
||||
return team;
|
||||
return await sdk.forProject.teams.get(id);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="u-flex u-cross-center u-gap-8 tippy-user">
|
||||
<div>
|
||||
{#if role === 'users'}
|
||||
<div>Users</div>
|
||||
{:else if role === 'guests'}
|
||||
<div>Guests</div>
|
||||
{:else if role === 'any'}
|
||||
<div>Any</div>
|
||||
{:else}
|
||||
<div
|
||||
class="u-trim-1"
|
||||
use:tooltip={{
|
||||
interactive: true,
|
||||
allowHTML: true,
|
||||
onShow(instance) {
|
||||
if (isFetching || data) {
|
||||
return;
|
||||
}
|
||||
|
||||
getData(role)
|
||||
.then((n) => {
|
||||
data = n;
|
||||
})
|
||||
.finally(() => {
|
||||
tick().then(() => {
|
||||
instance.setContent(content);
|
||||
});
|
||||
});
|
||||
}
|
||||
}}>
|
||||
{role}
|
||||
</div>
|
||||
<div class="u-hide">
|
||||
<div bind:this={content}>
|
||||
{#if data}
|
||||
{@const isUser = role.startsWith('user')}
|
||||
{@const isTeam = role.startsWith('team')}
|
||||
{@const isAnonymous = !data.email && !data.phone && isUser}
|
||||
<div class="user-profile">
|
||||
{#if role === 'users'}
|
||||
<div>Users</div>
|
||||
{:else if role === 'guests'}
|
||||
<div>Guests</div>
|
||||
{:else if role === 'any'}
|
||||
<div>Any</div>
|
||||
{:else}
|
||||
<Popover let:toggle placement="bottom-start">
|
||||
<Link.Button on:click={toggle}>{role}</Link.Button>
|
||||
<div let:showing slot="tooltip" style:width="200px">
|
||||
{#key showing}
|
||||
{#await getData(role)}
|
||||
<Layout.Stack alignItems="center">
|
||||
<Spinner />
|
||||
</Layout.Stack>
|
||||
{:then data}
|
||||
{@const isUser = role.startsWith('user')}
|
||||
{@const isTeam = role.startsWith('team')}
|
||||
{@const isAnonymous = !data.email && !data.phone && !data.name && isUser}
|
||||
<Layout.Stack>
|
||||
<Layout.Stack direction="row" gap="s" alignItems="center">
|
||||
{#if isAnonymous}
|
||||
<div class="avatar is-size-small">
|
||||
<span class="icon-anonymous" aria-hidden="true" />
|
||||
</div>
|
||||
<Avatar alt="avatar" size="xs">
|
||||
<Icon icon={IconAnonymous} size="s" />
|
||||
</Avatar>
|
||||
{:else if data.name}
|
||||
<AvatarInitials name={data.name} size="s" />
|
||||
<AvatarInitials name={data.name} size="xs" />
|
||||
{:else}
|
||||
<div class="avatar is-size-small">
|
||||
<span class="icon-minus-sm" aria-hidden="true" />
|
||||
</div>
|
||||
<Avatar alt="avatar" size="xs">
|
||||
<Icon icon={IconMinusSm} size="s" />
|
||||
</Avatar>
|
||||
{/if}
|
||||
<span class="user-profile-info is-only-desktop">
|
||||
<span class="name">
|
||||
{data.name ?? data?.email ?? data?.phone ?? '-'}
|
||||
</span>
|
||||
<Output value={data.$id}>{role}</Output>
|
||||
</span>
|
||||
{#if (isUser && (data?.email || data?.phone)) || isTeam}
|
||||
<span class="user-profile-sep" />
|
||||
<Typography.Text truncate color="--fgcolor-neutral-primary">
|
||||
{data.name ?? data?.email ?? data?.phone ?? '-'}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
|
||||
<span class="user-profile-empty-column" />
|
||||
<span class="user-profile-info is-only-desktop">
|
||||
{#if isUser}
|
||||
<div class="u-grid u-gap-4">
|
||||
{#if data?.email}
|
||||
<p class="text u-x-small">Email: {data?.email}</p>
|
||||
{/if}
|
||||
{#if data?.phone}
|
||||
<p class="text u-x-small">Phone: {data?.phone}</p>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if isTeam}
|
||||
<p class="text u-x-small">Members: {data?.total}</p>
|
||||
{/if}
|
||||
</span>
|
||||
<Divider />
|
||||
{#if isUser}
|
||||
{#if data?.email}
|
||||
<Typography.Text truncate>Email: {data?.email}</Typography.Text>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
Not found.
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- svelte-ignore css-unused-selector -->
|
||||
<style lang="scss" global>
|
||||
.tippy-user .tippy-box {
|
||||
--p-drop-bg-color: var(--color-neutral-105);
|
||||
--p-drop-border-color: var(--color-neutral-85);
|
||||
|
||||
inset-inline-start: -0.625rem;
|
||||
inset-block-end: calc(100% + 0.625rem);
|
||||
background-color: hsl(var(--p-drop-bg-color));
|
||||
border: solid 0.0625rem hsl(var(--p-drop-border-color));
|
||||
border-radius: var(--border-radius-small);
|
||||
box-shadow: var(--shadow-small);
|
||||
font-size: var(--font-size-0);
|
||||
color: hsl(var(--p-body-text-color));
|
||||
max-inline-size: 32.5rem;
|
||||
margin-inline: auto;
|
||||
line-height: 1.5;
|
||||
|
||||
body.theme-light & {
|
||||
--p-drop-bg-color: var(--color-neutral-0);
|
||||
--p-drop-border-color: var(--color-neutral-10);
|
||||
}
|
||||
|
||||
.tippy-content {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
&[data-placement^='top'] > .tippy-arrow::before {
|
||||
border-top-color: hsl(var(--p-drop-bg-color));
|
||||
}
|
||||
&[data-placement^='bottom'] > .tippy-arrow::before {
|
||||
border-bottom-color: hsl(var(--p-drop-bg-color));
|
||||
}
|
||||
&[data-placement^='left'] > .tippy-arrow::before {
|
||||
border-left-color: hsl(var(--p-drop-bg-color));
|
||||
}
|
||||
&[data-placement^='right'] > .tippy-arrow::before {
|
||||
border-right-color: hsl(var(--p-drop-bg-color));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{#if data?.phone}
|
||||
<Typography.Text truncate>Phone: {data?.phone}</Typography.Text>
|
||||
{/if}
|
||||
<div>
|
||||
<Button.Anchor
|
||||
href={`${base}/project-${$page.params.project}/auth/user-${data?.$id}`}
|
||||
size="xs"
|
||||
target="_blank"
|
||||
variant="secondary">
|
||||
View user
|
||||
<Icon slot="end" icon={IconExternalLink} size="s" />
|
||||
</Button.Anchor>
|
||||
</div>
|
||||
{:else if isTeam}
|
||||
<Typography.Text>Members: {data?.total}</Typography.Text>
|
||||
<div>
|
||||
<Button.Anchor
|
||||
href={`${base}/project-${$page.params.project}/auth/teams/team-${data?.$id}`}
|
||||
size="s"
|
||||
target="_blank"
|
||||
variant="secondary">
|
||||
View team
|
||||
<Icon slot="end" icon={IconExternalLink} size="s" />
|
||||
</Button.Anchor>
|
||||
</div>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{/await}
|
||||
{/key}
|
||||
</div>
|
||||
</Popover>
|
||||
{/if}
|
||||
|
||||
@@ -93,23 +93,14 @@
|
||||
<Layout.Stack direction="row" alignItems="center" gap="s">
|
||||
<AvatarInitials size="xs" name={team.name} />
|
||||
<Layout.Stack gap="none">
|
||||
<Typography.Caption variant="400">Text</Typography.Caption>
|
||||
<Typography.Caption variant="400">{team.name}</Typography.Caption>
|
||||
<Typography.Caption
|
||||
variant="400"
|
||||
color="--fgcolor-neutral-tertiary">
|
||||
Secondary Text
|
||||
{team.$id}
|
||||
</Typography.Caption>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" alignItems="center" gap="s">
|
||||
<AvatarInitials size="xs" name={team.name} />
|
||||
<span>
|
||||
{team.name}
|
||||
</span>
|
||||
<span>
|
||||
{team.$id}
|
||||
</span>
|
||||
</Layout.Stack>
|
||||
</Table.Cell>
|
||||
</Table.Button>
|
||||
{/each}
|
||||
|
||||
@@ -17,13 +17,7 @@
|
||||
Table,
|
||||
Typography
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import {
|
||||
IconAnonymous,
|
||||
IconChartSquareBar,
|
||||
IconCheck,
|
||||
IconMinus,
|
||||
IconMinusSm
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import { IconAnonymous, IconMinusSm } from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
export let show: boolean;
|
||||
export let groups: Writable<Map<string, Permission>>;
|
||||
|
||||
@@ -4,5 +4,13 @@
|
||||
</script>
|
||||
|
||||
{#if $uploader?.isOpen}
|
||||
<Upload.Box files={$uploader.files} on:close={() => uploader.close()} />
|
||||
<Upload.Box
|
||||
files={$uploader.files.map((file) => {
|
||||
return {
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
status: file.status
|
||||
};
|
||||
})}
|
||||
on:close={() => uploader.close()} />
|
||||
{/if}
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
export let hideView = false;
|
||||
export let hideColumns = false;
|
||||
export let allowNoColumns = false;
|
||||
export let fullWidthMobile = false;
|
||||
|
||||
onMount(async () => {
|
||||
if (isCustomCollection) {
|
||||
|
||||
@@ -6,11 +6,6 @@
|
||||
export let required = false;
|
||||
export let disabled = false;
|
||||
export let readonly = false;
|
||||
export let autofocus = false;
|
||||
export let fullWidth = false;
|
||||
export let autoSubmit = true;
|
||||
|
||||
$: console.log(value);
|
||||
</script>
|
||||
|
||||
<Input.OTP {length} bind:value {required} {disabled} {readonly} {autofocus} size="s" {fullWidth} />
|
||||
<Input.OTP {length} bind:value {required} {disabled} {readonly} size="s" />
|
||||
|
||||
@@ -144,15 +144,3 @@
|
||||
<Helper class="u-position-relative" type="warning">{error}</Helper>
|
||||
{/if}
|
||||
</div> -->
|
||||
|
||||
<style>
|
||||
.form-item :global(.drop) {
|
||||
translate: 0 4px;
|
||||
}
|
||||
|
||||
.form-item :global(.drop-section) {
|
||||
width: 100%;
|
||||
margin-inline: initial;
|
||||
max-inline-size: initial;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import InputCheckbox from './forms/inputCheckbox.svelte';
|
||||
|
||||
export let value = false;
|
||||
export let padding: number | null = null;
|
||||
</script>
|
||||
|
||||
<ActionMenu.Item.Button on:click>
|
||||
|
||||
@@ -8,6 +8,7 @@ type UploaderFile = {
|
||||
resourceId: string;
|
||||
name: string;
|
||||
progress: number;
|
||||
size: number;
|
||||
status: 'failed' | 'pending' | 'success';
|
||||
error?: string;
|
||||
};
|
||||
@@ -73,6 +74,7 @@ const createUploader = () => {
|
||||
$id: id,
|
||||
resourceId: bucketId,
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
progress: 0,
|
||||
status: 'pending'
|
||||
};
|
||||
@@ -104,6 +106,7 @@ const createUploader = () => {
|
||||
$id: '',
|
||||
resourceId: siteId,
|
||||
name: code.name,
|
||||
size: code.size,
|
||||
progress: 0,
|
||||
status: 'pending'
|
||||
};
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
<EmailVerificationTemplate />
|
||||
</Layout.Stack>
|
||||
</Accordion>
|
||||
<!-- <Accordion
|
||||
<Accordion
|
||||
title="Magic URL"
|
||||
bind:open={emailMagicSessionOpen}
|
||||
on:click={(e) => {
|
||||
@@ -179,7 +179,7 @@
|
||||
}}>
|
||||
Send an email to users when a new session is created.
|
||||
<EmailSessionAlertTemplate />
|
||||
</Accordion> -->
|
||||
</Accordion>
|
||||
</Layout.Stack>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Id } from '$lib/components';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
|
||||
const projectId = $page.params.project;
|
||||
let locale = 'en';
|
||||
@@ -34,11 +35,11 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="boxes-wrapper u-margin-block-start-16">
|
||||
<Layout.Stack>
|
||||
<LocaleOptions on:change={onLocaleChange} bind:value={locale} />
|
||||
<EmailTemplate bind:loading>
|
||||
<Id value={'{{user}}'}>{'{{user}}'}</Id>
|
||||
<Id value={'{{project}}'}>{'{{project}}'}</Id>
|
||||
<Id value={'{{otp}}'}>{'{{otp}}'}</Id>
|
||||
</EmailTemplate>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Id } from '$lib/components';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
|
||||
const projectId = $page.params.project;
|
||||
let locale = 'en';
|
||||
@@ -34,7 +35,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="boxes-wrapper u-margin-block-start-16">
|
||||
<Layout.Stack>
|
||||
<LocaleOptions on:change={onLocaleChange} bind:value={locale} />
|
||||
<EmailTemplate bind:loading>
|
||||
<Id value={'{{team}}'}>{'{{team}}'}</Id>
|
||||
@@ -42,4 +43,4 @@
|
||||
<Id value={'{{project}}'}>{'{{project}}'}</Id>
|
||||
<Id value={'{{redirect}}'}>{'{{redirect}}'}</Id>
|
||||
</EmailTemplate>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Id } from '$lib/components';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
|
||||
const projectId = $page.params.project;
|
||||
|
||||
@@ -36,11 +37,11 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="boxes-wrapper u-margin-block-start-16">
|
||||
<Layout.Stack>
|
||||
<LocaleOptions on:change={onLocaleChange} bind:value={locale} />
|
||||
<EmailTemplate bind:loading>
|
||||
<Id value={'{{user}}'}>{'{{user}}'}</Id>
|
||||
<Id value={'{{project}}'}>{'{{project}}'}</Id>
|
||||
<Id value={'{{redirect}}'}>{'{{redirect}}'}</Id>
|
||||
</EmailTemplate>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Id } from '$lib/components';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
|
||||
const projectId = $page.params.project;
|
||||
|
||||
@@ -35,11 +36,11 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="boxes-wrapper u-margin-block-start-16">
|
||||
<Layout.Stack>
|
||||
<LocaleOptions on:change={onLocaleChange} bind:value={locale} />
|
||||
<EmailTemplate bind:loading>
|
||||
<Id value={'{{user}}'}>{'{{user}}'}</Id>
|
||||
<Id value={'{{project}}'}>{'{{project}}'}</Id>
|
||||
<Id value={'{{redirect}}'}>{'{{redirect}}'}</Id>
|
||||
</EmailTemplate>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
|
||||
+3
-2
@@ -7,6 +7,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Id } from '$lib/components';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
|
||||
const projectId = $page.params.project;
|
||||
let locale = 'en';
|
||||
@@ -34,7 +35,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="boxes-wrapper u-margin-block-start-16">
|
||||
<Layout.Stack>
|
||||
<LocaleOptions on:change={onLocaleChange} bind:value={locale} />
|
||||
<EmailTemplate bind:loading>
|
||||
<Id value={'{{user}}'}>{'{{user}}'}</Id>
|
||||
@@ -43,4 +44,4 @@
|
||||
<Id value={'{{ipAddress'}>{'{{ipAddress}}'}</Id>
|
||||
<Id value={'{{country'}>{'{{country}}'}</Id>
|
||||
</EmailTemplate>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
|
||||
<Layout.Stack>
|
||||
<LocaleOptions on:change={onLocaleChange} bind:value={locale} />
|
||||
|
||||
<EmailTemplate bind:loading>
|
||||
<Id value={'{{user}}'}>{'{{user}}'}</Id>
|
||||
<Id value={'{{project}}'}>{'{{project}}'}</Id>
|
||||
|
||||
+3
-5
@@ -13,7 +13,7 @@
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { collection } from '../store';
|
||||
import { Alert } from '@appwrite.io/pink-svelte';
|
||||
import { Alert, Link } from '@appwrite.io/pink-svelte';
|
||||
|
||||
let showDelete = false;
|
||||
let permissions = $doc?.$permissions;
|
||||
@@ -71,12 +71,10 @@
|
||||
A user requires appropriate permissions at either the <b>collection level</b> or
|
||||
<b>document level</b> to access a document. If no permissions are configured, no user can
|
||||
access the document
|
||||
<a
|
||||
<Link.Anchor
|
||||
href="https://appwrite.io/docs/products/databases/permissions"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="link">Learn more about database permissions</a
|
||||
>.
|
||||
rel="noopener noreferrer">Learn more</Link.Anchor>
|
||||
<svelte:fragment slot="aside">
|
||||
{#if $collection.documentSecurity}
|
||||
{#if showPermissionAlert}
|
||||
|
||||
+36
-47
@@ -3,19 +3,11 @@
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { Alert, Modal, Trim } from '$lib/components';
|
||||
import { Modal, Trim } from '$lib/components';
|
||||
import { Button, InputChoice } from '$lib/elements/forms';
|
||||
import {
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableCellHead,
|
||||
TableCellText,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
TableScroll
|
||||
} from '$lib/elements/table';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { Alert, Selector, Table } from '@appwrite.io/pink-svelte';
|
||||
import { attributes, collection } from '../store';
|
||||
import { isRelationship } from './attributes/store';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
@@ -69,48 +61,45 @@
|
||||
|
||||
{#if relAttributes?.length}
|
||||
<p class="text">This document contains the following relationships:</p>
|
||||
<TableScroll noMargin>
|
||||
<TableHeader>
|
||||
<TableCellHead width={70}>Relation</TableCellHead>
|
||||
<TableCellHead width={70}>Setting</TableCellHead>
|
||||
<TableCellHead width={200} />
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each relAttributes as attr}
|
||||
<TableRow>
|
||||
<TableCell title="relation">
|
||||
<span class="u-flex u-cross-center u-gap-8">
|
||||
{#if attr.twoWay}
|
||||
<span class="icon-switch-horizontal" />
|
||||
{:else}
|
||||
<span class="icon-arrow-sm-right" />
|
||||
{/if}
|
||||
<Trim>{attr.key}</Trim>
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCellText title="Settings">
|
||||
{attr.onDelete}
|
||||
</TableCellText>
|
||||
<TableCellText title="description">
|
||||
{Deletion[attr.onDelete]}
|
||||
</TableCellText>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</TableScroll>
|
||||
<Table.Root>
|
||||
<svelte:fragment slot="header">
|
||||
<Table.Header.Cell width="70px">Relation</Table.Header.Cell>
|
||||
<Table.Header.Cell width="70px">Setting</Table.Header.Cell>
|
||||
<Table.Header.Cell />
|
||||
</svelte:fragment>
|
||||
{#each relAttributes as attr}
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<span class="u-flex u-cross-center u-gap-8">
|
||||
{#if attr.twoWay}
|
||||
<span class="icon-switch-horizontal" />
|
||||
{:else}
|
||||
<span class="icon-arrow-sm-right" />
|
||||
{/if}
|
||||
<Trim>{attr.key}</Trim>
|
||||
</span>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{attr.onDelete}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{Deletion[attr.onDelete]}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{/each}
|
||||
</Table.Root>
|
||||
<div class="u-flex u-flex-vertical u-gap-16">
|
||||
<Alert>To change the selection edit the relationship settings.</Alert>
|
||||
|
||||
<ul>
|
||||
<InputChoice id="delete" label="Delete" showLabel={false} bind:value={checked}>
|
||||
Delete document from <span data-private>{$collection.name}</span>
|
||||
</InputChoice>
|
||||
</ul>
|
||||
<Alert.Inline
|
||||
title="To change these deletion behaviors edit the relationship settings." />
|
||||
<Selector.Checkbox
|
||||
id="delete"
|
||||
label={`Delete document from ${$collection.name}`}
|
||||
bind:checked />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (showDelete = false)}>Cancel</Button>
|
||||
<Button secondary submit disabled={relAttributes?.length && !checked}>Delete</Button>
|
||||
<Button danger submit disabled={relAttributes?.length && !checked}>Delete</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
|
||||
@@ -19,9 +19,8 @@
|
||||
label: 'Create file',
|
||||
async callback() {
|
||||
if (!$page.url.pathname.endsWith($bucket.$id)) {
|
||||
goto(`${base}/project-${$project.$id}/storage/bucket-${$bucket.$id}`);
|
||||
goto(`${base}/project-${$project.$id}/storage/bucket-${$bucket.$id}/create`);
|
||||
}
|
||||
showCreateFile();
|
||||
},
|
||||
keys: $page.url.pathname.endsWith($bucket.$id) ? ['c'] : ['c', 'f'],
|
||||
group: 'files',
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { getServiceLimit } from '$lib/stores/billing';
|
||||
import { sdk } from '$lib/stores/sdk.js';
|
||||
import Create from './create-file/create.svelte';
|
||||
import DeleteFile from './deleteFile.svelte';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { Layout, Table, Icon } from '@appwrite.io/pink-svelte';
|
||||
@@ -56,7 +55,8 @@
|
||||
? bytesToSize(data.organizationUsage.storageTotal, 'GB')
|
||||
: null;
|
||||
const getPreview = (fileId: string) =>
|
||||
sdk.forProject.storage.getFilePreview(bucketId, fileId, 64, 64).toString() + '&mode=admin';
|
||||
sdk.forProject.storage.getFilePreview(bucketId, fileId, 128, 128).toString() +
|
||||
'&mode=admin';
|
||||
|
||||
async function fileDeleted(event: CustomEvent<Models.File>) {
|
||||
showDelete = false;
|
||||
@@ -113,7 +113,10 @@
|
||||
<SearchQuery search={data.search} placeholder="Search files" />
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<Button on:mousedown={() => wizard.start(Create)} event="create_file" size="s">
|
||||
<Button
|
||||
href={`${base}/project-${$page.params.project}/storage/bucket-${$page.params.bucket}/create`}
|
||||
event="create_file"
|
||||
size="s">
|
||||
<Icon icon={IconPlus} slot="start" size="s" />
|
||||
Create file
|
||||
</Button>
|
||||
@@ -166,7 +169,7 @@
|
||||
href={`${base}/project-${projectId}/storage/bucket-${bucketId}/file-${file.$id}`}>
|
||||
<Table.Cell>
|
||||
<div class="u-flex u-gap-12 u-cross-center">
|
||||
<Avatar size="s" src={getPreview(file.$id)} alt={file.name} />
|
||||
<Avatar size="xs" src={getPreview(file.$id)} alt={file.name} />
|
||||
<span class="text u-trim">{file.name}</span>
|
||||
</div>
|
||||
</Table.Cell>
|
||||
@@ -242,7 +245,7 @@
|
||||
single
|
||||
href="https://appwrite.io/docs/products/storage/upload-download"
|
||||
target="file"
|
||||
on:click={() => wizard.start(Create)} />
|
||||
on:click={() => void 0} />
|
||||
{/if}
|
||||
</Container>
|
||||
|
||||
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import Wizard from '$lib/layout/wizardWithSteps.svelte';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { uploader } from '$lib/stores/uploader';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { ID } from '@appwrite.io/console';
|
||||
import Step1 from './step1.svelte';
|
||||
import Step2 from './step2.svelte';
|
||||
import { createFile } from './store';
|
||||
|
||||
const bucketId = $page.params.bucket;
|
||||
|
||||
const stepComponents: WizardStepsType = new Map();
|
||||
|
||||
stepComponents.set(1, {
|
||||
component: Step1,
|
||||
label: 'Create'
|
||||
});
|
||||
|
||||
stepComponents.set(2, {
|
||||
component: Step2,
|
||||
label: 'Permissions'
|
||||
});
|
||||
|
||||
async function create() {
|
||||
const fileId = $createFile.id ?? ID.unique();
|
||||
|
||||
try {
|
||||
const uploadPromise = uploader.uploadFile(
|
||||
bucketId,
|
||||
fileId,
|
||||
$createFile.files[0],
|
||||
$createFile.permissions
|
||||
);
|
||||
|
||||
createFile.reset();
|
||||
wizard.hide();
|
||||
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `File upload in progress`
|
||||
});
|
||||
trackEvent(Submit.FileCreate, {
|
||||
customId: !!$createFile.id
|
||||
});
|
||||
|
||||
await uploadPromise;
|
||||
invalidate(Dependencies.FILES);
|
||||
} catch (e) {
|
||||
uploader.removeFromQueue(fileId);
|
||||
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: e.message
|
||||
});
|
||||
trackError(e, Submit.FileCreate);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Wizard title="Create file" steps={stepComponents} on:exit={createFile.reset} on:finish={create} />
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Alert, CustomId } from '$lib/components';
|
||||
import { page } from '$app/stores';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { FormList, InputFile } from '$lib/elements/forms';
|
||||
import { humanFileSize, sizeToBytes } from '$lib/helpers/sizeConvertion';
|
||||
import WizardStep from '$lib/layout/wizardStep.svelte';
|
||||
import { currentPlan } from '$lib/stores/organization';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { bucket } from '../store';
|
||||
import { createFile } from './store';
|
||||
import { base } from '$app/paths';
|
||||
|
||||
let showCustomId = false;
|
||||
let fileError: string = null;
|
||||
const service = $currentPlan['fileSize'];
|
||||
const projectId = $page.params.project;
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">File</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">Upload a file to add it to your bucket.</svelte:fragment>
|
||||
|
||||
{#if isCloud && fileError === 'File size exceeds the limit'}
|
||||
{@const size = humanFileSize($bucket.maximumFileSize ?? sizeToBytes(service, 'MB', 1000))}
|
||||
<Alert type="info">
|
||||
<p class="text">
|
||||
The maximum file upload size for this bucket is {parseInt(size.value)}{size.unit}.
|
||||
You can adjust it in your
|
||||
<a
|
||||
href={`${base}/project-${projectId}/storage/bucket-${$bucket.$id}/settings`}
|
||||
style:text-decoration="underline">bucket settings</a
|
||||
>.
|
||||
</p>
|
||||
</Alert>
|
||||
{/if}
|
||||
|
||||
<FormList>
|
||||
<div class="u-width-full-line u-overflow-hidden">
|
||||
<InputFile
|
||||
required
|
||||
bind:files={$createFile.files}
|
||||
allowedFileExtensions={$bucket.allowedFileExtensions}
|
||||
bind:error={fileError}
|
||||
maxSize={isCloud
|
||||
? $bucket.maximumFileSize
|
||||
: ($bucket.maximumFileSize ?? sizeToBytes(service, 'MB', 1000))} />
|
||||
</div>
|
||||
|
||||
{#if !showCustomId}
|
||||
<div>
|
||||
<Pill button on:click={() => (showCustomId = !showCustomId)}>
|
||||
<span class="icon-pencil" aria-hidden="true" /><span class="text">
|
||||
File ID
|
||||
</span>
|
||||
</Pill>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="custom-id-wrapper">
|
||||
<CustomId autofocus bind:show={showCustomId} name="File" bind:id={$createFile.id} />
|
||||
</div>
|
||||
{/if}
|
||||
</FormList>
|
||||
</WizardStep>
|
||||
|
||||
<style lang="scss">
|
||||
.custom-id-wrapper :global(.is-inner-modal) {
|
||||
inline-size: auto;
|
||||
}
|
||||
</style>
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
<script lang="ts">
|
||||
import Alert from '$lib/components/alert.svelte';
|
||||
import Permissions from '$lib/components/permissions/permissions.svelte';
|
||||
import WizardStep from '$lib/layout/wizardStep.svelte';
|
||||
import { bucket } from '../store';
|
||||
import { createFile } from './store';
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Permissions</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">
|
||||
Choose who can access your buckets and files. For more information, check out the
|
||||
<a
|
||||
href="https://appwrite.io/docs/advanced/platform/permissions"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="link">
|
||||
Permissions Guide
|
||||
</a>.</svelte:fragment>
|
||||
|
||||
{#if $bucket.fileSecurity}
|
||||
<div class="common-section">
|
||||
<Alert type="info">
|
||||
<svelte:fragment slot="title">File security enabled</svelte:fragment>
|
||||
Users will be able to access this file if they have been granted
|
||||
<b>either File or Bucket permissions</b>.
|
||||
</Alert>
|
||||
</div>
|
||||
<div class="common-section">
|
||||
<Permissions bind:permissions={$createFile.permissions} />
|
||||
</div>
|
||||
{:else}
|
||||
<Alert type="info">
|
||||
<svelte:fragment slot="title">File security disabled</svelte:fragment>
|
||||
If you want to assign file permissions, navigate to Bucket settings and enable file security.
|
||||
Otherwise, only Bucket permissions will be used.
|
||||
</Alert>
|
||||
{/if}
|
||||
</WizardStep>
|
||||
@@ -1,24 +0,0 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
type CreateFile = {
|
||||
files: FileList | null;
|
||||
id: string | null;
|
||||
permissions: string[];
|
||||
};
|
||||
|
||||
const initialState: CreateFile = {
|
||||
files: null,
|
||||
id: null,
|
||||
permissions: []
|
||||
};
|
||||
|
||||
export const createFile = (function initialize() {
|
||||
const store = writable<CreateFile>({ ...initialState });
|
||||
|
||||
return {
|
||||
...store,
|
||||
reset() {
|
||||
store.set({ ...initialState });
|
||||
}
|
||||
};
|
||||
})();
|
||||
@@ -0,0 +1,168 @@
|
||||
<script lang="ts">
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { Wizard } from '$lib/layout';
|
||||
import {
|
||||
Alert,
|
||||
Fieldset,
|
||||
Icon,
|
||||
Layout,
|
||||
Tag,
|
||||
Typography,
|
||||
Upload
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import Button from '$lib/elements/forms/button.svelte';
|
||||
import Form from '$lib/elements/forms/form.svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
import { Permissions } from '$lib/components/permissions';
|
||||
import type { PageData } from './$types';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { currentPlan } from '$lib/stores/organization';
|
||||
import { humanFileSize, sizeToBytes } from '$lib/helpers/sizeConvertion';
|
||||
import Pill from '$lib/elements/pill.svelte';
|
||||
import CustomId from '$lib/components/customId.svelte';
|
||||
import { IconPencil } from '@appwrite.io/pink-icons-svelte';
|
||||
import { ID } from '@appwrite.io/console';
|
||||
import { uploader } from '$lib/stores/uploader';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let showExitModal = false;
|
||||
let formComponent: Form;
|
||||
let isSubmitting = writable(false);
|
||||
let fileError: string = null;
|
||||
let showCustomId = false;
|
||||
const service = $currentPlan?.['fileSize'];
|
||||
let files: FileList | null = null;
|
||||
let id: string | null = null;
|
||||
let permissions: string[] = [];
|
||||
|
||||
async function create() {
|
||||
const fileId = id ?? ID.unique();
|
||||
|
||||
try {
|
||||
const promise = uploader.uploadFile($page.params.bucket, fileId, files[0], permissions);
|
||||
await goto(
|
||||
`${base}/project-${$page.params.project}/storage/bucket-${$page.params.bucket}`
|
||||
);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `File upload in progress`
|
||||
});
|
||||
trackEvent(Submit.FileCreate, {
|
||||
customId: !!id
|
||||
});
|
||||
await promise;
|
||||
invalidate(Dependencies.FILES);
|
||||
} catch (e) {
|
||||
uploader.removeFromQueue(fileId);
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: e.message
|
||||
});
|
||||
trackError(e, Submit.FileCreate);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Wizard
|
||||
title="Create file"
|
||||
href={`${base}/project-${$page.params.project}/storage/bucket-${$page.params.bucket}/`}
|
||||
bind:showExitModal
|
||||
column
|
||||
confirmExit>
|
||||
<Form bind:this={formComponent} onSubmit={create} bind:isSubmitting>
|
||||
<Layout.Stack gap="xxl">
|
||||
<Fieldset legend="Data">
|
||||
<Layout.Stack>
|
||||
{#if isCloud && fileError === 'File size exceeds the limit'}
|
||||
{@const size = humanFileSize(
|
||||
data.bucket.maximumFileSize ?? sizeToBytes(service, 'MB', 1000)
|
||||
)}
|
||||
<Alert.Inline status="info">
|
||||
The maximum file upload size for this bucket is {parseInt(
|
||||
size.value
|
||||
)}{size.unit}. You can adjust it in your
|
||||
<a
|
||||
href={`${base}/project-${$page.params.project}/storage/bucket-${$page.params.bucket}/settings`}
|
||||
style:text-decoration="underline">bucket settings</a
|
||||
>.
|
||||
</Alert.Inline>
|
||||
{/if}
|
||||
<Upload.Dropzone
|
||||
bind:files
|
||||
required
|
||||
extensions={data.bucket.allowedFileExtensions}>
|
||||
<Typography.Text variant="l-500"
|
||||
>Drag and drop files here or click to upload</Typography.Text>
|
||||
</Upload.Dropzone>
|
||||
{#if files}
|
||||
<Upload.List
|
||||
files={Array.from(files).map((b) => {
|
||||
return {
|
||||
name: b.name,
|
||||
size: b.size,
|
||||
extension: b.type,
|
||||
removable: true
|
||||
};
|
||||
})} />
|
||||
{/if}
|
||||
|
||||
{#if !showCustomId}
|
||||
<div>
|
||||
<Tag on:click={() => (showCustomId = !showCustomId)}>
|
||||
<Icon icon={IconPencil} slot="start" />
|
||||
<span class="text"> File ID </span>
|
||||
</Tag>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId autofocus bind:show={showCustomId} name="File" bind:id />
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Fieldset>
|
||||
|
||||
<Fieldset legend="Permissions">
|
||||
<Layout.Stack gap="xl">
|
||||
<Typography.Text>
|
||||
Choose who can access your buckets and files. For more information, check
|
||||
out the
|
||||
<a
|
||||
href="https://appwrite.io/docs/advanced/platform/permissions"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="link">
|
||||
Permissions Guide
|
||||
</a>.
|
||||
</Typography.Text>
|
||||
|
||||
{#if data.bucket.fileSecurity}
|
||||
<Alert.Inline status="info" title="File security enabled">
|
||||
Users will be able to access this file if they have been granted
|
||||
<b>either File or Bucket permissions</b>.
|
||||
</Alert.Inline>
|
||||
<Permissions bind:permissions />
|
||||
{:else}
|
||||
<Alert.Inline status="info" title="File security disabled">
|
||||
If you want to assign file permissions, navigate to Bucket settings and
|
||||
enable file security. Otherwise, only Bucket permissions will be used.
|
||||
</Alert.Inline>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Fieldset>
|
||||
</Layout.Stack>
|
||||
</Form>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button fullWidthMobile secondary on:click={() => (showExitModal = true)}>Cancel</Button>
|
||||
<Button
|
||||
fullWidthMobile
|
||||
on:click={() => formComponent.triggerSubmit()}
|
||||
disabled={$isSubmitting}>
|
||||
Create
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</Wizard>
|
||||
@@ -6,6 +6,8 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { ID } from '@appwrite.io/console';
|
||||
import { IconPencil } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Icon, Tag } from '@appwrite.io/pink-svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let showCreate = false;
|
||||
@@ -46,26 +48,25 @@
|
||||
</script>
|
||||
|
||||
<Modal title="Create bucket" {error} onSubmit={create} bind:show={showCreate}>
|
||||
<FormList>
|
||||
<InputText
|
||||
id="name"
|
||||
label="Name"
|
||||
placeholder="New bucket"
|
||||
bind:value={name}
|
||||
autofocus
|
||||
required />
|
||||
<InputText
|
||||
id="name"
|
||||
label="Name"
|
||||
placeholder="New bucket"
|
||||
bind:value={name}
|
||||
autofocus
|
||||
required />
|
||||
|
||||
{#if !showCustomId}
|
||||
<div>
|
||||
<Pill button on:click={() => (showCustomId = !showCustomId)}>
|
||||
<span class="icon-pencil" aria-hidden="true" />
|
||||
<span class="text"> Bucket ID </span>
|
||||
</Pill>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId autofocus bind:show={showCustomId} name="Bucket" bind:id />
|
||||
{/if}
|
||||
</FormList>
|
||||
{#if !showCustomId}
|
||||
<div>
|
||||
<Tag
|
||||
size="s"
|
||||
on:click={() => {
|
||||
showCustomId = true;
|
||||
}}><Icon icon={IconPencil} /> Bucket ID</Tag>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId autofocus bind:show={showCustomId} name="Bucket" bind:id />
|
||||
{/if}
|
||||
<svelte:fragment slot="footer">
|
||||
<Button secondary on:click={() => (showCreate = false)}>Cancel</Button>
|
||||
<Button submit>Create</Button>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
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 { Badge, Tooltip } from '@appwrite.io/pink-svelte';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
@@ -25,35 +25,24 @@
|
||||
<svelte:fragment slot="title">{bucket.name}</svelte:fragment>
|
||||
<svelte:fragment slot="status">
|
||||
{#if !bucket.enabled}
|
||||
<Pill>Disabled</Pill>
|
||||
<div>
|
||||
<Badge size="s" variant="secondary" content="Disabled" />
|
||||
</div>
|
||||
{/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>
|
||||
<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>
|
||||
</svelte:fragment>
|
||||
</GridItem1>
|
||||
{/each}
|
||||
|
||||
Reference in New Issue
Block a user