mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Generated
+794
-1833
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@analytics/google-analytics": "^1.0.5",
|
||||
"@appwrite.io/console": "npm:christy-console@^0.3.0",
|
||||
"@appwrite.io/console": "npm:christy-console@^0.4.0",
|
||||
"@appwrite.io/pink": "^0.0.6-rc.14",
|
||||
"@analytics/google-tag-manager": "^0.5.3",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
|
||||
@@ -142,6 +142,7 @@ export enum Submit {
|
||||
ProjectUpdateName = 'submit_project_update_name',
|
||||
ProjectUpdateTeam = 'submit_project_update_team',
|
||||
ProjectService = 'submit_project_service',
|
||||
ProjectUpdateSMTP = 'submit_project_update_smtp',
|
||||
MemberCreate = 'submit_member_create',
|
||||
MemberDelete = 'submit_member_delete',
|
||||
MembershipUpdateStatus = 'submit_membership_update_status',
|
||||
@@ -221,5 +222,16 @@ export enum Submit {
|
||||
BucketUpdateExtensions = 'submit_bucket_update_extensions',
|
||||
FileCreate = 'submit_file_create',
|
||||
FileDelete = 'submit_file_delete',
|
||||
FileUpdatePermissions = 'submit_file_update_permissions'
|
||||
FileUpdatePermissions = 'submit_file_update_permissions',
|
||||
EmailChangeLocale = 'submit_email_change_locale',
|
||||
EmailResetTemplate = 'submit_email_reset_template',
|
||||
EmailUpdateInviteTemplate = 'submit_email_update_invite_template',
|
||||
EmailUpdateMagicUrlTemplate = 'submit_email_update_magic_url_template',
|
||||
EmailUpdateRecoveryTemplate = 'submit_email_update_recovery_template',
|
||||
EmailUpdateVerificationTemplate = 'submit_email_update_verification_template',
|
||||
SmsChangeLocale = 'submit_sms_change_locale',
|
||||
SmsResetTemplate = 'submit_sms_reset_template',
|
||||
SmsUpdateInviteTemplate = 'submit_sms_update_invite_template',
|
||||
SmsUpdateLoginTemplate = 'submit_sms_update_login_template',
|
||||
SmsUpdateVerificationTemplate = 'submit_sms_update_verification_template'
|
||||
}
|
||||
|
||||
@@ -5,12 +5,16 @@
|
||||
export let dismissible = false;
|
||||
export let type: 'info' | 'success' | 'warning' | 'error' = 'info';
|
||||
export let buttons: Buttons[] = [];
|
||||
export let isAction = false;
|
||||
export let isStandalone = false;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
</script>
|
||||
|
||||
<section
|
||||
class="alert"
|
||||
class:is-action={isAction}
|
||||
class:is-standalone={isStandalone}
|
||||
class:is-success={type === 'success'}
|
||||
class:is-warning={type === 'warning'}
|
||||
class:is-danger={type === 'error'}
|
||||
@@ -39,7 +43,7 @@
|
||||
</h6>
|
||||
{/if}
|
||||
<p class="alert-message"><slot /></p>
|
||||
{#if buttons?.length}
|
||||
{#if buttons?.length && !isAction}
|
||||
<div class="alert-buttons u-flex">
|
||||
{#each buttons as button}
|
||||
<button class="button is-text" on:click={button.method}>
|
||||
@@ -49,5 +53,14 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if buttons?.length && isAction}
|
||||
<div class="alert-buttons u-flex u-gap-16 u-cross-child-center">
|
||||
{#each buttons as button}
|
||||
<button class="button is-text" on:click={button.method}>
|
||||
<span class="text">{button.name}</span>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { clickOnEnter } from '$lib/helpers/a11y';
|
||||
|
||||
export let withIndentation = false;
|
||||
export let open = false;
|
||||
</script>
|
||||
|
||||
<li class="collapsible-item">
|
||||
<details class="collapsible-wrapper" {open}>
|
||||
<summary class="collapsible-button">
|
||||
<summary class="collapsible-button" on:keyup={clickOnEnter} on:click>
|
||||
<slot name="beforetitle" />
|
||||
<div>
|
||||
<span class="text"><slot name="title" /></span>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
export let value: string;
|
||||
export let event: string = null;
|
||||
export let centered = true;
|
||||
|
||||
function truncateText(node: HTMLElement) {
|
||||
const MAX_TRIES = 100;
|
||||
@@ -49,6 +50,7 @@
|
||||
<Copy {value} {event}>
|
||||
<div
|
||||
class="interactive-text-output is-buttons-on-top"
|
||||
class:u-text-center={centered}
|
||||
style:min-inline-size="0"
|
||||
style:display="inline-flex">
|
||||
<span
|
||||
|
||||
@@ -20,9 +20,11 @@
|
||||
export let placeholder = '';
|
||||
export let required = false;
|
||||
export let disabled = false;
|
||||
export let fullWidth = false;
|
||||
export let autofocus = false;
|
||||
export let interactiveOutput = false;
|
||||
// Input value
|
||||
// stretch is used inside of a flex container to give the element flex:1
|
||||
export let stretch = true;
|
||||
export let search = '';
|
||||
// The actual selected value
|
||||
export let value: Option['value'];
|
||||
@@ -82,9 +84,12 @@
|
||||
value = option.value;
|
||||
search = option.label;
|
||||
// It's not working without this line.
|
||||
!$$slots.output && (element.value = search);
|
||||
if ($$slots.output) {
|
||||
search = '';
|
||||
} else {
|
||||
element.value = search;
|
||||
}
|
||||
hasFocus = false;
|
||||
search = '';
|
||||
dispatch('select', option);
|
||||
}
|
||||
|
||||
@@ -99,7 +104,10 @@
|
||||
$: showClearBtn = (hasFocus && search) || value;
|
||||
</script>
|
||||
|
||||
<li class="u-position-relative form-item u-stretch">
|
||||
<li
|
||||
class="u-position-relative form-item"
|
||||
class:u-width-full-line={fullWidth}
|
||||
class:u-stretch={stretch}>
|
||||
<DropList
|
||||
bind:show={hasFocus}
|
||||
noStyle
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { FormItem, Helper, Label } from '.';
|
||||
import NullCheckbox from './nullCheckbox.svelte';
|
||||
import TextCounter from './textCounter.svelte';
|
||||
import { clickOnEnter } from '$lib/helpers/a11y';
|
||||
|
||||
export let label: string;
|
||||
export let showLabel = true;
|
||||
@@ -60,7 +61,7 @@
|
||||
{label}
|
||||
</Label>
|
||||
|
||||
<div class="input-text-wrapper">
|
||||
<div class="input-text-wrapper" on:click on:keyup={clickOnEnter}>
|
||||
<textarea
|
||||
{id}
|
||||
{placeholder}
|
||||
|
||||
@@ -19,17 +19,23 @@
|
||||
hasChildren: true,
|
||||
event: 'teams'
|
||||
},
|
||||
{
|
||||
href: `${path}/security`,
|
||||
title: 'Security',
|
||||
event: 'security'
|
||||
},
|
||||
{
|
||||
href: `${path}/templates`,
|
||||
title: 'Templates',
|
||||
hasChildren: false,
|
||||
event: 'templates'
|
||||
},
|
||||
{
|
||||
href: `${path}/usage`,
|
||||
title: 'Usage',
|
||||
hasChildren: true,
|
||||
event: 'usage'
|
||||
},
|
||||
{
|
||||
href: `${path}/security`,
|
||||
title: 'Security',
|
||||
event: 'security'
|
||||
},
|
||||
{
|
||||
href: `${path}/settings`,
|
||||
title: 'Settings',
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
<script context="module" lang="ts">
|
||||
export async function loadEmailTemplate(projectId: string, type: string, locale: string) {
|
||||
try {
|
||||
return await sdk.forConsole.projects.getEmailTemplate(projectId, type, locale);
|
||||
} catch (e) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: e.message
|
||||
});
|
||||
}
|
||||
}
|
||||
export async function loadSmsTemplate(projectId: string, type: string, locale: string) {
|
||||
try {
|
||||
return await sdk.forConsole.projects.getSmsTemplate(projectId, type, locale);
|
||||
} catch (e) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: e.message
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { Alert, CardGrid, Collapsible, CollapsibleItem, Heading } from '$lib/components';
|
||||
import { Container } from '$lib/layout';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { project } from '../../store';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { onMount } from 'svelte';
|
||||
import type { PageData } from './$types';
|
||||
import EmailVerificationTemplate from './emailVerificationTemplate.svelte';
|
||||
import EmailMagicUrlTemplate from './emailMagicUrlTemplate.svelte';
|
||||
import EmailRecoveryTemplate from './emailRecoveryTemplate.svelte';
|
||||
import EmailInviteTemplate from './emailInviteTemplate.svelte';
|
||||
import SmsVerificationTemplate from './smsVerificationTemplate.svelte';
|
||||
import SmsLoginTemplate from './smsLoginTemplate.svelte';
|
||||
import { baseEmailTemplate, baseSmsTemplate, emailTemplate, smsTemplate } from './store';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
|
||||
export let data: PageData;
|
||||
const projectId = $page.params.project;
|
||||
|
||||
let emailOpen = 'verification';
|
||||
$: emailVerificationOpen = emailOpen === 'verification';
|
||||
$: emailMagicSessionOpen = emailOpen === 'magicSession';
|
||||
$: emailResetPassword = emailOpen === 'recovery';
|
||||
$: emailInviteUser = emailOpen === 'invitation';
|
||||
|
||||
let smsOpen = 'verification';
|
||||
$: smsVerificationOpen = smsOpen === 'verification';
|
||||
$: smsLoginOpen = smsOpen === 'login';
|
||||
$: smsInvitationOpen = smsOpen === 'invitation';
|
||||
|
||||
onMount(async () => {
|
||||
openEmail('verification');
|
||||
openSms('verification');
|
||||
});
|
||||
|
||||
async function openEmail(type: string) {
|
||||
type === emailOpen ? (emailOpen = null) : (emailOpen = type);
|
||||
$emailTemplate = await loadEmailTemplate(projectId, type, 'en');
|
||||
$baseEmailTemplate = { ...$emailTemplate };
|
||||
}
|
||||
|
||||
async function openSms(type: string) {
|
||||
type === smsOpen ? (smsOpen = null) : (smsOpen = type);
|
||||
$smsTemplate = await loadSmsTemplate(projectId, type, 'en');
|
||||
$baseSmsTemplate = { ...$smsTemplate };
|
||||
}
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<div class="u-flex u-gap-8 u-cross-center">
|
||||
<Heading tag="h2" size="5">Templates</Heading>
|
||||
<div class="tag eyebrow-heading-3">
|
||||
<span class="text u-x-small">Beta</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if !$project.smtpEnabled}
|
||||
<div class="u-margin-block-start-24">
|
||||
<Alert
|
||||
isStandalone
|
||||
dismissible
|
||||
type="info"
|
||||
buttons={[
|
||||
{
|
||||
name: 'Add SMTP server',
|
||||
method: () => {
|
||||
goto(`${base}/console/project-${$project.$id}/settings/smtp`);
|
||||
}
|
||||
}
|
||||
]}>
|
||||
<svelte:fragment slot="title">
|
||||
Custom SMTP server is required for customizing emails
|
||||
</svelte:fragment>
|
||||
Configure a custom SMTP server to enable custom email templates and prevent emails from
|
||||
being labeled as spam.
|
||||
</Alert>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<CardGrid>
|
||||
<Heading size="7" tag="h3">Email templates</Heading>
|
||||
<p class="text">
|
||||
Use templates to send and process account management emails. <a
|
||||
href="https://appwrite.io/docs"
|
||||
class="link">
|
||||
Learn more about email templates.
|
||||
</a>
|
||||
<!-- TODO Docs link -->
|
||||
</p>
|
||||
|
||||
<svelte:fragment slot="aside">
|
||||
<Collapsible>
|
||||
<CollapsibleItem
|
||||
bind:open={emailVerificationOpen}
|
||||
on:click={(e) => {
|
||||
// preventing default and propagation to open the collapsible correctly
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
openEmail('verification');
|
||||
}}>
|
||||
<svelte:fragment slot="title">Verification</svelte:fragment>
|
||||
<p class="text">
|
||||
Send a verification email to users that sign in with their email and
|
||||
password.
|
||||
</p>
|
||||
<EmailVerificationTemplate localeCodes={data.localeCodes} />
|
||||
</CollapsibleItem>
|
||||
<CollapsibleItem
|
||||
bind:open={emailMagicSessionOpen}
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
openEmail('magicSession');
|
||||
}}>
|
||||
<svelte:fragment slot="title">Magic URL</svelte:fragment>
|
||||
<p class="text">Send an email to users that sign in with a magic URL.</p>
|
||||
<EmailMagicUrlTemplate localeCodes={data.localeCodes} />
|
||||
</CollapsibleItem>
|
||||
<CollapsibleItem
|
||||
bind:open={emailResetPassword}
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
openEmail('recovery');
|
||||
}}>
|
||||
<svelte:fragment slot="title">Reset password</svelte:fragment>
|
||||
<p class="text">Send a recovery email to users that forget their password.</p>
|
||||
<EmailRecoveryTemplate localeCodes={data.localeCodes} />
|
||||
</CollapsibleItem>
|
||||
<CollapsibleItem
|
||||
bind:open={emailInviteUser}
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
openEmail('invitation');
|
||||
}}>
|
||||
<svelte:fragment slot="title">Invite user</svelte:fragment>
|
||||
<p class="text">Send an invitation email to become a member of your project.</p>
|
||||
<EmailInviteTemplate localeCodes={data.localeCodes} />
|
||||
</CollapsibleItem>
|
||||
</Collapsible>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<Button href={`${base}/console/project-${$project.$id}/settings/smtp`} secondary>
|
||||
Add SMTP server
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
|
||||
<CardGrid>
|
||||
<Heading size="7" tag="h3">SMS templates</Heading>
|
||||
<p class="text">
|
||||
Use templates to send and process account management mobile messages. <a
|
||||
href="https://appwrite.io/docs"
|
||||
class="link">
|
||||
Learn more about SMS templates</a
|
||||
>. <!-- TODO Docs link -->
|
||||
</p>
|
||||
|
||||
<svelte:fragment slot="aside">
|
||||
<Collapsible>
|
||||
<CollapsibleItem
|
||||
bind:open={smsVerificationOpen}
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
openSms('verification');
|
||||
}}>
|
||||
<svelte:fragment slot="title">Verification</svelte:fragment>
|
||||
<p class="text">
|
||||
Send a verification SMS to users that sign in with their phone
|
||||
</p>
|
||||
<SmsVerificationTemplate localeCodes={data.localeCodes} />
|
||||
</CollapsibleItem>
|
||||
<CollapsibleItem
|
||||
bind:open={smsLoginOpen}
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
openSms('login');
|
||||
}}>
|
||||
<svelte:fragment slot="title">Login</svelte:fragment>
|
||||
<p class="text">
|
||||
Send a one-time passcode to users' mobile phones to allow them to sign in.
|
||||
</p>
|
||||
<SmsLoginTemplate localeCodes={data.localeCodes} />
|
||||
</CollapsibleItem>
|
||||
<CollapsibleItem
|
||||
bind:open={smsInvitationOpen}
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
openSms('invitation');
|
||||
}}>
|
||||
<svelte:fragment slot="title">Invitation</svelte:fragment>
|
||||
<p class="text">Send an invitation SMS to become a member of your project.</p>
|
||||
<SmsLoginTemplate localeCodes={data.localeCodes} />
|
||||
</CollapsibleItem>
|
||||
</Collapsible>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Container>
|
||||
@@ -0,0 +1,8 @@
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
|
||||
export const load = async () => {
|
||||
const codes = await sdk.forProject.locale.listCodes();
|
||||
return {
|
||||
localeCodes: codes.localeCodes
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
<script lang="ts">
|
||||
import EmailTemplate from './emailTemplate.svelte';
|
||||
import LocaleOptions from './localeOptions.svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { loadEmailTemplate } from './+page.svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { baseEmailTemplate, emailTemplate } from './store';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Id } from '$lib/components';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
export let localeCodes: Models.LocaleCode[];
|
||||
|
||||
const projectId = $page.params.project;
|
||||
let locale = 'en';
|
||||
let loading = false;
|
||||
|
||||
async function onLocaleChange() {
|
||||
const timeout = setTimeout(() => {
|
||||
loading = true;
|
||||
}, 1000);
|
||||
try {
|
||||
const template = await loadEmailTemplate(projectId, 'invitation', locale);
|
||||
emailTemplate.set(template);
|
||||
$baseEmailTemplate = { ...$emailTemplate };
|
||||
trackEvent(Submit.EmailChangeLocale, { locale, type: 'invitation' });
|
||||
} catch (error) {
|
||||
trackError(error, Submit.EmailChangeLocale);
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="boxes-wrapper u-margin-block-start-16">
|
||||
<LocaleOptions {localeCodes} on:select={onLocaleChange} bind:value={locale} />
|
||||
<EmailTemplate bind:loading>
|
||||
<Id value={'{{team}}'}>{'{{team}}'}</Id>
|
||||
<Id value={'{{user}}'}>{'{{user}}'}</Id>
|
||||
<Id value={'{{project}}'}>{'{{project}}'}</Id>
|
||||
<Id value={'{{redirect}}'}>{'{{redirect}}'}</Id>
|
||||
</EmailTemplate>
|
||||
</div>
|
||||
@@ -0,0 +1,49 @@
|
||||
<script lang="ts">
|
||||
import { loadEmailTemplate } from './+page.svelte';
|
||||
import EmailTemplate from './emailTemplate.svelte';
|
||||
import LocaleOptions from './localeOptions.svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { baseEmailTemplate, emailTemplate } from './store';
|
||||
import { page } from '$app/stores';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Id } from '$lib/components';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
export let localeCodes: Models.LocaleCode[];
|
||||
const projectId = $page.params.project;
|
||||
|
||||
let locale = 'en';
|
||||
let loading = false;
|
||||
|
||||
async function onLocaleChange() {
|
||||
const timeout = setTimeout(() => {
|
||||
loading = true;
|
||||
}, 1000);
|
||||
try {
|
||||
const template = await loadEmailTemplate(projectId, 'magicSession', locale);
|
||||
emailTemplate.set(template);
|
||||
$baseEmailTemplate = { ...$emailTemplate };
|
||||
trackEvent(Submit.EmailChangeLocale, { locale, type: 'magicSession' });
|
||||
} catch (error) {
|
||||
trackError(error, Submit.EmailChangeLocale);
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="boxes-wrapper u-margin-block-start-16">
|
||||
<LocaleOptions {localeCodes} on:select={onLocaleChange} bind:value={locale} />
|
||||
<EmailTemplate bind:loading>
|
||||
<Id value={'{{team}}'}>{'{{team}}'}</Id>
|
||||
<Id value={'{{user}}'}>{'{{user}}'}</Id>
|
||||
<Id value={'{{project}}'}>{'{{project}}'}</Id>
|
||||
<Id value={'{{redirect}}'}>{'{{redirect}}'}</Id>
|
||||
</EmailTemplate>
|
||||
</div>
|
||||
@@ -0,0 +1,48 @@
|
||||
<script lang="ts">
|
||||
import EmailTemplate from './emailTemplate.svelte';
|
||||
import LocaleOptions from './localeOptions.svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { baseEmailTemplate, emailTemplate } from './store';
|
||||
import { loadEmailTemplate } from './+page.svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Id } from '$lib/components';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
export let localeCodes: Models.LocaleCode[];
|
||||
const projectId = $page.params.project;
|
||||
|
||||
let locale = 'en';
|
||||
let loading = false;
|
||||
|
||||
async function onLocaleChange() {
|
||||
const timeout = setTimeout(() => {
|
||||
loading = true;
|
||||
}, 1000);
|
||||
try {
|
||||
const template = await loadEmailTemplate(projectId, 'recovery', locale);
|
||||
emailTemplate.set(template);
|
||||
$baseEmailTemplate = { ...$emailTemplate };
|
||||
trackEvent(Submit.EmailChangeLocale, { locale, type: 'recovery' });
|
||||
} catch (error) {
|
||||
trackError(error, Submit.EmailChangeLocale);
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="boxes-wrapper u-margin-block-start-16">
|
||||
<LocaleOptions {localeCodes} on:select={onLocaleChange} bind:value={locale} />
|
||||
<EmailTemplate bind:loading>
|
||||
<Id value={'{{team}}'}>{'{{team}}'}</Id>
|
||||
<Id value={'{{user}}'}>{'{{user}}'}</Id>
|
||||
<Id value={'{{project}}'}>{'{{project}}'}</Id>
|
||||
<Id value={'{{redirect}}'}>{'{{redirect}}'}</Id>
|
||||
</EmailTemplate>
|
||||
</div>
|
||||
@@ -0,0 +1,139 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormList,
|
||||
InputEmail,
|
||||
InputText,
|
||||
InputTextarea
|
||||
} from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { project } from '../../store';
|
||||
import ResetEmail from './resetEmail.svelte';
|
||||
import { baseEmailTemplate, emailTemplate } from './store';
|
||||
import { deepEqual } from '$lib/helpers/object';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
export let loading = false;
|
||||
let openResetModal = false;
|
||||
|
||||
let eventType = Submit.EmailUpdateInviteTemplate;
|
||||
|
||||
async function saveEmailTemplate() {
|
||||
if (!$emailTemplate.locale) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: 'Locale is required'
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
switch ($emailTemplate.type) {
|
||||
case 'invitation':
|
||||
eventType = Submit.EmailUpdateInviteTemplate;
|
||||
break;
|
||||
case 'magicSession':
|
||||
eventType = Submit.EmailUpdateMagicUrlTemplate;
|
||||
break;
|
||||
case 'recovery':
|
||||
eventType = Submit.EmailUpdateRecoveryTemplate;
|
||||
break;
|
||||
case 'verification':
|
||||
eventType = Submit.EmailUpdateVerificationTemplate;
|
||||
break;
|
||||
}
|
||||
await sdk.forConsole.projects.updateEmailTemplate(
|
||||
$project.$id,
|
||||
$emailTemplate.type,
|
||||
$emailTemplate.locale,
|
||||
$emailTemplate.senderName,
|
||||
$emailTemplate.senderEmail,
|
||||
$emailTemplate.subject,
|
||||
$emailTemplate.message,
|
||||
$emailTemplate.replyTo ? $emailTemplate.replyTo : undefined
|
||||
);
|
||||
|
||||
$baseEmailTemplate = {
|
||||
...$emailTemplate
|
||||
};
|
||||
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `Email ${$emailTemplate.type} template for ${$emailTemplate.locale} updated`
|
||||
});
|
||||
trackEvent(eventType, {
|
||||
locale: $emailTemplate.locale
|
||||
});
|
||||
} catch (e) {
|
||||
trackError(e, eventType);
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: e.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$: isButtonDisabled = deepEqual($emailTemplate, $baseEmailTemplate);
|
||||
</script>
|
||||
|
||||
<div class="box u-position-relative">
|
||||
{#if loading}
|
||||
<div
|
||||
class="u-position-absolute u-width-full-line u-flex u-flex-vertical u-main-center u-cross-center u-gap-16 u-margin-block-start-32"
|
||||
style="inset-inline-start: 0;">
|
||||
<div class="loader" />
|
||||
<p class="text">Loading template...</p>
|
||||
</div>
|
||||
{/if}
|
||||
<div class:u-opacity-0={loading} style={loading ? 'pointer-events: none' : ''}>
|
||||
<Form onSubmit={saveEmailTemplate}>
|
||||
<FormList gap={8}>
|
||||
<InputText
|
||||
id="senderName"
|
||||
label="Sender name"
|
||||
bind:value={$emailTemplate.senderName}
|
||||
placeholder={'{{project}}'} />
|
||||
<InputEmail
|
||||
bind:value={$emailTemplate.senderEmail}
|
||||
id="senderEmail"
|
||||
label="Sender email"
|
||||
placeholder="Enter sender email" />
|
||||
<InputEmail id="replyTo" label="Reply to" placeholder="noreply@appwrite.io" />
|
||||
{#if $$slots.default}
|
||||
<li style="margin-block: 1rem;">
|
||||
<p class="text">
|
||||
Click to copy variables for the fields below. Learn more <a
|
||||
class="link"
|
||||
href="/#">here</a
|
||||
>.
|
||||
<!-- TODO: add link to docs -->
|
||||
</p>
|
||||
<div class="u-margin-block-start-16 u-flex u-gap-8">
|
||||
<slot />
|
||||
</div>
|
||||
</li>
|
||||
{/if}
|
||||
<InputText
|
||||
bind:value={$emailTemplate.subject}
|
||||
id="subject"
|
||||
label="Subject"
|
||||
placeholder="Enter subject" />
|
||||
<InputTextarea
|
||||
bind:value={$emailTemplate.message}
|
||||
id="message"
|
||||
label="Message"
|
||||
placeholder="Enter your message"
|
||||
tooltip="Set up an SMTP server to edit the message body"
|
||||
readonly={!$project.smtpEnabled} />
|
||||
</FormList>
|
||||
<div class="u-sep-block-start u-margin-block-start-24" />
|
||||
<div class="u-flex u-gap-16 u-main-end u-margin-block-start-24">
|
||||
<Button on:click={() => (openResetModal = true)} text>Reset changes</Button>
|
||||
<Button submit disabled={isButtonDisabled}>Update</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ResetEmail bind:show={openResetModal} />
|
||||
@@ -0,0 +1,49 @@
|
||||
<script lang="ts">
|
||||
import EmailTemplate from './emailTemplate.svelte';
|
||||
import LocaleOptions from './localeOptions.svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { loadEmailTemplate } from './+page.svelte';
|
||||
import { baseEmailTemplate, emailTemplate } from './store';
|
||||
import { page } from '$app/stores';
|
||||
import { Id } from '$lib/components';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
export let localeCodes: Models.LocaleCode[];
|
||||
const projectId = $page.params.project;
|
||||
|
||||
let locale = 'en';
|
||||
let loading = false;
|
||||
|
||||
async function onLocaleChange() {
|
||||
const timeout = setTimeout(() => {
|
||||
loading = true;
|
||||
}, 1000);
|
||||
try {
|
||||
const template = await loadEmailTemplate(projectId, 'verification', locale);
|
||||
emailTemplate.set(template);
|
||||
$baseEmailTemplate = { ...$emailTemplate };
|
||||
trackEvent(Submit.EmailChangeLocale, { locale, type: 'verification' });
|
||||
} catch (error) {
|
||||
trackError(error, Submit.EmailChangeLocale);
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="boxes-wrapper u-margin-block-start-16">
|
||||
<LocaleOptions {localeCodes} on:select={onLocaleChange} bind:value={locale} />
|
||||
|
||||
<EmailTemplate bind:loading>
|
||||
<Id value={'{{team}}'}>{'{{team}}'}</Id>
|
||||
<Id value={'{{user}}'}>{'{{user}}'}</Id>
|
||||
<Id value={'{{project}}'}>{'{{project}}'}</Id>
|
||||
<Id value={'{{redirect}}'}>{'{{redirect}}'}</Id>
|
||||
</EmailTemplate>
|
||||
</div>
|
||||
@@ -0,0 +1,39 @@
|
||||
<script lang="ts">
|
||||
import { InputSelectSearch } from '$lib/elements/forms';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
export let localeCodes: Models.LocaleCode[];
|
||||
export let value = 'en';
|
||||
|
||||
let search = 'English';
|
||||
|
||||
$: options =
|
||||
localeCodes
|
||||
?.map((code) => ({
|
||||
label: code.name,
|
||||
value: code.code
|
||||
}))
|
||||
?.filter((option) => option.label?.toLowerCase().startsWith(search?.toLowerCase())) ??
|
||||
[];
|
||||
</script>
|
||||
|
||||
<div class="box u-flex u-gap-16" style:--box-border-radius="var(--border-radius-small)">
|
||||
<div class="u-un-break-text">
|
||||
<span class="icon-translate" />
|
||||
<span class="text">Template language</span>
|
||||
</div>
|
||||
<InputSelectSearch
|
||||
on:select
|
||||
label="Language"
|
||||
id="language"
|
||||
placeholder="Select a language"
|
||||
name="locale"
|
||||
bind:value
|
||||
bind:search
|
||||
required
|
||||
fullWidth
|
||||
stretch={false}
|
||||
showLabel={false}
|
||||
interactiveOutput
|
||||
{options} />
|
||||
</div>
|
||||
@@ -0,0 +1,58 @@
|
||||
<script lang="ts">
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Modal } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { project } from '../../store';
|
||||
import { loadEmailTemplate } from './+page.svelte';
|
||||
import { baseEmailTemplate, emailTemplate } from './store';
|
||||
|
||||
export let show = false;
|
||||
|
||||
let error: string;
|
||||
|
||||
async function reset() {
|
||||
try {
|
||||
await sdk.forConsole.projects.deleteEmailTemplate(
|
||||
$project.$id,
|
||||
$emailTemplate.type,
|
||||
$emailTemplate.locale
|
||||
);
|
||||
$emailTemplate = await loadEmailTemplate(
|
||||
$project.$id,
|
||||
$emailTemplate.type,
|
||||
$emailTemplate.locale
|
||||
);
|
||||
$baseEmailTemplate = { ...$emailTemplate };
|
||||
trackEvent(Submit.EmailResetTemplate, {
|
||||
locale: $emailTemplate.locale,
|
||||
type: $emailTemplate.type
|
||||
});
|
||||
|
||||
show = false;
|
||||
} catch (e) {
|
||||
trackError(e, Submit.EmailResetTemplate);
|
||||
error = e.message;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
onSubmit={reset}
|
||||
size="big"
|
||||
{error}
|
||||
bind:show
|
||||
headerDivider={false}>
|
||||
<svelte:fragment slot="header">Reset Email Template?</svelte:fragment>
|
||||
<p class="text">
|
||||
Are you sure you want to reset the email template?
|
||||
<b>Default values will be set in all inputs.</b>
|
||||
</p>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text secondary on:click={() => (show = false)}>Cancel</Button>
|
||||
<Button secondary submit>Reset</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
@@ -0,0 +1,58 @@
|
||||
<script lang="ts">
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Modal } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { project } from '../../store';
|
||||
import { loadSmsTemplate } from './+page.svelte';
|
||||
import { baseSmsTemplate, smsTemplate } from './store';
|
||||
|
||||
export let show = false;
|
||||
|
||||
let error: string;
|
||||
|
||||
async function reset() {
|
||||
try {
|
||||
await sdk.forConsole.projects.deleteSmsTemplate(
|
||||
$project.$id,
|
||||
$smsTemplate.type,
|
||||
$smsTemplate.locale
|
||||
);
|
||||
|
||||
$smsTemplate = await loadSmsTemplate(
|
||||
$project.$id,
|
||||
$smsTemplate.type,
|
||||
$smsTemplate.locale
|
||||
);
|
||||
$baseSmsTemplate = { ...$smsTemplate };
|
||||
trackEvent(Submit.SmsResetTemplate, {
|
||||
locale: $smsTemplate.locale,
|
||||
type: $smsTemplate.type
|
||||
});
|
||||
show = false;
|
||||
} catch (e) {
|
||||
trackError(e, Submit.SmsResetTemplate);
|
||||
error = e.message;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
onSubmit={reset}
|
||||
size="big"
|
||||
{error}
|
||||
bind:show
|
||||
headerDivider={false}>
|
||||
<svelte:fragment slot="header">Reset SMS Template?</svelte:fragment>
|
||||
<p class="text">
|
||||
Are you sure you want to reset the SMS template?
|
||||
<b>Default values will be set in all inputs.</b>
|
||||
</p>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text secondary on:click={() => (show = false)}>Cancel</Button>
|
||||
<Button secondary submit>Reset</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
@@ -0,0 +1,48 @@
|
||||
<script lang="ts">
|
||||
import SmsTemplate from './smsTemplate.svelte';
|
||||
import LocaleOptions from './localeOptions.svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { baseSmsTemplate, smsTemplate } from './store';
|
||||
import { page } from '$app/stores';
|
||||
import { loadSmsTemplate } from './+page.svelte';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Id } from '$lib/components';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
export let localeCodes: Models.LocaleCode[];
|
||||
|
||||
const projectId = $page.params.project;
|
||||
let locale = 'en';
|
||||
let loading = false;
|
||||
|
||||
async function onLocaleChange() {
|
||||
const timeout = setTimeout(() => {
|
||||
loading = true;
|
||||
}, 1000);
|
||||
try {
|
||||
const template = await loadSmsTemplate(projectId, 'invitation', locale);
|
||||
smsTemplate.set(template);
|
||||
$baseSmsTemplate = { ...$smsTemplate };
|
||||
trackEvent(Submit.SmsChangeLocale, { locale, type: 'invitation' });
|
||||
} catch (error) {
|
||||
trackError(error, Submit.SmsChangeLocale);
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="boxes-wrapper u-margin-block-start-16">
|
||||
<LocaleOptions {localeCodes} on:select={onLocaleChange} bind:value={locale} />
|
||||
<SmsTemplate bind:loading>
|
||||
<Id value={'{{team}}'}>{'{{team}}'}</Id>
|
||||
<Id value={'{{user}}'}>{'{{user}}'}</Id>
|
||||
<Id value={'{{project}}'}>{'{{project}}'}</Id>
|
||||
<Id value={'{{redirect}}'}>{'{{redirect}}'}</Id>
|
||||
</SmsTemplate>
|
||||
</div>
|
||||
@@ -0,0 +1,49 @@
|
||||
<script lang="ts">
|
||||
import SmsTemplate from './smsTemplate.svelte';
|
||||
import LocaleOptions from './localeOptions.svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { page } from '$app/stores';
|
||||
import { loadSmsTemplate } from './+page.svelte';
|
||||
import { baseSmsTemplate, smsTemplate } from './store';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Id } from '$lib/components';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
export let localeCodes: Models.LocaleCode[];
|
||||
|
||||
const projectId = $page.params.project;
|
||||
let locale = 'en';
|
||||
let loading = false;
|
||||
|
||||
async function onLocaleChange() {
|
||||
const timeout = setTimeout(() => {
|
||||
loading = true;
|
||||
}, 1000);
|
||||
try {
|
||||
const template = await loadSmsTemplate(projectId, 'login', locale);
|
||||
smsTemplate.set(template);
|
||||
$baseSmsTemplate = { ...$smsTemplate };
|
||||
trackEvent(Submit.SmsChangeLocale, { locale, type: 'login' });
|
||||
} catch (error) {
|
||||
trackError(error, Submit.SmsChangeLocale);
|
||||
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="boxes-wrapper u-margin-block-start-16">
|
||||
<LocaleOptions {localeCodes} on:select={onLocaleChange} bind:value={locale} />
|
||||
<SmsTemplate bind:loading>
|
||||
<Id value={'{{team}}'}>{'{{team}}'}</Id>
|
||||
<Id value={'{{user}}'}>{'{{user}}'}</Id>
|
||||
<Id value={'{{project}}'}>{'{{project}}'}</Id>
|
||||
<Id value={'{{redirect}}'}>{'{{redirect}}'}</Id>
|
||||
</SmsTemplate>
|
||||
</div>
|
||||
@@ -0,0 +1,94 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Button, Form, FormList, InputTextarea } from '$lib/elements/forms';
|
||||
import ResetSms from './resetSms.svelte';
|
||||
import { baseSmsTemplate, smsTemplate } from './store';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { deepEqual } from '$lib/helpers/object';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
export let loading = false;
|
||||
const projectId = $page.params.project;
|
||||
let openResetModal = false;
|
||||
|
||||
let eventType = Submit.SmsUpdateInviteTemplate;
|
||||
|
||||
async function saveSmsTemplate() {
|
||||
if (!$smsTemplate.locale) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: 'Locale is required'
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
switch ($smsTemplate.type) {
|
||||
case 'invitation':
|
||||
eventType = Submit.SmsUpdateInviteTemplate;
|
||||
break;
|
||||
case 'login':
|
||||
eventType = Submit.SmsUpdateLoginTemplate;
|
||||
break;
|
||||
case 'verification':
|
||||
eventType = Submit.SmsUpdateVerificationTemplate;
|
||||
break;
|
||||
}
|
||||
await sdk.forConsole.projects.updateSmsTemplate(
|
||||
projectId,
|
||||
$smsTemplate.type,
|
||||
$smsTemplate.locale,
|
||||
$smsTemplate.message
|
||||
);
|
||||
|
||||
$baseSmsTemplate = {
|
||||
...$smsTemplate
|
||||
};
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `SMS ${$smsTemplate.type} template for ${$smsTemplate.locale} updated`
|
||||
});
|
||||
trackEvent(eventType, {
|
||||
locale: $smsTemplate.locale
|
||||
});
|
||||
} catch (e) {
|
||||
trackError(e, eventType);
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: e.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$: isButtonDisabled = deepEqual($smsTemplate, $baseSmsTemplate);
|
||||
</script>
|
||||
|
||||
<div class="box u-position-relative">
|
||||
{#if loading}
|
||||
<div
|
||||
class="u-position-absolute u-width-full-line u-flex u-flex-vertical u-main-center u-cross-center u-gap-16 u-margin-block-start-32"
|
||||
style="inset-inline-start: 0;">
|
||||
<div class="loader" />
|
||||
<p class="text">Loading template...</p>
|
||||
</div>
|
||||
{/if}
|
||||
<div class:u-opacity-0={loading} style={loading ? 'pointer-events: none' : ''}>
|
||||
<Form onSubmit={saveSmsTemplate}>
|
||||
<FormList gap={8}>
|
||||
<InputTextarea
|
||||
bind:value={$smsTemplate.message}
|
||||
id="message"
|
||||
label="Message"
|
||||
placeholder="Enter your message" />
|
||||
</FormList>
|
||||
|
||||
<div class="u-sep-block-start u-margin-block-start-24" />
|
||||
|
||||
<div class="u-flex u-gap-16 u-main-end u-margin-block-start-24">
|
||||
<Button on:click={() => (openResetModal = true)} text>Reset changes</Button>
|
||||
<Button submit disabled={isButtonDisabled}>Update</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
<ResetSms bind:show={openResetModal} />
|
||||
@@ -0,0 +1,48 @@
|
||||
<script lang="ts">
|
||||
import SmsTemplate from './smsTemplate.svelte';
|
||||
import LocaleOptions from './localeOptions.svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { page } from '$app/stores';
|
||||
import { loadSmsTemplate } from './+page.svelte';
|
||||
import { baseSmsTemplate, smsTemplate } from './store';
|
||||
import { Id } from '$lib/components';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
export let localeCodes: Models.LocaleCode[];
|
||||
|
||||
const projectId = $page.params.project;
|
||||
let locale = 'en';
|
||||
let loading = false;
|
||||
|
||||
async function onLocaleChange() {
|
||||
const timeout = setTimeout(() => {
|
||||
loading = true;
|
||||
}, 1000);
|
||||
try {
|
||||
const template = await loadSmsTemplate(projectId, 'verification', locale);
|
||||
smsTemplate.set(template);
|
||||
$baseSmsTemplate = { ...$smsTemplate };
|
||||
trackEvent(Submit.SmsChangeLocale, { locale, type: 'verification' });
|
||||
} catch (error) {
|
||||
trackError(error, Submit.SmsChangeLocale);
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="boxes-wrapper u-margin-block-start-16">
|
||||
<LocaleOptions {localeCodes} on:select={onLocaleChange} bind:value={locale} />
|
||||
<SmsTemplate bind:loading>
|
||||
<Id value={'{{team}}'}>{'{{team}}'}</Id>
|
||||
<Id value={'{{user}}'}>{'{{user}}'}</Id>
|
||||
<Id value={'{{project}}'}>{'{{project}}'}</Id>
|
||||
<Id value={'{{redirect}}'}>{'{{redirect}}'}</Id>
|
||||
</SmsTemplate>
|
||||
</div>
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const emailTemplate = writable<Models.EmailTemplate>({
|
||||
type: null,
|
||||
locale: null,
|
||||
message: null,
|
||||
senderName: '',
|
||||
senderEmail: null,
|
||||
replyTo: null,
|
||||
subject: null
|
||||
});
|
||||
|
||||
export const baseEmailTemplate = writable<Models.EmailTemplate>({
|
||||
type: null,
|
||||
locale: null,
|
||||
message: null,
|
||||
senderName: null,
|
||||
senderEmail: null,
|
||||
replyTo: null,
|
||||
subject: null
|
||||
});
|
||||
|
||||
export const smsTemplate = writable<Models.SmsTemplate>({
|
||||
type: null,
|
||||
locale: null,
|
||||
message: null
|
||||
});
|
||||
export const baseSmsTemplate = writable<Models.SmsTemplate>({
|
||||
type: null,
|
||||
locale: null,
|
||||
message: null
|
||||
});
|
||||
@@ -21,6 +21,11 @@
|
||||
href: `${path}/webhooks`,
|
||||
title: 'Webhooks',
|
||||
event: 'webhooks'
|
||||
},
|
||||
{
|
||||
href: `${path}/smtp`,
|
||||
title: 'SMTP Settings',
|
||||
event: 'smtp'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
<script lang="ts">
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Button, Form, FormList, InputText, InputChoice } from '$lib/elements/forms';
|
||||
import { Container } from '$lib/layout';
|
||||
import { project } from '../../store';
|
||||
import { onMount } from 'svelte/internal';
|
||||
import InputPassword from '$lib/elements/forms/inputPassword.svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import InputNumber from '$lib/elements/forms/inputNumber.svelte';
|
||||
import { base } from '$app/paths';
|
||||
import { deepEqual } from '$lib/helpers/object';
|
||||
|
||||
let enabled = false;
|
||||
let sender: string;
|
||||
let host: string;
|
||||
let port: number;
|
||||
let username: string;
|
||||
let password: string;
|
||||
|
||||
onMount(() => {
|
||||
enabled = $project.smtpEnabled ?? false;
|
||||
sender = $project.smtpSender;
|
||||
host = $project.smtpHost;
|
||||
port = $project.smtpPort;
|
||||
username = $project.smtpUsername;
|
||||
password = $project.smtpPassword;
|
||||
});
|
||||
|
||||
async function updateSmtp() {
|
||||
try {
|
||||
await sdk.forConsole.projects.updateSmtpConfiguration(
|
||||
$project.$id,
|
||||
enabled,
|
||||
sender,
|
||||
host,
|
||||
port,
|
||||
username,
|
||||
password
|
||||
);
|
||||
|
||||
invalidate(Dependencies.PROJECT);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'SMTP server has been ' + (enabled ? 'enabled.' : 'disabled.')
|
||||
});
|
||||
trackEvent(Submit.ProjectUpdateSMTP);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
trackError(error, Submit.ProjectUpdateSMTP);
|
||||
}
|
||||
}
|
||||
|
||||
$: isButtonDisabled = deepEqual(
|
||||
{ enabled, sender, host, port, username, password },
|
||||
{
|
||||
enabled: $project.smtpEnabled,
|
||||
sender: $project.smtpSender,
|
||||
host: $project.smtpHost,
|
||||
port: $project.smtpPort,
|
||||
username: $project.smtpUsername,
|
||||
password: $project.smtpPassword
|
||||
}
|
||||
);
|
||||
|
||||
$: if (!enabled) {
|
||||
sender = undefined;
|
||||
host = undefined;
|
||||
port = undefined;
|
||||
username = undefined;
|
||||
password = undefined;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<Form onSubmit={updateSmtp}>
|
||||
<CardGrid>
|
||||
<Heading tag="h6" size="7">SMTP server</Heading>
|
||||
<p class="text">
|
||||
You can customize the email service by providing your own SMTP server. View your
|
||||
email templates <a
|
||||
href="{base}/console/project-{$project.$id}/auth/templates"
|
||||
class="link">here</a>
|
||||
</p>
|
||||
<svelte:fragment slot="aside">
|
||||
<FormList>
|
||||
<InputChoice
|
||||
type="switchbox"
|
||||
id="enabled"
|
||||
bind:value={enabled}
|
||||
label="Custom SMTP server">
|
||||
Enabling this option allows customizing email templates and prevents emails
|
||||
from being labeled as spam.
|
||||
</InputChoice>
|
||||
|
||||
{#if enabled}
|
||||
<InputText
|
||||
id="senderEmail"
|
||||
label="Sender email"
|
||||
bind:value={sender}
|
||||
required
|
||||
placeholder="user@example.io" />
|
||||
<InputText
|
||||
id="serverHost"
|
||||
label="Server host"
|
||||
bind:value={host}
|
||||
required
|
||||
placeholder="smtp.server.com" />
|
||||
<InputNumber
|
||||
id="serverPort"
|
||||
label="Server port"
|
||||
bind:value={port}
|
||||
required
|
||||
placeholder="587" />
|
||||
<InputText
|
||||
id="username"
|
||||
label="Username"
|
||||
bind:value={username}
|
||||
required
|
||||
placeholder="Enter username" />
|
||||
<InputPassword
|
||||
showPasswordButton
|
||||
id="passwort"
|
||||
label="Password"
|
||||
bind:value={password}
|
||||
required
|
||||
placeholder="Enter password" />
|
||||
|
||||
<InputChoice id="tls" label="TLS secure protocol">
|
||||
Enable if TLS is supported on your SMTP server.
|
||||
</InputChoice>
|
||||
{/if}
|
||||
</FormList>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<Button submit disabled={isButtonDisabled}>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
</Container>
|
||||
Reference in New Issue
Block a user