Put the reply text inside question notification emails
The notification only said that the school had answered and linked into the LMS, so a student had to log in to read two lines. Delivered mail plus an unread thread was the common outcome — one such thread was a pre-sale question that never converted. The mail now carries the answer itself (capped at 2000 chars, with a pointer to the thread when longer or when files are attached). Quoted blocks are HTML-escaped, which they were not before. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -78,6 +78,8 @@ export async function POST(
|
||||
question.user.name,
|
||||
question.title,
|
||||
id,
|
||||
text.trim(),
|
||||
Boolean(safeFiles?.length),
|
||||
);
|
||||
} else {
|
||||
const staff = await prisma.user.findMany({
|
||||
|
||||
+29
-5
@@ -89,11 +89,19 @@ function btn(href: string, label: string) {
|
||||
</table>`;
|
||||
}
|
||||
|
||||
function escapeHtml(text: string) {
|
||||
return text
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """);
|
||||
}
|
||||
|
||||
function quote(text: string) {
|
||||
return `<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="margin:16px 0;">
|
||||
<tr>
|
||||
<td style="border-left:3px solid #323232;padding:10px 14px;background-color:#E8F0D8;">
|
||||
<p style="margin:0;font-family:'Courier New',Courier,monospace;font-size:13px;line-height:1.6;color:#323232;">${text.replace(/\n/g, "<br/>")}</p>
|
||||
<p style="margin:0;font-family:'Courier New',Courier,monospace;font-size:13px;line-height:1.6;color:#323232;">${escapeHtml(text).replace(/\n/g, "<br/>")}</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>`;
|
||||
@@ -335,23 +343,39 @@ export async function sendQuestionCreatedEmail(
|
||||
}).catch((e) => console.error("[email] sendQuestionCreatedEmail:", e));
|
||||
}
|
||||
|
||||
// Текст ответа кладём прямо в письмо: раньше письмо звало в LMS «прочитать ответ»,
|
||||
// и ради двух строк человеку надо было авторизоваться — часть студентов так и не
|
||||
// открывала тред (isRead=false при доставленном письме).
|
||||
const REPLY_PREVIEW_LIMIT = 2000;
|
||||
|
||||
export async function sendQuestionReplyEmail(
|
||||
to: string,
|
||||
studentName: string,
|
||||
questionTitle: string,
|
||||
questionId: string,
|
||||
replyText: string,
|
||||
hasFiles = false,
|
||||
) {
|
||||
const school = await getSchoolName();
|
||||
const trimmed = replyText.trim();
|
||||
const preview =
|
||||
trimmed.length > REPLY_PREVIEW_LIMIT
|
||||
? `${trimmed.slice(0, REPLY_PREVIEW_LIMIT)}…`
|
||||
: trimmed;
|
||||
const truncated = trimmed.length > REPLY_PREVIEW_LIMIT;
|
||||
|
||||
await getResend().emails.send({
|
||||
from: FROM,
|
||||
to,
|
||||
subject: `Ответ на ваш вопрос`,
|
||||
html: base(`
|
||||
<p ${p}>Привет, ${studentName}!</p>
|
||||
<p ${p}>Школа ответила на ваш вопрос:</p>
|
||||
${quote(questionTitle)}
|
||||
<p ${pLast}>Откройте тред чтобы прочитать ответ:</p>
|
||||
${btn(`${BASE_URL}/questions/${questionId}`, "Читать ответ")}
|
||||
<p ${p}>Школа ответила на ваш вопрос «${escapeHtml(questionTitle)}»:</p>
|
||||
${quote(preview)}
|
||||
${truncated ? `<p ${p}>Ответ длинный — целиком он в треде.</p>` : ""}
|
||||
${hasFiles ? `<p ${p}>К ответу приложены файлы — открыть их можно в треде.</p>` : ""}
|
||||
<p ${pLast}>Ответить или посмотреть переписку целиком:</p>
|
||||
${btn(`${BASE_URL}/questions/${questionId}`, "Открыть тред")}
|
||||
`, school),
|
||||
}).catch((e) => console.error("[email] sendQuestionReplyEmail:", e));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user