fix: templates not loading.

This commit is contained in:
Darshan
2025-05-30 13:18:41 +05:30
parent 637bf20cc2
commit f77d4fc073
12 changed files with 234 additions and 214 deletions
+1 -1
View File
@@ -26,7 +26,7 @@
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "^2.0.0-RC.1",
"@appwrite.io/pink-legacy": "^1.0.3",
"@appwrite.io/pink-svelte": "https://try-module.cloud/-/@appwrite/@appwrite.io/pink-svelte@f1287d5",
"@appwrite.io/pink-svelte": "https://try-module.cloud/-/@appwrite/@appwrite.io/pink-svelte@4b056c8",
"@popperjs/core": "^2.11.8",
"@sentry/sveltekit": "^8.38.0",
"@stripe/stripe-js": "^3.5.0",
+5 -5
View File
@@ -24,8 +24,8 @@ importers:
specifier: ^1.0.3
version: 1.0.3
'@appwrite.io/pink-svelte':
specifier: https://try-module.cloud/-/@appwrite/@appwrite.io/pink-svelte@f1287d5
version: https://try-module.cloud/-/@appwrite/%40appwrite.io%2Fpink-svelte@f1287d5(svelte@5.25.3)
specifier: https://try-module.cloud/-/@appwrite/@appwrite.io/pink-svelte@4b056c8
version: https://try-module.cloud/-/@appwrite/%40appwrite.io%2Fpink-svelte@4b056c8(svelte@5.25.3)
'@popperjs/core':
specifier: ^2.11.8
version: 2.11.8
@@ -278,8 +278,8 @@ packages:
'@appwrite.io/pink-legacy@1.0.3':
resolution: {integrity: sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ==}
'@appwrite.io/pink-svelte@https://try-module.cloud/-/@appwrite/%40appwrite.io%2Fpink-svelte@f1287d5':
resolution: {tarball: https://try-module.cloud/-/@appwrite/%40appwrite.io%2Fpink-svelte@f1287d5}
'@appwrite.io/pink-svelte@https://try-module.cloud/-/@appwrite/%40appwrite.io%2Fpink-svelte@4b056c8':
resolution: {tarball: https://try-module.cloud/-/@appwrite/%40appwrite.io%2Fpink-svelte@4b056c8}
version: 2.0.0-RC.2
peerDependencies:
svelte: ^4.0.0
@@ -3635,7 +3635,7 @@ snapshots:
'@appwrite.io/pink-icons': 1.0.0
the-new-css-reset: 1.11.3
'@appwrite.io/pink-svelte@https://try-module.cloud/-/@appwrite/%40appwrite.io%2Fpink-svelte@f1287d5(svelte@5.25.3)':
'@appwrite.io/pink-svelte@https://try-module.cloud/-/@appwrite/%40appwrite.io%2Fpink-svelte@4b056c8(svelte@5.25.3)':
dependencies:
'@appwrite.io/pink-icons-svelte': 2.0.0-RC.1(svelte@5.25.3)
'@floating-ui/dom': 1.6.13
@@ -1,4 +1,14 @@
<script context="module" lang="ts">
import type {
SmsTemplateLocale,
SmsTemplateType,
EmailTemplateType,
EmailTemplateLocale
} from '@appwrite.io/console';
import { sdk } from '$lib/stores/sdk';
import { addNotification } from '$lib/stores/notifications';
export async function loadEmailTemplate(projectId: string, type: string, locale: string) {
try {
// TODO: fix TemplateType and TemplateLocale typing once SDK is updated
@@ -35,49 +45,43 @@
import { base } from '$app/paths';
import { CardGrid } from '$lib/components';
import { Container } from '$lib/layout';
import { sdk } from '$lib/stores/sdk';
import { addNotification } from '$lib/stores/notifications';
import EmailVerificationTemplate from './emailVerificationTemplate.svelte';
import EmailMagicUrlTemplate from './emailMagicUrlTemplate.svelte';
import EmailRecoveryTemplate from './emailRecoveryTemplate.svelte';
import EmailInviteTemplate from './emailInviteTemplate.svelte';
import Email2FaTemplate from './email2FATemplate.svelte';
import EmailSessionAlertTemplate from './emailSessionAlertTemplate.svelte';
// import SmsVerificationTemplate from './smsVerificationTemplate.svelte';
// import SmsLoginTemplate from './smsLoginTemplate.svelte';
// import { baseEmailTemplate, baseSmsTemplate, emailTemplate, smsTemplate } from './store';
import { baseEmailTemplate, emailTemplate } from './store';
import { baseEmailTemplate, emailTemplate, templates } from './store';
import { Button } from '$lib/elements/forms';
import { currentPlan } from '$lib/stores/organization';
import EmailSignature from './emailSignature.svelte';
import { isCloud } from '$lib/system';
import type {
SmsTemplateLocale,
SmsTemplateType,
EmailTemplateType,
EmailTemplateLocale
} from '@appwrite.io/console';
import { Accordion, Alert, Badge, Layout, Link, Typography } from '@appwrite.io/pink-svelte';
import { page } from '$app/state';
export let data;
let emailOpen = 'verification';
$: emailVerificationOpen = emailOpen === 'verification';
$: emailMagicSessionOpen = emailOpen === 'magicSession';
$: emailOtpSessionOpen = emailOpen === 'otpSession';
$: emailResetPassword = emailOpen === 'recovery';
$: emailInviteUser = emailOpen === 'invitation';
$: email2FAVerificationOpen = emailOpen === 'mfaChallenge';
$: emailSessionAlertOpen = emailOpen === 'sessionAlert';
let templateType = null;
let isTemplateLoading = false;
let openStates = Object.fromEntries(templates.map(({ key }) => [key, false]));
openEmail('verification');
loadTemplateFor('verification');
async function loadTemplateFor(type: string) {
// return, already loaded!
if (templateType === type) return;
templateType = type;
isTemplateLoading = true;
async function openEmail(type: string) {
type === emailOpen ? (emailOpen = null) : (emailOpen = type);
$emailTemplate = await loadEmailTemplate(page.params.project, type, 'en');
$baseEmailTemplate = { ...$emailTemplate };
isTemplateLoading = false;
}
function toggleAccordion(type: string) {
for (const key in openStates) {
openStates[key] = false;
}
openStates[type] = true;
loadTemplateFor(type);
}
</script>
@@ -109,95 +113,21 @@
Learn more
</Link.Anchor>
<svelte:fragment slot="aside">
<Layout.Stack gap="none">
<Accordion
title="Verification"
bind:open={emailVerificationOpen}
on:click={() => openEmail('verification')}>
<Layout.Stack>
<Typography.Text>
Send a verification email to users that sign in with their email and
password.
</Typography.Text>
<EmailVerificationTemplate />
</Layout.Stack>
</Accordion>
<Accordion
title="Magic URL"
bind:open={emailMagicSessionOpen}
on:click={(e) => {
e.preventDefault();
openEmail('magicSession');
}}>
<Layout.Stack>
<Typography.Text>
Send an email to users that sign in with a magic URL.
</Typography.Text>
<EmailMagicUrlTemplate />
</Layout.Stack>
</Accordion>
<Accordion
title="OTP session"
bind:open={emailOtpSessionOpen}
on:click={(e) => {
e.preventDefault();
openEmail('otpSession');
}}>
<Layout.Stack>
<Typography.Text>
Send an email to users that sign in with a email OTP.</Typography.Text>
<EmailMagicUrlTemplate />
</Layout.Stack>
</Accordion>
<Accordion
title="Reset password"
bind:open={emailResetPassword}
on:click={(e) => {
e.preventDefault();
openEmail('recovery');
}}>
<Layout.Stack>
<Typography.Text>
Send a recovery email to users that forget their password.</Typography.Text>
<EmailRecoveryTemplate /></Layout.Stack>
</Accordion>
<Accordion
title="Invite user"
bind:open={emailInviteUser}
on:click={(e) => {
e.preventDefault();
openEmail('invitation');
}}>
<Layout.Stack>
<Typography.Text>
Send an invitation email to become a member of your project.</Typography.Text>
<EmailInviteTemplate />
</Layout.Stack>
</Accordion>
<Accordion
title="2FA verification"
bind:open={email2FAVerificationOpen}
on:click={(e) => {
e.preventDefault();
openEmail('mfaChallenge');
}}>
<Layout.Stack>
<Typography.Text>
Send a two-factor authentication email to a user.</Typography.Text>
<Email2FaTemplate /></Layout.Stack>
</Accordion>
<Accordion
title="Session alert"
bind:open={emailSessionAlertOpen}
on:click={(e) => {
e.preventDefault();
openEmail('sessionAlert');
}}>
<Layout.Stack>
<Typography.Text>
Send an email to users when a new session is created.</Typography.Text>
<EmailSessionAlertTemplate /></Layout.Stack>
</Accordion>
<Layout.Stack gap="s">
{#each templates as section (section.key)}
<Accordion
title={section.title}
hideDivider={section.hideDivider}
bind:open={openStates[section.key]}
on:toggle={(event) => event.detail && toggleAccordion(section.key)}>
<Layout.Stack>
<Typography.Text>{section.description}</Typography.Text>
<svelte:component
this={section.component}
loading={isTemplateLoading} />
</Layout.Stack>
</Accordion>
{/each}
</Layout.Stack>
</svelte:fragment>
<svelte:fragment slot="actions">
@@ -9,13 +9,15 @@
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { Layout, Card } from '@appwrite.io/pink-svelte';
const projectId = page.params.project;
export let loading = false;
let locale = 'en';
let loading = false;
let isUpdating = false;
const projectId = page.params.project;
async function onLocaleChange() {
const timeout = setTimeout(() => {
loading = true;
isUpdating = true;
}, 1000);
try {
const template = await loadEmailTemplate(projectId, 'mfaChallenge', locale);
@@ -30,7 +32,7 @@
});
} finally {
clearTimeout(timeout);
loading = false;
isUpdating = false;
}
}
</script>
@@ -38,7 +40,7 @@
<Card.Base variant="secondary" padding="s">
<Layout.Stack>
<LocaleOptions on:change={onLocaleChange} bind:value={locale} />
<EmailTemplate bind:loading>
<EmailTemplate {loading} {isUpdating}>
<Id value={'{{user}}'}>{'{{user}}'}</Id>
<Id value={'{{project}}'}>{'{{project}}'}</Id>
<Id value={'{{otp}}'}>{'{{otp}}'}</Id>
@@ -9,13 +9,15 @@
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { Layout, Card } from '@appwrite.io/pink-svelte';
const projectId = page.params.project;
export let loading = false;
let locale = 'en';
let loading = false;
let isUpdating = false;
const projectId = page.params.project;
async function onLocaleChange() {
const timeout = setTimeout(() => {
loading = true;
isUpdating = true;
}, 1000);
try {
const template = await loadEmailTemplate(projectId, 'invitation', locale);
@@ -30,7 +32,7 @@
});
} finally {
clearTimeout(timeout);
loading = false;
isUpdating = false;
}
}
</script>
@@ -38,7 +40,7 @@
<Card.Base variant="secondary" padding="s">
<Layout.Stack>
<LocaleOptions on:change={onLocaleChange} bind:value={locale} />
<EmailTemplate bind:loading>
<EmailTemplate {loading} {isUpdating}>
<Id value={'{{team}}'}>{'{{team}}'}</Id>
<Id value={'{{user}}'}>{'{{user}}'}</Id>
<Id value={'{{project}}'}>{'{{project}}'}</Id>
@@ -9,14 +9,15 @@
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { Layout, Card } from '@appwrite.io/pink-svelte';
const projectId = page.params.project;
export let loading = false;
let locale = 'en';
let loading = false;
let isUpdating = false;
const projectId = page.params.project;
async function onLocaleChange() {
const timeout = setTimeout(() => {
loading = true;
isUpdating = true;
}, 1000);
try {
const template = await loadEmailTemplate(projectId, 'magicSession', locale);
@@ -32,7 +33,7 @@
} finally {
clearTimeout(timeout);
loading = false;
isUpdating = false;
}
}
</script>
@@ -40,7 +41,7 @@
<Card.Base variant="secondary" padding="s">
<Layout.Stack>
<LocaleOptions on:change={onLocaleChange} bind:value={locale} />
<EmailTemplate bind:loading>
<EmailTemplate {loading} {isUpdating}>
<Id value={'{{user}}'}>{'{{user}}'}</Id>
<Id value={'{{project}}'}>{'{{project}}'}</Id>
<Id value={'{{redirect}}'}>{'{{redirect}}'}</Id>
@@ -9,14 +9,15 @@
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { Layout, Card } from '@appwrite.io/pink-svelte';
const projectId = page.params.project;
export let loading = false;
let locale = 'en';
let loading = false;
let isUpdating = false;
const projectId = page.params.project;
async function onLocaleChange() {
const timeout = setTimeout(() => {
loading = true;
isUpdating = true;
}, 1000);
try {
const template = await loadEmailTemplate(projectId, 'recovery', locale);
@@ -30,8 +31,8 @@
message: error.message
});
} finally {
isUpdating = false;
clearTimeout(timeout);
loading = false;
}
}
</script>
@@ -39,7 +40,7 @@
<Card.Base variant="secondary" padding="s">
<Layout.Stack>
<LocaleOptions on:change={onLocaleChange} bind:value={locale} />
<EmailTemplate bind:loading>
<EmailTemplate {loading} {isUpdating}>
<Id value={'{{user}}'}>{'{{user}}'}</Id>
<Id value={'{{project}}'}>{'{{project}}'}</Id>
<Id value={'{{redirect}}'}>{'{{redirect}}'}</Id>
@@ -9,13 +9,15 @@
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { Layout, Card } from '@appwrite.io/pink-svelte';
const projectId = page.params.project;
export let loading = false;
let locale = 'en';
let loading = false;
let isUpdating = false;
const projectId = page.params.project;
async function onLocaleChange() {
const timeout = setTimeout(() => {
loading = true;
isUpdating = true;
}, 1000);
try {
const template = await loadEmailTemplate(projectId, 'sessionalert', locale);
@@ -30,7 +32,7 @@
});
} finally {
clearTimeout(timeout);
loading = false;
isUpdating = false;
}
}
</script>
@@ -38,7 +40,7 @@
<Card.Base variant="secondary" padding="s">
<Layout.Stack>
<LocaleOptions on:change={onLocaleChange} bind:value={locale} />
<EmailTemplate bind:loading>
<EmailTemplate {loading} {isUpdating}>
<Id value={'{{user}}'}>{'{{user}}'}</Id>
<Id value={'{{project}}'}>{'{{project}}'}</Id>
<Id value={'{{device}}'}>{'{{device}}'}</Id>
@@ -10,10 +10,12 @@
import type { EmailTemplateLocale, EmailTemplateType } from '@appwrite.io/console';
import { Icon, Layout, Tooltip, Typography } from '@appwrite.io/pink-svelte';
import { IconInfo } from '@appwrite.io/pink-icons-svelte';
import TemplateSkeleton from './templateSkeleton.svelte';
export let loading = false;
let openResetModal = false;
export let isUpdating = false;
let openResetModal = false;
let eventType = Submit.EmailUpdateInviteTemplate;
async function saveEmailTemplate() {
@@ -79,69 +81,70 @@
}
}
$: isButtonDisabled = deepEqual($emailTemplate, $baseEmailTemplate);
$: isSmtpEnabled = $project?.smtpEnabled;
$: isButtonDisabled = deepEqual($emailTemplate, $baseEmailTemplate);
</script>
{#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"></div>
<p class="text">Loading template...</p>
</div>
{/if}
<div class:u-opacity-0={loading} style={loading ? 'pointer-events: none' : ''}>
<div class:u-opacity-0={isUpdating} style={isUpdating ? 'pointer-events: none' : ''}>
<Form onSubmit={saveEmailTemplate}>
<Layout.Stack>
<InputText
id="senderName"
label="Sender name"
bind:value={$emailTemplate.senderName}
placeholder="Enter sender name"
disabled={!isSmtpEnabled} />
<InputEmail
bind:value={$emailTemplate.senderEmail}
id="senderEmail"
label="Sender email"
placeholder="Enter sender email"
disabled={!isSmtpEnabled} />
<InputEmail
bind:value={$emailTemplate.replyTo}
id="replyTo"
label="Reply to"
placeholder="noreply@appwrite.io" />
{#if $$slots.default}
<p class="text">
Click to copy variables for the fields below. Learn more <a
class="link"
href="https://appwrite.io/docs/advanced/platform/message-templates">here</a
>.
</p>
<Layout.Stack direction="row">
<slot />
</Layout.Stack>
{/if}
<InputText
bind:value={$emailTemplate.subject}
id="subject"
label="Subject"
placeholder="Enter subject" />
{#if loading}
<TemplateSkeleton count={3} />
{:else}
<InputText
id="senderName"
label="Sender name"
bind:value={$emailTemplate.senderName}
placeholder="Enter sender name"
disabled={!isSmtpEnabled} />
<InputTextarea
bind:value={$emailTemplate.message}
id="message"
label="Message"
placeholder="Enter your message"
readonly={!isSmtpEnabled}
rows={8}>
<Tooltip slot="info" maxWidth="15rem">
<Icon icon={IconInfo} size="s" />
<Typography.Caption variant="400" slot="tooltip">
Set up an SMTP server to edit the message body
</Typography.Caption>
</Tooltip>
</InputTextarea>
<InputEmail
bind:value={$emailTemplate.senderEmail}
id="senderEmail"
label="Sender email"
placeholder="Enter sender email"
disabled={!isSmtpEnabled} />
<InputEmail
bind:value={$emailTemplate.replyTo}
id="replyTo"
label="Reply to"
placeholder="noreply@appwrite.io" />
{#if $$slots.default}
<p class="text">
Click to copy variables for the fields below. Learn more <a
class="link"
href="https://appwrite.io/docs/advanced/platform/message-templates"
>here</a
>.
</p>
<Layout.Stack direction="row">
<slot />
</Layout.Stack>
{/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"
readonly={!isSmtpEnabled}
rows={8}>
<Tooltip slot="info" maxWidth="15rem">
<Icon icon={IconInfo} size="s" />
<Typography.Caption variant="400" slot="tooltip">
Set up an SMTP server to edit the message body
</Typography.Caption>
</Tooltip>
</InputTextarea>
{/if}
</Layout.Stack>
<div class="u-sep-block-start u-margin-block-start-24"></div>
<div class="u-flex u-gap-16 u-main-end u-margin-block-start-24">
@@ -9,14 +9,15 @@
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { Layout, Card } from '@appwrite.io/pink-svelte';
const projectId = page.params.project;
export let loading = false;
let locale = 'en';
let loading = false;
let isUpdating = false;
const projectId = page.params.project;
async function onLocaleChange() {
const timeout = setTimeout(() => {
loading = true;
isUpdating = true;
}, 1000);
try {
const template = await loadEmailTemplate(projectId, 'verification', locale);
@@ -31,7 +32,7 @@
});
} finally {
clearTimeout(timeout);
loading = false;
isUpdating = false;
}
}
</script>
@@ -39,7 +40,7 @@
<Card.Base variant="secondary" padding="s">
<Layout.Stack gap="l">
<LocaleOptions on:change={onLocaleChange} bind:value={locale} />
<EmailTemplate bind:loading>
<EmailTemplate {loading} {isUpdating}>
<Id value={'{{user}}'}>{'{{user}}'}</Id>
<Id value={'{{project}}'}>{'{{project}}'}</Id>
<Id value={'{{redirect}}'}>{'{{redirect}}'}</Id>
@@ -1,6 +1,14 @@
import { page } from '$app/stores';
import type { Models } from '@appwrite.io/console';
import { derived, writable } from 'svelte/store';
import type { Models } from '@appwrite.io/console';
// component imports
import Email2FaTemplate from './email2FATemplate.svelte';
import EmailInviteTemplate from './emailInviteTemplate.svelte';
import EmailRecoveryTemplate from './emailRecoveryTemplate.svelte';
import EmailMagicUrlTemplate from './emailMagicUrlTemplate.svelte';
import EmailVerificationTemplate from './emailVerificationTemplate.svelte';
import EmailSessionAlertTemplate from './emailSessionAlertTemplate.svelte';
export const localeCodes = derived(page, ($page) => $page.data.localeCodes as Models.LocaleCode[]);
@@ -29,8 +37,56 @@ export const smsTemplate = writable<Models.SmsTemplate>({
locale: null,
message: null
});
export const baseSmsTemplate = writable<Models.SmsTemplate>({
type: null,
locale: null,
message: null
});
export const templates = [
{
key: 'verification',
title: 'Verification',
description:
'Send a verification email to users that sign in with their email and password.',
component: EmailVerificationTemplate
},
{
key: 'magicSession',
title: 'Magic URL',
description: 'Send an email to users that sign in with a magic URL.',
component: EmailMagicUrlTemplate
},
{
key: 'otpSession',
title: 'OTP session',
description: 'Send an email to users that sign in with a email OTP.',
component: EmailMagicUrlTemplate
},
{
key: 'recovery',
title: 'Reset password',
description: 'Send a recovery email to users that forget their password.',
component: EmailRecoveryTemplate
},
{
key: 'invitation',
title: 'Invite user',
description: 'Send an invitation email to become a member of your project.',
component: EmailInviteTemplate
},
{
key: 'mfaChallenge',
title: '2FA verification',
description: 'Send a two-factor authentication email to a user.',
component: Email2FaTemplate
},
{
key: 'sessionAlert',
title: 'Session alert',
description: 'Send an email to users when a new session is created.',
component: EmailSessionAlertTemplate,
hideDivider: true
}
];
@@ -0,0 +1,22 @@
<script lang="ts">
import { Layout, Skeleton } from '@appwrite.io/pink-svelte';
export let count: number = 5;
export let textArea: boolean = true;
</script>
<Layout.Stack inline gap="xl">
{#each Array.from({ length: count }) as _}
<Layout.Stack inline gap="xs">
<Skeleton variant="line" width="25%" height={18} />
<Skeleton variant="line" width="100%" height={35} />
</Layout.Stack>
{/each}
{#if textArea}
<Layout.Stack inline gap="xs">
<Skeleton variant="line" width="25%" height={18} />
<Skeleton variant="line" width="100%" height={125} />
</Layout.Stack>
{/if}
</Layout.Stack>