Implemented e-mail notification trigger.

This commit is contained in:
2026-07-18 00:01:18 +02:00
parent fab895b85e
commit b6408f3244
13 changed files with 4248 additions and 149 deletions

View File

@@ -0,0 +1,38 @@
<script setup>
import { inject } from 'vue';
import Admin from '#/components/admin/admin.vue';
const { pageProps } = inject('pageContext');
const { templates } = pageProps;
</script>
<template>
<Admin class="page">
<ul
v-if="templates.length > 0"
class="menu"
>
<li v-for="template in templates" :key="template">
<a
:href="`/admin/emails/${template}`"
class="link"
>{{ template }}</a>
</li>
</ul>
<p v-else>No e-mail templates found in components/emails.</p>
</Admin>
</template>
<style scoped>
.menu {
margin: 0;
.link {
display: block;
padding: .25rem;
}
}
</style>

View File

@@ -0,0 +1,18 @@
import { render } from 'vike/abort';
import { listEmailTemplates } from '#/src/email-templates.js';
export default function onBeforeRender(pageContext) {
if (!pageContext.user || pageContext.user.role === 'user') {
throw render(404);
}
return {
pageContext: {
title: 'E-mail Previews',
pageProps: {
templates: listEmailTemplates(),
},
},
};
}

View File

@@ -0,0 +1,70 @@
<script setup>
import { inject } from 'vue';
import Admin from '#/components/admin/admin.vue';
const { pageProps } = inject('pageContext');
const {
template,
html,
text,
error,
} = pageProps;
</script>
<template>
<Admin class="page">
<div class="header">
<h2 class="heading">{{ template }}</h2>
</div>
<p v-if="error" class="error">Failed to render template: {{ error }}</p>
<template v-else>
<iframe
:srcdoc="html"
class="frame"
title="E-mail preview"
/>
<details v-if="text" class="text">
<summary>Plain-text version</summary>
<pre>{{ text }}</pre>
</details>
</template>
</Admin>
</template>
<style scoped>
.header {
display: flex;
align-items: baseline;
gap: 1rem;
margin-bottom: 1rem;
}
.error {
color: var(--warn);
font-weight: bold;
}
.frame {
width: 100%;
min-height: 60rem;
border: solid 1px var(--shadow-weak-30);
border-radius: .5rem;
background: #fff;
}
.text {
margin-top: 1rem;
pre {
white-space: pre-wrap;
padding: 1rem;
background: var(--background-dark-20);
border-radius: .5rem;
}
}
</style>

View File

@@ -0,0 +1,38 @@
import { render } from 'vike/abort';
import { emailTemplates, listEmailTemplates } from '#/src/email-templates.js';
export default async function onBeforeRender(pageContext) {
if (!pageContext.user || pageContext.user.role === 'user') {
throw render(404);
}
const { template } = pageContext.routeParams;
if (!listEmailTemplates().includes(template)) {
throw render(404);
}
let html = null;
let text = null;
let error = null;
try {
({ html, text } = await emailTemplates.render(`${template}.vue`));
}
catch (renderError) {
error = renderError.message;
}
return {
pageContext: {
title: `E-mail Preview: ${template}`,
pageProps: {
template,
html,
text,
error,
},
},
};
}

View File

@@ -0,0 +1,18 @@
import { match } from 'path-to-regexp';
const path = '/admin/emails/:template';
const urlMatch = match(path, { decode: decodeURIComponent });
export default (pageContext) => {
const matched = urlMatch(pageContext.urlPathname);
if (matched) {
return {
routeParams: {
template: matched.params.template,
},
};
}
return false;
};