diff --git a/package-lock.json b/package-lock.json index ad8e28f..c876ac9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -80,6 +80,7 @@ "tailwindcss": "^3.4.1", "tw-elements": "^1.1.0", "typescript": "^5", + "uuid": "^9.0.1", "webpack-bundle-analyzer": "^4.10.1", "winston": "^3.11.0", "xlsx": "https://cdn.sheetjs.com/xlsx-0.19.1/xlsx-0.19.1.tgz", diff --git a/package.json b/package.json index 2d0ae8e..cedd7b8 100644 --- a/package.json +++ b/package.json @@ -97,6 +97,7 @@ "tailwindcss": "^3.4.1", "tw-elements": "^1.1.0", "typescript": "^5", + "uuid": "^9.0.1", "webpack-bundle-analyzer": "^4.10.1", "winston": "^3.11.0", "xlsx": "https://cdn.sheetjs.com/xlsx-0.19.1/xlsx-0.19.1.tgz", @@ -109,4 +110,4 @@ "depcheck": "^1.4.7", "prisma": "^5.11.0" } -} \ No newline at end of file +} diff --git a/pages/api/email.ts b/pages/api/email.ts index e77134e..cb84196 100644 --- a/pages/api/email.ts +++ b/pages/api/email.ts @@ -5,6 +5,7 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { createRouter, expressWrapper } from "next-connect"; const common = require('../../src/helpers/common'); const emailHelper = require('../../src/helpers/email'); +const { v4: uuidv4 } = require('uuid'); import fs from 'fs'; import path from 'path'; @@ -24,125 +25,60 @@ const router = createRouter(); export default async function handler(req, res) { const prisma = common.getPrismaClient(); - var action = req.query.action; + const action = req.query.action; + const emailaction = req.query.emailaction; // Retrieve and validate the JWT token - const token = await getToken({ req: req }); //response is a special action that does not require a token - if (action !== "email_response") { - if (!token) { - // If no token or invalid token, return unauthorized status - return res.status(401).json({ message: "Unauthorized to call this API endpoint" }); - } - } - - var userId = req.query.userId; - var email = req.query.email; - let date = new Date(); - - if (!userId && !email) { - return res.status(400).json({ message: "User ID or email is not provided" }); - } - // Retrieve the user - const user = await prisma.publisher.findUnique({ - where: { - id: userId, - email: email - } - }); - if (!user) { - return res.status(404).json({ message: "User not found" }); - } - var emailaction = req.query.emailaction; - switch (action) { - case "send_coverme_request": - // Send CoverMe request to the user - //get from POST data: shiftId, assignmentId, date - let shiftId = req.body.shiftId; - let assignmentId = req.body.assignmentId; - let date = req.body.date; - - console.log("User: " + user.email + " sent a CoverMe request: " + - shiftId + " " + assignmentId + " " + date); - - //get all subscribed publisers - const subscribedPublishers = await prisma.publisher.findMany({ - where: { - isSubscribedToCoverMe: true - } - }); - //send email to all subscribed publishers - for (let i = 0; i < subscribedPublishers.length; i++) { - //send email to subscribed publisher - let shift = await prisma.shift.findUnique({ - where: { - id: parseInt(shiftId) - }, - include: { - cartEvent: { - include: { - location: true - } - }, - } - } - ); - - let acceptUrl = process.env.NEXTAUTH_URL + "/api/emailActions?action=coverme_accept&userId=" + user.id + "&shiftId=" + shiftId + "&assignmentId=" + assignmentId; - - let model = { - user: user, - shiftId: shiftId, - assignmentId: assignmentId, - acceptUrl: acceptUrl, - prefix: subscribedPublishers[i].isMale ? "Брат" : "Сестра", - firstName: subscribedPublishers[i].firstName, - lastName: subscribedPublishers[i].lastName, - placeName: shift.cartEvent.location.name, - dateStr: date.toLocaleDateString(), - sentDate: date.toLocaleTimeString() - }; - emailHelper.SendEmailHandlebars(subscribedPublishers[i].email, "coverMe", model); - } - - break; - case "email_response": - //get email action - if (!emailaction) { - return res.status(400).json({ message: "Email action is not provided" }); - } - break; - default: - return res.status(400).json({ message: "Invalid action" }); - } - - - - if (action !== "email_response") { + if (action == "email_response") { switch (emailaction) { - case "coverme_accept": - // Update the user status to accepted - console.log("User: " + user.firstName + " " + user.lastName + " accepted the CoverMe request"); + case "coverMeAccept": //validate shiftId and assignmentId let shiftId = req.query.shiftId; - let assignmentId = req.query.assignmentId; - if (!shiftId || !assignmentId) { - return res.status(400).json({ message: "Shift ID or Assignment ID is not provided" }); - } - - //get the assignment - const assignment = await prisma.assignment.findUnique({ + let userId = req.query.userId; + let user = await prisma.publisher.findUnique({ where: { - id: parseInt(assignmentId) + id: userId } }); + // Update the user status to accepted + console.log("User: " + user.firstName + " " + user.lastName + " accepted the CoverMe request"); + + let assignmentPID = req.query.assignmentPID; + if (!shiftId) { + return res.status(400).json({ message: "Shift ID is not provided" }); + } + if (!assignmentPID) { + return res.status(400).json({ message: "Assignment PID is not provided" }); + } + //check if the assignment request is still open + const assignment = await prisma.assignment.findFirst({ + where: { + publicGuid: assignmentPID, + shiftId: parseInt(shiftId), + isConfirmed: false + }, + include: { + shift: { + include: { + cartEvent: { + include: { + location: true + } + } + } + } + } + }); + if (!assignment) { - return res.status(404).json({ message: "Assignment not found" }); - } - if (assignment.shiftId != parseInt(shiftId)) { - return res.status(400).json({ message: "Shift ID does not match" }); + const messagePageUrl = `/message?message=${encodeURIComponent('Някой друг вече е отговорил на рази заявка за заместване')}&type=info&caption=${encodeURIComponent('Заявката е вече обработена')}`; + res.redirect(messagePageUrl); + return; } + emailHelper.SendEmail_NewShifts(user, [assignment.shift]); + // await prisma.user.update({ // where: { // id: parseInt(userId) @@ -189,13 +125,109 @@ export default async function handler(req, res) { // const emailResponse = await common.sendEmail(user.email, "Email Action Processed", // "Your email action was processed successfully"); } - return res.status(200).json({ message: "User action processed" }); -} + else { + const token = await getToken({ req: req }); + if (!token) { + // If no token or invalid token, return unauthorized status + return res.status(401).json({ message: "Unauthorized to call this API endpoint" }); + } + + const user = await prisma.publisher.findUnique({ + where: { + email: token.email + } + }); + + switch (action) { + case "sendCoverMeRequestByEmail": + // Send CoverMe request to the user + //get from POST data: shiftId, assignmentId, date + //let shiftId = req.body.shiftId; + let assignmentId = req.body.assignmentId; + let date = req.body.date; + + console.log("User: " + user.email + " sent a 'CoverMe' request for his assignment " + assignmentId + " " + date); + + let assignment = await prisma.assignment.findUnique({ + where: { + id: parseInt(assignmentId) + }, + include: { + shift: { + include: { + cartEvent: { + include: { + location: true + } + } + } + } + } + }); + + // update the assignment. generate new publicGuid, isConfirmed to false + let newPublicGuid = uuidv4(); + await prisma.assignment.update({ + where: { + id: parseInt(assignmentId) + }, + data: { + publicGuid: newPublicGuid, // if this exists, we consider the request open + isConfirmed: false + } + }); + + //get all subscribed publisers + const subscribedPublishers = await prisma.publisher.findMany({ + where: { + isSubscribedToCoverMe: true + } + }); + //send email to all subscribed publishers + for (let i = 0; i < subscribedPublishers.length; i++) { + if (subscribedPublishers[i].id == user.id) { + continue; + } + + //send email to subscribed publisher + let acceptUrl = process.env.NEXTAUTH_URL + "/api/email?action=email_response&emailaction=coverMeAccept&userId=" + subscribedPublishers[i].id + "&shiftId=" + assignment.shiftId + "&assignmentPID=" + newPublicGuid; + + let model = { + user: user, + shiftId: assignment.shiftId, + acceptUrl: acceptUrl, + prefix: user.isMale ? "Брат" : "Сестра", + firstName: subscribedPublishers[i].firstName, + lastName: subscribedPublishers[i].lastName, + placeName: assignment.shift.cartEvent.location.name, + dateStr: common.getDateFormated(assignment.shift.startTime), + time: common.formatTimeHHmm(assignment.shift.startTime), + sentDate: common.getDateFormated(new Date()) + }; + let results = emailHelper.SendEmailHandlebars( + { + name: subscribedPublishers[i].firstName + " " + subscribedPublishers[i].lastName, + email: subscribedPublishers[i].email + }, "coverMe", model); + // if (results) { + // console.log("Error sending email: " + error); + // return res.status(500).json({ message: "Error sending email:" + error }); + //} + + if (results) { + console.log("Email sent to: " + subscribedPublishers[i].email); + } + + } + break; + default: + return res.status(400).json({ message: "Invalid action" }); + } + + return res.status(200).json({ message: "User action processed" }); + } + +} router.use(expressWrapper(handler)); - - - - - diff --git a/pages/cart/publishers/myschedule.tsx b/pages/cart/publishers/myschedule.tsx index 273dc56..ad93c97 100644 --- a/pages/cart/publishers/myschedule.tsx +++ b/pages/cart/publishers/myschedule.tsx @@ -51,6 +51,23 @@ export default function MySchedulePage({ assignments }) { console.log("error", error); }); }; + + const searchReplacement = (assignmentId) => { + axiosInstance.post('/api/email?action=sendCoverMeRequestByEmail', { + assignmentId: assignmentId, + }).then(response => { + console.log("response", response); + //toast success and confirm the change + toast.success("Заявката за заместник е изпратена!", { + onClose: () => { + window.location.reload(); + } + }); + }).catch(error => { + console.log("error", error); + }); + } + return ( @@ -101,14 +118,14 @@ export default function MySchedulePage({ assignments }) { setIsModalOpen(true) }} > - Заместник + Избери Заместник - {/* */} + diff --git a/pages/message.tsx b/pages/message.tsx new file mode 100644 index 0000000..a3502a3 --- /dev/null +++ b/pages/message.tsx @@ -0,0 +1,27 @@ +// pages/message.js + +import { useRouter } from 'next/router'; +import Layout from "../components/layout"; + +export default function MessagePage() { + const router = useRouter(); + const messageStyles = { + error: "text-red-500", + warning: "text-yellow-500", + info: "text-blue-500", + }; + const { message, type = messageStyles.info, caption } = router.query; + + return ( + +
+
+

{caption || 'Информация'}

+

+ {message || 'Така ще получавате различни съобщения.'} +

+
+
+
+ ); +} diff --git a/src/helpers/common.js b/src/helpers/common.js index 210119e..a9a8b24 100644 --- a/src/helpers/common.js +++ b/src/helpers/common.js @@ -525,7 +525,9 @@ exports.getCurrentYearMonth = () => { const month = String(currentDate.getMonth() + 1).padStart(2, '0'); // Month is 0-indexed return `${year}-${month}`; } - +exports.getTimeFormated = function (date) { + return this.formatTimeHHmm(date); +} // format date to 'HH:mm' time string required by the time picker exports.formatTimeHHmm = function (input) { // Check if the input is a string or a Date object @@ -729,3 +731,7 @@ exports.getLocalStorage = function (key, defaultValue) { } return defaultValue; }; + +exports.root = function (req) { + return process.env.NEXT_PUBLIC_PUBLIC_URL; +} diff --git a/src/helpers/email.js b/src/helpers/email.js index 09c4e82..288dc4b 100644 --- a/src/helpers/email.js +++ b/src/helpers/email.js @@ -1,10 +1,12 @@ // helper module to send emails with nodemailer const fs = require("fs"); +const path = require('path'); const { MailtrapClient } = require("mailtrap"); const nodemailer = require("nodemailer"); const CON = require("./const"); const CAL = require("./calendar"); +const Handlebars = require('handlebars'); // const { google } = require("googleapis"); // const OAuth2 = google.auth.OAuth2; @@ -12,14 +14,42 @@ const CAL = require("./calendar"); const { Shift, Publisher, PrismaClient } = require("@prisma/client"); const TOKEN = process.env.TOKEN || "a7d7147a530235029d74a4c2f228e6ad"; -const SENDER_EMAIL = "pw@d-popov.com"; -const sender = { name: "JW Cart: Shift Info", email: SENDER_EMAIL }; +const SENDER_EMAIL = "sofia@mwitnessing.com"; +const sender = { name: "Специално Свидетелстване София", email: SENDER_EMAIL }; const client = new MailtrapClient({ token: TOKEN }); -const mailtrapTestClient = new MailtrapClient({ - username: '8ec69527ff2104',//not working now - password: 'c7bc05f171c96c' -}); +let mailtrapTestClient = null; +// const mailtrapTestClient = new MailtrapClient({ +// username: '8ec69527ff2104',//not working now +// password: 'c7bc05f171c96c' +// }); +//test +var transporter = nodemailer.createTransport({ + host: "sandbox.smtp.mailtrap.io", + port: 2525, + auth: { + user: "8ec69527ff2104", + pass: "c7bc05f171c96c" + } +}); +// production +// var transporter = nodemailer.createTransport({ +// host: "live.smtp.mailtrap.io", +// port: 587, +// auth: { +// user: "api", +// pass: "1cfe82e747b8dc3390ed08bb16e0f48d" +// } +// }); + +var transporterBulk = nodemailer.createTransport({ + host: "bulk.smtp.mailtrap.io", + port: 587, + auth: { + user: "api", + pass: "1cfe82e747b8dc3390ed08bb16e0f48d" + } +}); // ------------------ Email sending ------------------ var lastResult = null; function setResult(result) { @@ -29,91 +59,173 @@ exports.GetLastResult = function () { return lastResult; }; -exports.SendEmail = async function (to, subject, text, html) { +exports.SendEmail = async function (to, subject, text, html, attachments = []) { + let sender = '"Специално Свидетелстване София - тест" '; + to = Array.isArray(to) ? to : [to]; + const emailAddresses = to.map(item => `"${item.name}" <${item.email}>`).join(', '); + const message = { from: sender, - to, + to: emailAddresses, subject, text, html, + attachments }; + + if (mailtrapTestClient !== null) { + // Assuming mailtrapTestClient is correctly set up to send emails + await mailtrapTestClient + .send(message) + .then(console.log) + .catch(console.error); + + } else { + + let result = await transporter + .sendMail(message) + .then(console.log) + .catch(console.error); + return result; + } + }; -exports.SendEmailHandlebars = async function (to, templateName, model) { - // Ensure the sender and mailtrapTestClient are correctly defined or imported +exports.SendEmailHandlebars = async function (to, templateName, model, attachments = []) { + try { + // Ensure the sender and mailtrapTestClient are correctly defined or imported - // Load and compile the main template - const mainTemplateSource = fs.readFileSync(path.join(__dirname, 'src', 'templates', 'emails', 'main.hbs'), 'utf8'); - const mainTemplate = Handlebars.compile(mainTemplateSource); + // Load and compile the main template + const mainTemplateSource = fs.readFileSync(path.join(process.cwd(), 'src', 'templates', 'emails', 'main.hbs'), 'utf8'); + const mainTemplate = Handlebars.compile(mainTemplateSource); - // Dynamically load and compile the specified template - const templateSource = fs.readFileSync(path.join(__dirname, 'src', 'templates', 'emails', `${templateName}.hbs`), 'utf8'); + // Dynamically load and compile the specified template + const templateSource = fs.readFileSync(path.join(process.cwd(), 'src', 'templates', 'emails', `${templateName}.hbs`), 'utf8'); - // Extract subject and optional text version from the template source - const subjectMatch = templateSource.match(/{{!-- Subject: (.*) --}}/); - const textMatch = templateSource.match(/{{!-- Text: ([\s\S]*?) --}}/); + // Extract subject and optional text version from the template source + const subjectMatch = templateSource.match(/{{!-- Subject: (.*) --}}/); + const textMatch = templateSource.match(/{{!-- Text: ([\s\S]*?) --}}/); - const subject = subjectMatch ? subjectMatch[1].trim() : 'Default Subject'; - const textVersion = textMatch ? textMatch[1].trim() : null; + const subject = subjectMatch ? subjectMatch[1].trim() : 'Default Subject'; + const textVersion = textMatch ? textMatch[1].trim() : null; - // Remove the subject and text annotations from the template source - const cleanTemplateSource = templateSource.replace(/{{!-- Subject: .* --}}/, '').replace(/{{!-- Text: [\s\S]*? --}}/, ''); + // Remove the subject and text annotations from the template source + const cleanTemplateSource = templateSource.replace(/{{!-- Subject: .* --}}/, '').replace(/{{!-- Text: [\s\S]*? --}}/, ''); - // Compile the cleaned template - const template = Handlebars.compile(cleanTemplateSource); + // Compile the cleaned template + const template = Handlebars.compile(cleanTemplateSource); - // Render the specified template with the provided model - const templateHtml = template(model); + // Render the specified template with the provided model + const templateHtml = template(model); - // Render the main template, inserting the specific template HTML - const html = mainTemplate({ body: templateHtml }); + // Render the main template, inserting the specific template HTML + const html = mainTemplate({ body: templateHtml }); - // Generate a plain text version if not explicitly provided - const text = textVersion || html.replace(/<[^>]*>?/gm, ''); // Simple regex to strip HTML tags. Might need refinement. + // Generate a plain text version if not explicitly provided + const text = textVersion || html.replace(/<[^>]*>?/gm, ''); // Simple regex to strip HTML tags. Might need refinement. - const message = { - from: sender, // Ensure 'sender' is defined - to, - subject, - text, - html, - }; + let results = this.SendEmail(to, subject, text, html, attachments); + return results; - // Assuming mailtrapTestClient is correctly set up to send emails - await mailtrapTestClient - .send(message) - .then(console.log) - .catch(console.error); + } catch (error) { + console.error(error); + return new Error('Error sending email'); + } }; -exports.SendEmail_Test = async function (to, subject, text, html) { - const message = { - from: sender, - to, - subject, - text, - html, + +exports.SendEmail_NewShifts = async function (publisher, shifts) { + if (shifts.length === 0) return; + + var date = new Date(shifts[0].startTime); + + // Generate ICS calendar links for all shifts + const icsLink = CAL.GenerateICS(shifts); + + // Prepare the shifts string + const shiftStr = shifts.map((s) => { + return `${CON.weekdaysBG[s.startTime.getDay()]} ${CON.GetDateFormat(s.startTime)} at ${s.cartEvent.location.name} from ${CON.GetTimeFormat(s.startTime)} to ${CON.GetTimeFormat(s.endTime)}`; + }).join("
"); + + // Define the model for the Handlebars template + const model = { + publisherFirstName: publisher.firstName, + publisherLastName: publisher.lastName, + month: CON.monthNamesBG[date.getMonth()], + shifts: shiftStr, + sentDate: new Date().toLocaleDateString() // Assuming you want to include the sent date in the email }; - await mailtrapTestClient - .send(message) - .then(console.log, console.error, setResult); -} + // Call the refactored function to send the email with Handlebars template rendering + await exports.SendEmailHandlebars( + publisher.email, // Assuming the publisher's email is to be used + "newShifts", // The name of your Handlebars template for new shifts notification + model, + [{ + filename: "calendar.ics", + content: icsLink, + contentType: 'text/calendar' // Ensure this is correctly set for the ICS file + }] + ).then(console.log).catch(console.error); +}; + + + + + + + +//----------------------- OLD ----------------------------- + +// exports.SendEmail_NewShifts = async function (publisher, shifts) { +// if (shifts.length == 0) return; + +// var date = new Date(shifts[0].startTime); + +// //generate ICS calendar links for all shifts +// const icsLink = CAL.GenerateICS(shifts); + +// const shftStr = shifts +// .map((s) => { +// return ` ${CON.weekdaysBG[s.startTime.getDay()] +// } ${CON.GetDateFormat(s.startTime)} ${s.cartEvent.location.name +// } ${CON.GetTimeFormat(s.startTime)} - ${CON.GetTimeFormat( +// s.endTime +// )}`; +// }) +// .join("\n"); + +// await client.send({ +// from: sender, +// to: [ +// { +// email: "dobromir.popov@gmail.com",//publisher.email, +// name: publisher.firstName + " " + publisher.lastName, +// }, +// ], +// subject: "[CCC]: вашите смени през " + CON.monthNamesBG[date.getMonth()], +// text: +// "Здравейте, " + publisher.firstName + " " + publisher.lastName + "!\n\n" + +// "Ти регистриран да получавате известия за нови смени на количка.\n" + +// `За месец ${CON.monthNamesBG[date.getMonth()]} имате следните смени:\n` + +// ` ${shftStr} \n\n\n` + +// "Поздрави,\n" + +// "Специално Свидетелстване София", +// attachments: [ +// { +// filename: "calendar.ics", +// content_id: "calendar.ics", +// disposition: "inline", +// content: icsLink, +// }, +// ], +// }) +// .then(console.log, console.error, setResult); +// }; // https://mailtrap.io/blog/sending-emails-with-nodemailer/ -exports.SendTestEmail = async function (to) { - // await client - // .send({ - // from: sender, - // to: [{ email: RECIPIENT_EMAIL }], - // subject: "Hello from Mailtrap!", - // text: "Welcome to Mailtrap Sending!",Shift Info" - // }) - // .then(console.log, console.error, setResult); - - // return lastResult; - +exports.SendEmail_Example = async function (to) { const welcomeImage = fs.readFileSync( path.join(CON.contentPath, "welcome.png") ); @@ -160,50 +272,3 @@ exports.SendTestEmail = async function (to) { }) .then(console.log, console.error, setResult); }; - - -exports.SendEmail_NewShifts = async function (publisher, shifts) { - if (shifts.length == 0) return; - - var date = new Date(shifts[0].startTime); - - //generate ICS calendar links for all shifts - const icsLink = CAL.GenerateICS(shifts); - - const shftStr = shifts - .map((s) => { - return ` ${CON.weekdaysBG[s.startTime.getDay()] - } ${CON.GetDateFormat(s.startTime)} ${s.cartEvent.location.name - } ${CON.GetTimeFormat(s.startTime)} - ${CON.GetTimeFormat( - s.endTime - )}`; - }) - .join("\n"); - - await client.send({ - from: sender, - to: [ - { - email: "dobromir.popov@gmail.com",//publisher.email, - name: publisher.firstName + " " + publisher.lastName, - }, - ], - subject: "[CCC]: вашите смени през " + CON.monthNamesBG[date.getMonth()], - text: - "Здравейте, " + publisher.firstName + " " + publisher.lastName + "!\n\n" + - "Ти регистриран да получавате известия за нови смени на количка.\n" + - `За месец ${CON.monthNamesBG[date.getMonth()]} имате следните смени:\n` + - ` ${shftStr} \n\n\n` + - "Поздрави,\n" + - "Специално Свидетелстване София", - attachments: [ - { - filename: "calendar.ics", - content_id: "calendar.ics", - disposition: "inline", - content: icsLink, - }, - ], - }) - .then(console.log, console.error, setResult); -}; diff --git a/src/templates/emails/coverMe copy.hbs b/src/templates/emails/coverMe copy.hbs new file mode 100644 index 0000000..e300167 --- /dev/null +++ b/src/templates/emails/coverMe copy.hbs @@ -0,0 +1,24 @@ +{{!-- Subject: ССС: Нужен е заместник--}} +{{!-- Text: Plain text version of your email. If not provided, HTML tags will be stripped from the HTML version for the +text version. --}} + +
+

Търси се зместник за смяна на {{placeName}} за {{dateStr}}!

+

Здравейте,

+

{{prefix}} {{firstName}} {{lastName}} търси заместник.

+ {{!--

Shift Details:

--}} +

Дата: {{dateStr}}
Час: {{time}}
Място: {{placeName}}

+

С натискането на бутона по-долу можете да премете да го замествате. Вие, той/тя и останалите участници в смяната + ще бъдат уведумени чрез имейл за промяната. Вашата помощ е много ценна.

+

+ Ще + поема смяната +

+ {{!--

Thank you very much for considering my request.

+

Best regards,
{{name}}

--}} +
+
+

Изпратено на: {{sentDate}}

+
\ No newline at end of file diff --git a/src/templates/emails/coverMe.hbs b/src/templates/emails/coverMe.hbs index 90a58e8..8f238fe 100644 --- a/src/templates/emails/coverMe.hbs +++ b/src/templates/emails/coverMe.hbs @@ -1,24 +1,24 @@ -{{!-- Subject: Your email subject here --}} -{{!-- Text: Plain text version of your email. If not provided, HTML tags will be stripped from the HTML version for the -text version. --}} +{{!-- Subject: ССС: Нужен е заместник--}}
-

{{firstName}} {{lastName}} търси зместник за {{placeName}}!

-

Здравейте,

-

{{prefix}} {{firstName}} {{lastName}} търси заместник за своята смяна на {{dateStr}} на {{placeName}}.

+

Търси се зместник за смяна на {{placeName}} за {{dateStr}}!

+

Здравей {{firstName}},

+

{{prefix}} {{user.firstName}} {{user.lastName}} търси заместник.

{{!--

Shift Details:

--}} - {{!--

Date: {{date}}
Time: {{time}}
Location: {{placeName}}

--}} -

С натискането на бутона по-долу можете да премете да го замествате. Вие, той/тя и останалите участници в смяната - ще бъдат уведумени чрез имейл за промяната. Вашата помощ е високо ценена.

+

Дата: {{dateStr}}
Час: {{time}}
Място: {{placeName}}

+

С натискането на бутона по-долу можеш да премеш да го заместваш. + {{!-- Ти, той/тя и останалите участници в смяната ще + получат имейл за промяната. Твоята помощ е много ценна. --}} +

Приеми - смяната + style="background-color: #4CAF50; color: white; padding: 10px 20px; text-decoration: none; display: inline-block; border-radius: 5px;">Ще + поема смяната

{{!--

Thank you very much for considering my request.

Best regards,
{{name}}

--}}
-

Изпратено на: {{sentDate}}

+

Изпратено до {{firstName}} {{lastName}} на {{sentDate}}

\ No newline at end of file diff --git a/src/templates/emails/main.hbs b/src/templates/emails/main.hbs index 80249dd..d9959a4 100644 --- a/src/templates/emails/main.hbs +++ b/src/templates/emails/main.hbs @@ -10,7 +10,7 @@
-

Company Name

+

Cпециално Свидетелстване София

@@ -18,7 +18,7 @@
- © 2024 Company Name. All rights reserved. + © 2024 ССС. All rights reserved.
diff --git a/src/templates/emails/newShifts.hbs b/src/templates/emails/newShifts.hbs new file mode 100644 index 0000000..32a1508 --- /dev/null +++ b/src/templates/emails/newShifts.hbs @@ -0,0 +1,14 @@ +{{!-- Subject: ССС: Нужен е заместник--}} + +
+

Здравейте, {{publisherFirstName}} {{publisherLastName}}!

+

Ти регистриран да получавате известия за нови смени на количка.

+

За месец {{month}} имате следните смени:

+
+ {{{shifts}}} +
+
+ + \ No newline at end of file