mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
feat: variable editor
This commit is contained in:
+1
-1
@@ -23,7 +23,7 @@
|
||||
"@appwrite.io/pink-icons": "0.25.0",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@5312d5d",
|
||||
"@appwrite.io/pink-legacy": "^1.0.0",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@5312d5d",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@a8068b7",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@sentry/sveltekit": "^8.38.0",
|
||||
"@stripe/stripe-js": "^3.5.0",
|
||||
|
||||
Generated
+9
-9
@@ -21,8 +21,8 @@ importers:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
'@appwrite.io/pink-svelte':
|
||||
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@5312d5d
|
||||
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@5312d5d(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)
|
||||
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@a8068b7
|
||||
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@a8068b7(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)
|
||||
'@popperjs/core':
|
||||
specifier: ^2.11.8
|
||||
version: 2.11.8
|
||||
@@ -227,8 +227,8 @@ packages:
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@5312d5d32ffe17459f863a0e37d55e097ccf1cfb':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@5312d5d32ffe17459f863a0e37d55e097ccf1cfb}
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@a8068b75f1efdadfcb9b56773fb3b5c02629452b':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@a8068b75f1efdadfcb9b56773fb3b5c02629452b}
|
||||
version: 1.0.0-next.7
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
@@ -242,8 +242,8 @@ packages:
|
||||
'@appwrite.io/pink-legacy@1.0.0':
|
||||
resolution: {integrity: sha512-EZk/wX8oDhIsCXf2A0oQjdNOaHvh5MnWbLQI4pAiGrdy5qQGPoU19AK6iVX6z3QoOq7geoEu0zVwAowBy5hZbQ==}
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@5312d5d':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@5312d5d}
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@a8068b7':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@a8068b7}
|
||||
version: 1.0.0-next.85
|
||||
peerDependencies:
|
||||
react-dom: ^18.0.0
|
||||
@@ -4096,7 +4096,7 @@ snapshots:
|
||||
dependencies:
|
||||
svelte: 4.2.19
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@5312d5d32ffe17459f863a0e37d55e097ccf1cfb(svelte@4.2.19)':
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@a8068b75f1efdadfcb9b56773fb3b5c02629452b(svelte@4.2.19)':
|
||||
dependencies:
|
||||
svelte: 4.2.19
|
||||
|
||||
@@ -4109,9 +4109,9 @@ snapshots:
|
||||
'@appwrite.io/pink-icons': 1.0.0
|
||||
the-new-css-reset: 1.11.3
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@5312d5d(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)':
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@a8068b7(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)':
|
||||
dependencies:
|
||||
'@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@5312d5d32ffe17459f863a0e37d55e097ccf1cfb(svelte@4.2.19)
|
||||
'@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@a8068b75f1efdadfcb9b56773fb3b5c02629452b(svelte@4.2.19)
|
||||
'@floating-ui/dom': 1.6.13
|
||||
'@melt-ui/pp': 0.3.2(@melt-ui/svelte@0.86.2(svelte@4.2.19))(svelte@4.2.19)
|
||||
'@melt-ui/svelte': 0.86.2(svelte@4.2.19)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
};
|
||||
export let title = '';
|
||||
export let hideFooter = false;
|
||||
export let submitOnEnter = true;
|
||||
|
||||
let alert: HTMLElement;
|
||||
let formComponent: Form;
|
||||
@@ -24,7 +25,7 @@
|
||||
});
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if (event.key === 'Enter') {
|
||||
if (event.key === 'Enter' && submitOnEnter) {
|
||||
event.preventDefault();
|
||||
if (show) {
|
||||
formComponent.triggerSubmit();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let label: string;
|
||||
export let label = '';
|
||||
export let id: string;
|
||||
export let name: string = id;
|
||||
export let value = '';
|
||||
@@ -12,6 +12,7 @@
|
||||
export let readonly = false;
|
||||
export let autofocus = false;
|
||||
export let maxlength: number = null;
|
||||
export let rows = 3;
|
||||
|
||||
let error: string;
|
||||
|
||||
@@ -39,6 +40,7 @@
|
||||
{maxlength}
|
||||
{label}
|
||||
{nullable}
|
||||
{rows}
|
||||
autofocus={autofocus || undefined}
|
||||
helper={error}
|
||||
state={error ? 'error' : 'default'}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Button, Form } from '$lib/elements/forms';
|
||||
import { Button, Form, Label } from '$lib/elements/forms';
|
||||
import { Wizard } from '$lib/layout';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
@@ -129,26 +129,29 @@
|
||||
confirmExit>
|
||||
<Form bind:this={formComponent} onSubmit={create} bind:isSubmitting>
|
||||
<Layout.Stack gap="xl">
|
||||
<Upload.Dropzone folder bind:files on:change={handleChange}>
|
||||
<Layout.Stack alignItems="center" gap="s">
|
||||
<Layout.Stack
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
direction="row"
|
||||
gap="s">
|
||||
<Typography.Text variant="l-500">
|
||||
Drag and drop files here or click to upload
|
||||
</Typography.Text>
|
||||
<Tooltip>
|
||||
<Icon icon={IconInfo} size="s" />
|
||||
<svelte:fragment slot="tooltip">
|
||||
Only PNG, JPEG, PDF files allowed
|
||||
</svelte:fragment>
|
||||
</Tooltip>
|
||||
<Layout.Stack gap="xxs">
|
||||
<Label>Files or folder</Label>
|
||||
<Upload.Dropzone folder bind:files on:change={handleChange}>
|
||||
<Layout.Stack alignItems="center" gap="s">
|
||||
<Layout.Stack
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
direction="row"
|
||||
gap="s">
|
||||
<Typography.Text variant="l-500">
|
||||
Drag and drop files here or click to upload
|
||||
</Typography.Text>
|
||||
<Tooltip>
|
||||
<Icon icon={IconInfo} size="s" />
|
||||
<svelte:fragment slot="tooltip">
|
||||
Only PNG, JPEG, PDF files allowed
|
||||
</svelte:fragment>
|
||||
</Tooltip>
|
||||
</Layout.Stack>
|
||||
<Typography.Caption variant="400">Max file size: 10MB</Typography.Caption>
|
||||
</Layout.Stack>
|
||||
<Typography.Caption variant="400">Max file size: 10MB</Typography.Caption>
|
||||
</Layout.Stack>
|
||||
</Upload.Dropzone>
|
||||
</Upload.Dropzone>
|
||||
</Layout.Stack>
|
||||
<Details bind:name bind:id />
|
||||
|
||||
<Configuration
|
||||
|
||||
@@ -1,7 +1,36 @@
|
||||
<script lang="ts">
|
||||
import { Card } from '$lib/components';
|
||||
import { Button, InputSelect } from '$lib/elements/forms';
|
||||
import { IconDownload } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Icon, Image, Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
|
||||
let framework: 'react' | 'vue' | 'angular' | 'svelte' | 'web' = 'web';
|
||||
</script>
|
||||
|
||||
<Card padding="xs">
|
||||
<slot />
|
||||
<Layout.Stack gap="xxs">
|
||||
<Typography.Text variant="m-500">Don't have a site yet?</Typography.Text>
|
||||
<Typography.Text variant="m-400">
|
||||
Download our starter kit, and deploy in seconds.
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
|
||||
<Layout.Stack>
|
||||
<InputSelect
|
||||
id="framework"
|
||||
options={[
|
||||
{ label: 'React', value: 'react' },
|
||||
{ label: 'Vue', value: 'vue' },
|
||||
{ label: 'Angular', value: 'angular' },
|
||||
{ label: 'Svelte', value: 'svelte' },
|
||||
{ label: 'Web', value: 'web' }
|
||||
]}
|
||||
bind:value={framework} />
|
||||
|
||||
<Image src="https://placehold.co/349x201/111/bbb?text=Preview&font=inter" alt="preview" />
|
||||
|
||||
<Button size="s" fullWidth secondary href="#">
|
||||
<Icon icon={IconDownload} /> Starter kit
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
</Card>
|
||||
|
||||
+2
-7
@@ -24,6 +24,7 @@
|
||||
import SecretVariableModal from '../secretVariableModal.svelte';
|
||||
import ImportSiteVariablesModal from '../importSiteVariablesModal.svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import VariableEditorModal from '../variableEditorModal.svelte';
|
||||
|
||||
export let frameworks: Models.Framework[];
|
||||
export let selectedFramework: Models.Framework;
|
||||
@@ -206,13 +207,7 @@
|
||||
</Fieldset>
|
||||
|
||||
{#if showEditorModal}
|
||||
<!-- <RawVariableEditor
|
||||
{isGlobal}
|
||||
{sdkCreateVariable}
|
||||
{sdkUpdateVariable}
|
||||
{sdkDeleteVariable}
|
||||
{variableList}
|
||||
bind:showEditor={showEditorModal} /> -->
|
||||
<VariableEditorModal bind:variables bind:showEditor={showEditorModal} />
|
||||
{/if}
|
||||
|
||||
{#if showSecretModal}
|
||||
|
||||
+2
-7
@@ -26,6 +26,7 @@
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { getFrameworkIcon } from '../../../store';
|
||||
import { iconPath } from '$lib/stores/app';
|
||||
import VariableEditorModal from '../../variableEditorModal.svelte';
|
||||
|
||||
export let frameworks: Models.Framework[];
|
||||
export let selectedFramework: Models.Framework;
|
||||
@@ -212,13 +213,7 @@
|
||||
</Fieldset>
|
||||
|
||||
{#if showEditorModal}
|
||||
<!-- <RawVariableEditor
|
||||
{isGlobal}
|
||||
{sdkCreateVariable}
|
||||
{sdkUpdateVariable}
|
||||
{sdkDeleteVariable}
|
||||
{variableList}
|
||||
bind:showEditor={showEditorModal} /> -->
|
||||
<VariableEditorModal bind:variables bind:showEditor={showEditorModal} />
|
||||
{/if}
|
||||
|
||||
{#if showSecretModal}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
<script lang="ts">
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import Copy from '$lib/components/copy.svelte';
|
||||
import Modal from '$lib/components/modal.svelte';
|
||||
import Button from '$lib/elements/forms/button.svelte';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { parse } from 'envfile';
|
||||
import { Icon, Layout, Tabs } from '@appwrite.io/pink-svelte';
|
||||
import { IconDownload, IconDuplicate } from '@appwrite.io/pink-icons-svelte';
|
||||
import { InputTextarea } from '$lib/elements/forms';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
export let showEditor = false;
|
||||
export let variables: Partial<Models.Variable>[];
|
||||
|
||||
let error = '';
|
||||
let envCode = variables.map((variable) => `${variable.key}=${variable.value}`).join('\n');
|
||||
let jsonCode = JSON.stringify(
|
||||
JSON.parse(
|
||||
`{${variables
|
||||
.map((variable) => `"${variable.key}":"${variable.value.split('"').join('\\"')}"`)
|
||||
.join(',')}}`
|
||||
),
|
||||
null,
|
||||
2
|
||||
);
|
||||
const baseEnvCode = envCode;
|
||||
const baseJsonCode = jsonCode;
|
||||
|
||||
if (jsonCode === '{}') {
|
||||
jsonCode = '';
|
||||
}
|
||||
|
||||
let tab: 'env' | 'json' = 'env';
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const vars = tab === 'env' ? parse(envCode) : JSON.parse(jsonCode ? jsonCode : '{}');
|
||||
|
||||
const entries = Object.entries(vars);
|
||||
|
||||
for (const [key, value] of entries) {
|
||||
if (('' + value).length > 8192) {
|
||||
throw new Error(`Variable ${key} is longer than 8192 allowed characters`);
|
||||
}
|
||||
}
|
||||
|
||||
variables.map((variable) => {
|
||||
const newValue = vars[variable.key] ?? null;
|
||||
|
||||
if (newValue === null) {
|
||||
variables = variables.filter((v) => v.key !== variable.key);
|
||||
} else if (newValue !== variable.value) {
|
||||
variable.value = newValue;
|
||||
variables = [...variables];
|
||||
}
|
||||
delete vars[variable.key];
|
||||
});
|
||||
|
||||
Object.keys(vars).map(async (key) => {
|
||||
const value = vars[key];
|
||||
variables.push({ key, value });
|
||||
});
|
||||
|
||||
showEditor = false;
|
||||
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `Variables have been updated.`
|
||||
});
|
||||
|
||||
trackEvent(Submit.VariableEditor);
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
trackError(e, Submit.VariableEditor);
|
||||
}
|
||||
}
|
||||
|
||||
function downloadVariables() {
|
||||
const content = tab === 'json' ? jsonCode : envCode;
|
||||
const fileName = tab === 'json' ? 'vars.json' : '.env';
|
||||
const type = tab === 'json' ? 'application/json' : 'application/x-envoy';
|
||||
|
||||
const file = new File([content], fileName, {
|
||||
type
|
||||
});
|
||||
|
||||
const link = document.createElement('a');
|
||||
const url = URL.createObjectURL(file);
|
||||
|
||||
link.href = url;
|
||||
link.download = file.name;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if (event.key === 'Enter') {
|
||||
if (tab === 'env') {
|
||||
envCode += '\n';
|
||||
} else {
|
||||
jsonCode += '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: isButtonDisabled =
|
||||
(tab === 'env' && baseEnvCode === envCode) || (tab === 'json' && baseJsonCode === jsonCode);
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={handleKeydown} />
|
||||
|
||||
<Modal
|
||||
title="Editor"
|
||||
bind:show={showEditor}
|
||||
onSubmit={handleSubmit}
|
||||
bind:error
|
||||
submitOnEnter={false}>
|
||||
<p slot="description">Add, edit, or delete environment variables using a raw editor.</p>
|
||||
<Layout.Stack gap="s">
|
||||
<Tabs.Root>
|
||||
<Tabs.Item.Button on:click={() => (tab = 'env')} active={tab === 'env'}>
|
||||
ENV
|
||||
</Tabs.Item.Button>
|
||||
<Tabs.Item.Button on:click={() => (tab = 'json')} active={tab === 'json'}>
|
||||
JSON
|
||||
</Tabs.Item.Button>
|
||||
</Tabs.Root>
|
||||
|
||||
{#if tab === 'env'}
|
||||
<InputTextarea
|
||||
id="variables"
|
||||
bind:value={envCode}
|
||||
rows={10}
|
||||
placeholder={`SECRET_KEY=dQw4w9WgXcQ...`} />
|
||||
{:else if tab === 'json'}
|
||||
<InputTextarea
|
||||
id="variables"
|
||||
bind:value={jsonCode}
|
||||
rows={10}
|
||||
placeholder={`{\n "SECRET_KEY": "dQw4w9WgXcQ..."\n}`} />
|
||||
{/if}
|
||||
<Layout.Stack direction="row" gap="xs">
|
||||
<Button on:click={() => downloadVariables()} text>
|
||||
<Icon icon={IconDownload} />
|
||||
Download
|
||||
</Button>
|
||||
|
||||
<Copy value={tab == 'json' ? jsonCode : envCode}>
|
||||
<Button text>
|
||||
<Icon icon={IconDuplicate} />
|
||||
Copy
|
||||
</Button>
|
||||
</Copy>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button secondary on:click={() => (showEditor = false)}>Cancel</Button>
|
||||
<Button submit disabled={isButtonDisabled}>Save</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
Reference in New Issue
Block a user