Merge branch 'main' of github.com:appwrite/console into feat-databases-v2

This commit is contained in:
Arman
2023-03-23 15:38:46 +01:00
20 changed files with 196 additions and 113 deletions
+9
View File
@@ -16,6 +16,7 @@
"@sentry/svelte": "^7.44.2",
"@sentry/tracing": "^7.44.2",
"analytics": "^0.8.1",
"dotenv": "^16.0.3",
"echarts": "^5.4.1",
"logrocket": "^3.0.1",
"pretty-bytes": "^6.1.0",
@@ -3659,6 +3660,14 @@
"node": ">=12"
}
},
"node_modules/dotenv": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
"integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
"engines": {
"node": ">=12"
}
},
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+1
View File
@@ -27,6 +27,7 @@
"@sentry/svelte": "^7.44.2",
"@sentry/tracing": "^7.44.2",
"analytics": "^0.8.1",
"dotenv": "^16.0.3",
"echarts": "^5.4.1",
"logrocket": "^3.0.1",
"pretty-bytes": "^6.1.0",
+2
View File
@@ -33,6 +33,8 @@
</script>
<span
style:display="inline-block"
style:cursor="pointer"
on:click|preventDefault={handleClick}
on:keyup={clickOnEnter}
on:mouseenter={() => setTimeout(() => (content = 'Click to copy'))}
+9 -7
View File
@@ -2,16 +2,18 @@
import Copy from './copy.svelte';
export let value: string;
export let hideCopyIcon = false;
</script>
<Copy {value}>
<div class="interactive-text-output is-textarea">
<div class="interactive-text-output is-textarea" style:min-inline-size="0">
<span class="text u-line-height-1-5 u-break-all"><slot /></span>
<div class="u-flex u-cross-child-start u-gap-8">
<button class="interactive-text-output-button" aria-label="copy text">
<span class="icon-duplicate" aria-hidden="true" />
</button>
</div>
{#if !hideCopyIcon}
<div class="u-flex u-cross-child-start u-gap-8">
<button class="interactive-text-output-button" aria-label="copy text">
<span class="icon-duplicate" aria-hidden="true" />
</button>
</div>
{/if}
</div>
</Copy>
+1 -1
View File
@@ -104,7 +104,7 @@
<Row {role} />
</TableCell>
<TableCellText title="Remove">
<div class="u-flex">
<div class="u-flex u-main-end">
<button
class="button is-text is-only-icon"
type="button"
+2 -5
View File
@@ -1,6 +1,7 @@
<script lang="ts">
import { onMount } from 'svelte';
import { FormItem } from '.';
import TextCounter from './textCounter.svelte';
export let value = '';
export let autofocus = true;
@@ -42,11 +43,7 @@
bind:value
bind:this={element}
on:invalid={handleInvalid} />
<span class="text-counter">
<span class="text-counter-count">{value?.length ?? 0}</span>
<span class="text-counter-separator" />
<span class="text-counter-max">36</span>
</span>
<TextCounter count={value?.length ?? 0} max={36} />
</div>
</FormItem>
<div
+3 -5
View File
@@ -1,6 +1,7 @@
<script lang="ts">
import { onMount } from 'svelte';
import { FormItem, Helper, Label } from '.';
import TextCounter from './textCounter.svelte';
export let label: string;
export let optionalText: string | undefined = undefined;
@@ -58,14 +59,11 @@
type="text"
class="input-text"
bind:value
class:u-padding-inline-end-56={typeof maxlength === 'number'}
bind:this={element}
on:invalid={handleInvalid} />
{#if maxlength}
<span class="text-counter">
<span class="text-counter-count">{value?.length ?? 0}</span>
<span class="text-counter-separator" />
<span class="text-counter-max">{maxlength}</span>
</span>
<TextCounter max={maxlength} count={value?.length ?? 0} />
{/if}
</div>
{#if error}
+5 -6
View File
@@ -1,6 +1,7 @@
<script lang="ts">
import { onMount } from 'svelte';
import { FormItem, Helper, Label } from '.';
import TextCounter from './textCounter.svelte';
export let label: string;
export let showLabel = true;
@@ -12,6 +13,8 @@
export let readonly = false;
export let autofocus = false;
export let maxlength: number = null;
export let optionalText: string | undefined = undefined;
export let tooltip: string = null;
let element: HTMLTextAreaElement;
let error: string;
@@ -37,7 +40,7 @@
</script>
<FormItem>
<Label {required} hide={!showLabel} for={id}>
<Label {required} {tooltip} {optionalText} hide={!showLabel} for={id}>
{label}
</Label>
@@ -54,11 +57,7 @@
bind:this={element}
on:invalid={handleInvalid} />
{#if maxlength}
<span class="text-counter">
<span class="text-counter-count">{value?.length ?? 0}</span>
<span class="text-counter-separator" />
<span class="text-counter-max">{maxlength}</span>
</span>
<TextCounter max={maxlength} count={value?.length ?? 0} />
{/if}
</div>
+28
View File
@@ -0,0 +1,28 @@
<script lang="ts">
export let count: number;
export let max: number;
</script>
<span class="text-counter" class:active={count > 0}>
<span class="text-counter-count">{count}</span>
<span class="text-counter-separator" />
<span class="text-counter-max">{max}</span>
</span>
<style lang="scss">
.text-counter {
--p-text-counter-color: var(--color-neutral-50);
&.active {
--p-text-counter-color: var(--color-neutral-70);
}
}
:global(.theme-dark) .text-counter {
--p-text-counter-color: var(--color-neutral-70);
&.active {
--p-text-counter-color: var(--color-neutral-50);
}
}
</style>
+3 -3
View File
@@ -11,13 +11,13 @@
<ul class="inline-links is-no-padding-first-and-last u-x-small">
<li class="inline-links-item">
<div class="u-flex u-cross-center u-gap-8">
{#if MODE === Mode.CLOUD}
{#if MODE !== Mode.CLOUD}
<span class="icon-cloud" />
{/if}
{#if $version}
<a
class="text"
href="https://github.com/appwrite/appwrite/blob/master/CHANGES.md"
href="https://github.com/appwrite/appwrite/releases"
target="_blank"
rel="noreferrer">
Version {$version}
@@ -62,7 +62,7 @@
margin-block-start: auto;
}
[class^='icon-']:not(:hover) {
[class^='icon-']:not(.icon-cloud):not(:hover) {
color: hsl(var(--color-neutral-50));
}
</style>
+5 -3
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { fly } from 'svelte/transition';
import type { Notification } from '../stores/notifications';
export let type: Notification['type'] = 'info';
@@ -9,12 +10,13 @@
const dispatch = createEventDispatcher();
</script>
<li
<div
class="alert-sticky"
class:is-success={type === 'success'}
class:is-warning={type === 'warning'}
class:is-danger={type === 'error'}
class:is-info={type === 'info'}>
class:is-info={type === 'info'}
transition:fly={{ x: 50 }}>
<button
class="button is-text is-only-icon"
style="--button-size:1.5rem;"
@@ -45,4 +47,4 @@
{/each}
</div>
{/if}
</li>
</div>
+19 -9
View File
@@ -1,19 +1,22 @@
<script lang="ts">
import Notification from './notification.svelte';
import { dismissNotification, notifications } from '../stores/notifications';
import { flip } from 'svelte/animate';
</script>
{#if $notifications}
<section>
<ul class="u-flex u-flex-vertical u-gap-16">
{#each $notifications as notification (notification.id)}
<Notification
type={notification.type}
title={notification.title}
on:dismiss={() => dismissNotification(notification.id)}
buttons={notification?.buttons}>
{notification.message}
</Notification>
<li animate:flip={{ duration: 500 }}>
<Notification
type={notification.type}
title={notification.title}
on:dismiss={() => dismissNotification(notification.id)}
buttons={notification?.buttons}>
{notification.message}
</Notification>
</li>
{/each}
</ul>
</section>
@@ -22,8 +25,15 @@
<style lang="scss">
section {
position: fixed;
top: calc(var(--main-header-height) + 24px);
right: 24px;
top: calc(var(--main-header-height) + 12px);
right: 12px;
z-index: 1000;
}
@media (min-width: 768px) {
section {
top: calc(var(--main-header-height) + 24px);
right: 24px;
}
}
</style>
+1 -1
View File
@@ -74,7 +74,7 @@ function createFeedbackStore() {
customFields: value ? [{ id: '40655', value }] : undefined
})
});
if (response.status !== 200) {
if (response.status >= 400) {
throw new Error('Failed to submit feedback');
}
}
+1 -5
View File
@@ -33,11 +33,7 @@ export const addNotification = (notification: Omit<Notification, 'id'>) => {
const n = { ...defaults, ...notification };
notifications.update((all) => {
if (all.length < 3) {
return [n, ...all];
} else {
return [n, ...all.slice(0, 2)];
}
return [n, ...all.slice(0, 4)];
});
if (n.timeout) setTimeout(() => dismissNotification(n.id), n.timeout);
@@ -120,9 +120,9 @@
href={`${base}/console/project-${projectId}/databases/database-${databaseId}/collection-${$collection.$id}/document-${document.$id}`}>
<TableCell width={230}>
<Copy value={document.$id}>
<Pill button>
<Pill button trim>
<span class="icon-duplicate" aria-hidden="true" />
<span class="text u-trim-start">{document.$id}</span>
<span class="text">{document.$id}</span>
</Pill>
</Copy>
</TableCell>
@@ -37,6 +37,7 @@
<script lang="ts">
import { InputNumber, InputText, InputChoice } from '$lib/elements/forms';
import InputTextarea from '$lib/elements/forms/inputTextarea.svelte';
export let data: Partial<Models.AttributeString> = {
required: false,
@@ -52,12 +53,21 @@
</script>
<InputNumber id="size" label="Size" bind:value={data.size} required readonly={editing} />
<InputText
id="default"
label="Default value"
bind:value={data.default}
maxlength={data.size}
disabled={data.required || data.array} />
{#if data.size > 50}
<InputTextarea
id="default"
label="Default value"
bind:value={data.default}
maxlength={data.size}
disabled={data.required || data.array} />
{:else}
<InputText
id="default"
label="Default value"
bind:value={data.default}
maxlength={data.size}
disabled={data.required || data.array} />
{/if}
<InputChoice id="required" label="Required" bind:value={data.required} disabled={data.array}>
Indicate whether this is a required attribute
</InputChoice>
@@ -133,6 +133,7 @@
<div class="form-item-part u-cross-child-end">
<Button
noMargin
text
disabled={attributeList.length <= 1}
on:click={() => {
@@ -3,7 +3,16 @@
import { base } from '$app/paths';
import { page } from '$app/stores';
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
import { Box, CardGrid, DropList, DropListItem, Empty, Output, Secret } from '$lib/components';
import {
Box,
CardGrid,
DropList,
DropListItem,
Empty,
Output,
PaginationInline,
Secret
} from '$lib/components';
import Heading from '$lib/components/heading.svelte';
import { Roles } from '$lib/components/permissions';
import { Dependencies } from '$lib/constants';
@@ -18,7 +27,7 @@
import { onMount } from 'svelte';
import Variable from '../../createVariable.svelte';
import { execute, func } from '../store';
// import Upload from './uploadVariables.svelte';
import UploadVariables from './uploadVariables.svelte';
import {
Table,
TableBody,
@@ -36,7 +45,7 @@
const functionId = $page.params.function;
let showDelete = false;
let selectedVar: Models.Variable = null;
// let showVariablesUpload = false;
let showVariablesUpload = false;
let showVariablesModal = false;
let showVariablesDropdown = [];
let timeout: number = null;
@@ -44,6 +53,7 @@
let functionSchedule: string = null;
let permissions: string[] = [];
let arePermsDisabled = true;
let offset = 0;
onMount(async () => {
timeout ??= $func.timeout;
@@ -369,24 +379,26 @@
<span class="icon-download" />
<span class="text">Download .env file</span>
</Button>
<!-- <Button secondary on:click={() => (showVariablesUpload = true)}>
<Button secondary on:click={() => (showVariablesUpload = true)}>
<span class="icon-upload" />
<span class="text">Import .env file</span>
</Button> -->
</Button>
</div>
{#if data.variables.total}
{@const limit = 10}
{@const sum = data.variables.total}
{#if sum}
<div class="u-flex u-flex-vertical u-gap-16">
<Table noMargin noStyles>
<TableHeader>
<TableCellHead>Key</TableCellHead>
<TableCellHead>Value</TableCellHead>
<TableCellHead width={180}>Value</TableCellHead>
<TableCellHead width={30} />
</TableHeader>
<TableBody>
{#each data.variables.variables as variable, i}
{#each data.variables.variables.slice(offset, offset + limit) as variable, i}
<TableRow>
<TableCell title="key">
<Output value={variable.key}>
<Output value={variable.key} hideCopyIcon>
{variable.key}
</Output>
</TableCell>
@@ -435,19 +447,19 @@
{/each}
</TableBody>
</Table>
<Button
text
noMargin
on:click={() => {
showVariablesModal = true;
}}>
<Button text noMargin on:click={() => (showVariablesModal = true)}>
<span class="icon-plus" aria-hidden="true" />
<span class="text">Create variable</span>
</Button>
<div class="u-flex u-main-space-between">
<p class="text">Total variables: {sum}</p>
<PaginationInline {sum} {limit} bind:offset hidePages />
</div>
</div>
{:else}
<Empty on:click={() => (showVariablesModal = !showVariablesModal)}
>Create a variable to get started</Empty>
<Empty on:click={() => (showVariablesModal = !showVariablesModal)}>
Create a variable to get started
</Empty>
{/if}
</svelte:fragment>
</CardGrid>
@@ -505,3 +517,6 @@
on:created={handleVariableCreated}
on:updated={handleVariableUpdated} />
{/if}
{#if showVariablesUpload}
<UploadVariables bind:show={showVariablesUpload} />
{/if}
@@ -0,0 +1,8 @@
import { page } from '$app/stores';
import { derived } from 'svelte/store';
import type { Models } from '@aw-labs/appwrite-console';
export const variables = derived(
page,
($page) => $page.data.variables.variables as Models.Variable[]
);
@@ -6,69 +6,74 @@
import { Button, InputFile } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { createEventDispatcher } from 'svelte';
import { parse } from 'dotenv';
import { variables } from './store';
export let show = false;
const dispatch = createEventDispatcher();
const functionId = $page.params.function;
let files: FileList;
const handleSubmit = async () => {
if (files?.length) {
const variables = await parseFile(files[0]);
for (const variable of variables) {
try {
await sdk.forProject.functions.createVariable(
functionId,
variable.key,
variable.value
);
invalidate(Dependencies.VARIABLES);
addNotification({
type: 'success',
message: 'Variable uploaded'
});
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
let files: FileList;
let error: string;
async function handleSubmit() {
try {
if (!files?.length) {
throw new Error('No file selected');
}
const uploaded = parse(await files[0].text());
if (!Object.keys(uploaded).length) {
throw new Error('No variables found');
}
const entries = Object.entries(uploaded);
for (const [key, value] of entries) {
if (value.length > 8192) {
throw new Error(`Variable ${key} is longer than 8192 allowed characters`);
}
}
dispatch('uploaded', variables);
} else {
addNotification({
type: 'error',
message: 'No file uploaded'
});
}
};
async function parseFile(file: File) {
if (file) {
let variables = [];
let text = await file.text();
text.split('\n').forEach((line) => {
const [key, value] = line.split('=');
variables.push({ key, value });
await Promise.all(
entries
.filter(([, value]) => !!value)
.map(([key, value]) => {
const found = $variables.find((variable) => variable.key === key);
return found
? sdk.forProject.functions.updateVariable(
functionId,
found.$id,
key,
value
)
: sdk.forProject.functions.createVariable(functionId, key, value);
})
);
invalidate(Dependencies.VARIABLES);
addNotification({
type: 'success',
message: 'Variables uploaded'
});
return variables;
show = false;
} catch (e) {
error = e.message;
}
}
</script>
<Modal bind:show onSubmit={handleSubmit}>
<Modal bind:show onSubmit={handleSubmit} bind:error>
<svelte:fragment slot="header">Upload Variables</svelte:fragment>
<p>
Upload multiple variables via a .env file that will be passed to your function at runtime.
</p>
<InputFile bind:files label="Attach a file" />
<InputFile bind:files />
<svelte:fragment slot="footer">
<Button text on:click={() => (show = false)}>Cancel</Button>
<Button submit>Create</Button>
<Button submit>Upload</Button>
</svelte:fragment>
</Modal>