remove email from backend

This commit is contained in:
einhornimmond 2025-11-24 14:42:56 +01:00
parent 99effdc2f3
commit 9d4bda53da
45 changed files with 0 additions and 3623 deletions

View File

@ -1,50 +0,0 @@
# Using `forwardemail``email-templates` With `pug` Package
You'll find the GitHub repository of the `email-templates` package and the `pug` package here:
- [email-templates](https://github.com/forwardemail/email-templates)
- [pug](https://www.npmjs.com/package/pug)
## `pug` Documentation
The full `pug` documentation you'll find here:
- [pugjs.org](https://pugjs.org/)
### Caching Possibility
In case we are sending many emails in the future there is the possibility to cache the `pug` templates:
- [cache-pug-templates](https://github.com/ladjs/cache-pug-templates)
## Testing
To test your send emails you have different possibilities:
### In General
To send emails to yourself while developing set in `.env` the value `EMAIL_TEST_MODUS=true` and `EMAIL_TEST_RECEIVER` to your preferred email address.
### Unit Or Integration Tests
To change the behavior to show previews etc. you have the following options to be set in `sendEmailTranslated.ts` on creating the email object:
```js
const email = new Email({
// send emails in development/test env:
send: true,
// to open send emails in the browser
preview: true,
// or
// to open send emails in a specific the browser
preview: {
open: {
app: 'firefox',
wait: false,
},
},
})
```

File diff suppressed because it is too large Load Diff

View File

@ -1,149 +0,0 @@
import { createTransport } from 'nodemailer'
import { i18n } from '@test/testSetup'
import { CONFIG } from '@/config'
import { getLogger } from 'config-schema/test/testSetup'
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
import { sendEmailTranslated } from './sendEmailTranslated'
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.emails.sendEmailTranslated`)
const testMailServerHost = 'localhost'
const testMailServerPort = 1025
CONFIG.EMAIL = false
CONFIG.EMAIL_SMTP_HOST = testMailServerHost
CONFIG.EMAIL_SMTP_PORT = testMailServerPort
CONFIG.EMAIL_SENDER = 'info@gradido.net'
CONFIG.EMAIL_USERNAME = 'user'
CONFIG.EMAIL_PASSWORD = 'pwd'
CONFIG.EMAIL_TLS = true
jest.mock('nodemailer', () => {
return {
__esModule: true,
createTransport: jest.fn(() => {
return {
sendMail: () => {
return {
messageId: 'message',
}
},
}
}),
}
})
describe('sendEmailTranslated', () => {
let result: Record<string, unknown> | boolean | null
describe('config email is false', () => {
beforeEach(async () => {
result = await sendEmailTranslated({
receiver: {
to: 'receiver@mail.org',
cc: 'support@gradido.net',
},
template: 'accountMultiRegistration',
locals: {
locale: 'en',
},
})
})
it('logs warning', () => {
expect(logger.info).toBeCalledWith('Emails are disabled via config...')
})
it('returns false', () => {
expect(result).toBeFalsy()
})
})
describe('config email is true', () => {
beforeEach(async () => {
CONFIG.EMAIL = true
result = await sendEmailTranslated({
receiver: {
to: 'receiver@mail.org',
cc: 'support@gradido.net',
},
template: 'accountMultiRegistration',
locals: {
locale: 'en',
},
})
})
it('calls the transporter', () => {
expect(createTransport).toBeCalledWith({
host: testMailServerHost,
port: testMailServerPort,
secure: false,
requireTLS: true,
auth: {
user: 'user',
pass: 'pwd',
},
})
})
describe('call of "sendEmailTranslated"', () => {
it('has expected result', () => {
expect(result).toMatchObject({
originalMessage: expect.objectContaining({
to: 'receiver@mail.org',
cc: 'support@gradido.net',
from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: expect.any(Array),
subject: 'Try To Register Again With Your Email',
html: expect.stringContaining('Try To Register Again With Your Email'),
text: expect.stringContaining('TRY TO REGISTER AGAIN WITH YOUR EMAIL'),
}),
})
})
})
it.skip('calls "i18n.setLocale" with "en"', () => {
expect(i18n.setLocale).toBeCalledWith('en')
})
it.skip('calls "i18n.__" for translation', () => {
expect(i18n.__).toBeCalled()
})
})
describe('with email EMAIL_TEST_MODUS true', () => {
beforeEach(async () => {
jest.clearAllMocks()
CONFIG.EMAIL = true
CONFIG.EMAIL_TEST_MODUS = true
result = await sendEmailTranslated({
receiver: {
to: 'receiver@mail.org',
cc: 'support@gradido.net',
},
template: 'accountMultiRegistration',
locals: {
locale: 'en',
},
})
})
it('call of "sendEmailTranslated" with faked "to"', () => {
expect(result).toMatchObject({
originalMessage: expect.objectContaining({
to: CONFIG.EMAIL_TEST_RECEIVER,
cc: 'support@gradido.net',
from: `Gradido (emails.general.doNotAnswer) <${CONFIG.EMAIL_SENDER}>`,
attachments: expect.any(Array),
subject: 'Try To Register Again With Your Email',
html: expect.stringContaining('Try To Register Again With Your Email'),
text: expect.stringContaining('TRY TO REGISTER AGAIN WITH YOUR EMAIL'),
}),
})
})
})
})

View File

@ -1,118 +0,0 @@
import path from 'path'
import Email from 'email-templates'
import i18n from 'i18n'
import { createTransport } from 'nodemailer'
import { CONFIG } from '@/config'
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
import { getLogger } from 'log4js'
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.emails.sendEmailTranslated`)
export const sendEmailTranslated = async ({
receiver,
template,
locals,
}: {
receiver: {
to: string
cc?: string
}
template: string
locals: Record<string, unknown>
}): Promise<Record<string, unknown> | boolean | null> => {
// TODO: test the calling order of 'i18n.setLocale' for example: language of logging 'en', language of email receiver 'es', reset language of current user 'de'
if (!CONFIG.EMAIL) {
logger.info(`Emails are disabled via config...`)
return null
}
// because language of receiver can differ from language of current user who triggers the sending
// const rememberLocaleToRestore = i18n.getLocale()
i18n.setLocale('en') // for logging
logger.info(
`send Email: language=${locals.locale as string} to=${receiver.to.substring(0, 3)}...` +
(receiver.cc ? `, cc=${receiver.cc.substring(0, 3)}...` : '') +
`, subject=${i18n.__('emails.' + template + '.subject')}`,
)
if (CONFIG.EMAIL_TEST_MODUS) {
logger.info(
`Testmodus=ON: change receiver from ${receiver.to} to ${CONFIG.EMAIL_TEST_RECEIVER}`,
)
receiver.to = CONFIG.EMAIL_TEST_RECEIVER
}
const transport = createTransport({
host: CONFIG.EMAIL_SMTP_HOST,
port: CONFIG.EMAIL_SMTP_PORT,
secure: false, // true for 465, false for other ports
requireTLS: CONFIG.EMAIL_TLS,
auth: {
user: CONFIG.EMAIL_USERNAME,
pass: CONFIG.EMAIL_PASSWORD,
},
})
i18n.setLocale(locals.locale as string) // for email
// TESTING: see 'README.md'
const email = new Email({
message: {
from: `Gradido (${i18n.__('emails.general.doNotAnswer')}) <${CONFIG.EMAIL_SENDER}>`,
},
send: CONFIG.EMAIL,
transport,
preview: false,
// i18n, // is only needed if you don't install i18n
})
const resultSend = await email
.send({
template: path.join(__dirname, 'templates', template),
message: {
...receiver,
attachments: [
{
filename: 'gradido-header.jpeg',
path: path.join(__dirname, 'templates/includes/gradido-header.jpeg'),
cid: 'gradidoheader',
},
{
filename: 'facebook-icon.png',
path: path.join(__dirname, 'templates/includes/facebook-icon.png'),
cid: 'facebookicon',
},
{
filename: 'telegram-icon.png',
path: path.join(__dirname, 'templates/includes/telegram-icon.png'),
cid: 'telegramicon',
},
{
filename: 'twitter-icon.png',
path: path.join(__dirname, 'templates/includes/twitter-icon.png'),
cid: 'twittericon',
},
{
filename: 'youtube-icon.png',
path: path.join(__dirname, 'templates/includes/youtube-icon.png'),
cid: 'youtubeicon',
},
{
filename: 'chatbox-icon.png',
path: path.join(__dirname, 'templates/includes/chatbox-icon.png'),
cid: 'chatboxicon',
},
],
},
locals, // the 'locale' in here seems not to be used by 'email-template', because it doesn't work if the language isn't set before by 'i18n.setLocale'
})
.catch((error: unknown) => {
logger.error('Error sending notification email', error)
return error
})
return resultSend
}

View File

@ -1,623 +0,0 @@
import { ApolloServerTestClient } from 'apollo-server-testing'
import { Decimal } from 'decimal.js-light'
import { DataSource } from 'typeorm'
import { testEnvironment } from '@test/helpers'
import { i18n as localization } from '@test/testSetup'
import { getLogger } from 'config-schema/test/testSetup'
import { CONFIG } from '@/config'
import * as sendEmailTranslatedApi from './sendEmailTranslated'
import {
sendAccountActivationEmail,
sendAccountMultiRegistrationEmail,
sendAddedContributionMessageEmail,
sendContributionChangedByModeratorEmail,
sendContributionConfirmedEmail,
sendContributionDeletedEmail,
sendContributionDeniedEmail,
sendResetPasswordEmail,
sendTransactionLinkRedeemedEmail,
sendTransactionReceivedEmail,
} from './sendEmailVariants'
const testMailServerHost = 'localhost'
const testMailServerPort = 1025
const testMailTLS = false
CONFIG.EMAIL_SENDER = 'info@gradido.net'
CONFIG.EMAIL_SMTP_HOST = testMailServerHost
CONFIG.EMAIL_SMTP_PORT = testMailServerPort
CONFIG.EMAIL_TLS = testMailTLS
jest.mock('nodemailer', () => {
return {
__esModule: true,
createTransport: jest.fn(() => {
return {
sendMail: () => {
return {
messageId: 'message',
}
},
}
}),
}
})
let con: DataSource
let testEnv: {
mutate: ApolloServerTestClient['mutate']
query: ApolloServerTestClient['query']
con: DataSource
}
beforeAll(async () => {
testEnv = await testEnvironment(getLogger('apollo'), localization)
con = testEnv.con
})
afterAll(async () => {
await con.destroy()
})
const sendEmailTranslatedSpy = jest.spyOn(sendEmailTranslatedApi, 'sendEmailTranslated')
describe('sendEmailVariants', () => {
let result: any
const contributionFrontendLink =
'https://gradido.net/contributions/own-contributions/1#contributionListItem-1'
describe('sendAddedContributionMessageEmail', () => {
beforeAll(async () => {
result = await sendAddedContributionMessageEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
contributionMemo: 'My contribution.',
contributionFrontendLink,
message: 'My message.',
})
})
describe('calls "sendEmailTranslated"', () => {
it('with expected parameters', () => {
expect(sendEmailTranslatedSpy).toBeCalledWith({
receiver: {
to: 'Peter Lustig <peter@lustig.de>',
},
template: 'addedContributionMessage',
locals: {
firstName: 'Peter',
lastName: 'Lustig',
locale: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
contributionMemo: 'My contribution.',
contributionFrontendLink,
message: 'My message.',
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
},
})
})
})
describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({
originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: expect.any(Array),
subject: 'Message about your common good contribution',
html: expect.any(String),
text: expect.stringContaining('MESSAGE ABOUT YOUR COMMON GOOD CONTRIBUTION'),
}),
})
})
it('has the correct html as snapshot', () => {
expect(result.originalMessage.html).toMatchSnapshot()
})
})
})
describe('sendAccountActivationEmail', () => {
beforeAll(async () => {
result = await sendAccountActivationEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
activationLink: 'http://localhost/checkEmail/6627633878930542284',
timeDurationObject: { hours: 23, minutes: 30 },
})
})
describe('calls "sendEmailTranslated"', () => {
it('with expected parameters', () => {
expect(sendEmailTranslatedSpy).toBeCalledWith({
receiver: {
to: 'Peter Lustig <peter@lustig.de>',
},
template: 'accountActivation',
locals: {
firstName: 'Peter',
lastName: 'Lustig',
locale: 'en',
activationLink: 'http://localhost/checkEmail/6627633878930542284',
timeDurationObject: { hours: 23, minutes: 30 },
resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD,
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
communityURL: CONFIG.COMMUNITY_URL,
},
})
})
})
describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({
originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: expect.any(Array),
subject: 'Email Verification',
html: expect.any(String),
text: expect.stringContaining('EMAIL VERIFICATION'),
}),
})
})
it('has the correct html as snapshot', () => {
expect(result.originalMessage.html).toMatchSnapshot()
})
})
})
describe('sendAccountMultiRegistrationEmail', () => {
beforeAll(async () => {
result = await sendAccountMultiRegistrationEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
})
})
describe('calls "sendEmailTranslated"', () => {
it('with expected parameters', () => {
expect(sendEmailTranslatedSpy).toBeCalledWith({
receiver: {
to: 'Peter Lustig <peter@lustig.de>',
},
template: 'accountMultiRegistration',
locals: {
firstName: 'Peter',
lastName: 'Lustig',
locale: 'en',
resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD,
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
communityURL: CONFIG.COMMUNITY_URL,
},
})
})
describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({
originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: expect.any(Array),
subject: 'Try To Register Again With Your Email',
html: expect.any(String),
text: expect.stringContaining('TRY TO REGISTER AGAIN WITH YOUR EMAIL'),
}),
})
})
it('has the correct html as snapshot', () => {
expect(result.originalMessage.html).toMatchSnapshot()
})
})
})
})
describe('sendContributionConfirmedEmail', () => {
beforeAll(async () => {
result = await sendContributionConfirmedEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
contributionMemo: 'My contribution.',
contributionAmount: new Decimal(23.54),
contributionFrontendLink,
})
})
describe('calls "sendEmailTranslated"', () => {
it('with expected parameters', () => {
expect(sendEmailTranslatedSpy).toBeCalledWith({
receiver: {
to: 'Peter Lustig <peter@lustig.de>',
},
template: 'contributionConfirmed',
locals: {
firstName: 'Peter',
lastName: 'Lustig',
locale: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
contributionMemo: 'My contribution.',
contributionAmount: '23.54',
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
contributionFrontendLink,
},
})
})
})
describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({
originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: expect.any(Array),
subject: 'Your contribution to the common good was confirmed',
html: expect.any(String),
text: expect.stringContaining('YOUR CONTRIBUTION TO THE COMMON GOOD WAS CONFIRMED'),
}),
})
})
it('has the correct html as snapshot', () => {
expect(result.originalMessage.html).toMatchSnapshot()
})
})
})
describe('sendContributionChangedByModeratorEmail', () => {
beforeAll(async () => {
result = await sendContributionChangedByModeratorEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
contributionMemo: 'My contribution.',
contributionMemoUpdated: 'This is a better contribution memo.',
contributionFrontendLink,
})
})
describe('calls "sendEmailTranslated"', () => {
it('with expected parameters', () => {
expect(sendEmailTranslatedSpy).toBeCalledWith({
receiver: {
to: 'Peter Lustig <peter@lustig.de>',
},
template: 'contributionChangedByModerator',
locals: {
firstName: 'Peter',
lastName: 'Lustig',
locale: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
contributionMemo: 'My contribution.',
contributionMemoUpdated: 'This is a better contribution memo.',
contributionFrontendLink,
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
},
})
})
})
describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({
originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: expect.any(Array),
subject: 'Your common good contribution has been changed',
html: expect.any(String),
text: expect.stringContaining('YOUR COMMON GOOD CONTRIBUTION HAS BEEN CHANGED'),
}),
})
})
it('has the correct html as snapshot', () => {
expect(result.originalMessage.html).toMatchSnapshot()
})
})
})
describe('sendContributionDeniedEmail', () => {
beforeAll(async () => {
result = await sendContributionDeniedEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
contributionMemo: 'My contribution.',
contributionFrontendLink,
})
})
describe('calls "sendEmailTranslated"', () => {
it('with expected parameters', () => {
expect(sendEmailTranslatedSpy).toBeCalledWith({
receiver: {
to: 'Peter Lustig <peter@lustig.de>',
},
template: 'contributionDenied',
locals: {
firstName: 'Peter',
lastName: 'Lustig',
locale: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
contributionMemo: 'My contribution.',
contributionFrontendLink,
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
},
})
})
})
describe('result', () => {
it('has expected result', () => {
expect(result).toMatchObject({
originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: expect.any(Array),
subject: 'Your common good contribution was rejected',
html: expect.any(String),
text: expect.stringContaining('YOUR COMMON GOOD CONTRIBUTION WAS REJECTED'),
}),
})
})
it('has the correct html as snapshot', () => {
expect(result.originalMessage.html).toMatchSnapshot()
})
})
})
describe('sendContributionDeletedEmail', () => {
beforeAll(async () => {
result = await sendContributionDeletedEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
contributionMemo: 'My contribution.',
contributionFrontendLink,
})
})
describe('calls "sendEmailTranslated"', () => {
it('with expected parameters', () => {
expect(sendEmailTranslatedSpy).toBeCalledWith({
receiver: {
to: 'Peter Lustig <peter@lustig.de>',
},
template: 'contributionDeleted',
locals: {
firstName: 'Peter',
lastName: 'Lustig',
locale: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
contributionMemo: 'My contribution.',
contributionFrontendLink,
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
},
})
})
})
describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({
originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: expect.any(Array),
subject: 'Your common good contribution was deleted',
html: expect.any(String),
text: expect.stringContaining('YOUR COMMON GOOD CONTRIBUTION WAS DELETED'),
}),
})
})
it('has the correct html as snapshot', () => {
expect(result.originalMessage.html).toMatchSnapshot()
})
})
})
describe('sendResetPasswordEmail', () => {
beforeAll(async () => {
result = await sendResetPasswordEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
resetLink: 'http://localhost/reset-password/3762660021544901417',
timeDurationObject: { hours: 23, minutes: 30 },
})
})
describe('calls "sendEmailTranslated"', () => {
it('with expected parameters', () => {
expect(sendEmailTranslatedSpy).toBeCalledWith({
receiver: {
to: 'Peter Lustig <peter@lustig.de>',
},
template: 'resetPassword',
locals: {
firstName: 'Peter',
lastName: 'Lustig',
locale: 'en',
resetLink: 'http://localhost/reset-password/3762660021544901417',
timeDurationObject: { hours: 23, minutes: 30 },
resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD,
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
communityURL: CONFIG.COMMUNITY_URL,
},
})
})
})
describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({
originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: expect.any(Array),
subject: 'Reset password',
html: expect.any(String),
text: expect.stringContaining('RESET PASSWORD'),
}),
})
})
it('has the correct html as snapshot', () => {
expect(result.originalMessage.html).toMatchSnapshot()
})
})
})
describe('sendTransactionLinkRedeemedEmail', () => {
beforeAll(async () => {
result = await sendTransactionLinkRedeemedEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
senderEmail: 'bibi@bloxberg.de',
transactionMemo: 'You deserve it! 🙏🏼',
transactionAmount: new Decimal(17.65),
})
})
describe('calls "sendEmailTranslated"', () => {
it('with expected parameters', () => {
expect(sendEmailTranslatedSpy).toBeCalledWith({
receiver: {
to: 'Peter Lustig <peter@lustig.de>',
},
template: 'transactionLinkRedeemed',
locals: {
firstName: 'Peter',
lastName: 'Lustig',
locale: 'en',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
senderEmail: 'bibi@bloxberg.de',
transactionMemo: 'You deserve it! 🙏🏼',
transactionAmount: '17.65',
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
communityURL: CONFIG.COMMUNITY_URL,
},
})
})
})
describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({
originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: expect.any(Array),
subject: 'Bibi Bloxberg has redeemed your Gradido link',
html: expect.any(String),
text: expect.stringContaining('BIBI BLOXBERG HAS REDEEMED YOUR GRADIDO LINK'),
}),
})
})
it('has the correct html as snapshot', () => {
expect(result.originalMessage.html).toMatchSnapshot()
})
})
})
describe('sendTransactionReceivedEmail', () => {
beforeAll(async () => {
result = await sendTransactionReceivedEmail({
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
language: 'en',
memo: 'Du bist schon lustiger ;)',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
senderEmail: 'bibi@bloxberg.de',
transactionAmount: new Decimal(37.4),
})
})
describe('calls "sendEmailTranslated"', () => {
it('with expected parameters', () => {
expect(sendEmailTranslatedSpy).toBeCalledWith({
receiver: {
to: 'Peter Lustig <peter@lustig.de>',
},
template: 'transactionReceived',
locals: {
firstName: 'Peter',
lastName: 'Lustig',
locale: 'en',
memo: 'Du bist schon lustiger ;)',
senderFirstName: 'Bibi',
senderLastName: 'Bloxberg',
senderEmail: 'bibi@bloxberg.de',
transactionAmount: '37.40',
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
communityURL: CONFIG.COMMUNITY_URL,
},
})
})
})
describe('result', () => {
it('is the expected object', () => {
expect(result).toMatchObject({
originalMessage: expect.objectContaining({
to: 'Peter Lustig <peter@lustig.de>',
from: 'Gradido (emails.general.doNotAnswer) <info@gradido.net>',
attachments: expect.any(Array),
subject: 'Bibi Bloxberg has sent you 37.40 Gradido',
html: expect.any(String),
text: expect.stringContaining('BIBI BLOXBERG HAS SENT YOU 37.40 GRADIDO'),
}),
})
})
it('has the correct html as snapshot', () => {
expect(result.originalMessage.html).toMatchSnapshot()
})
})
})
})

View File

@ -1,225 +0,0 @@
import { Decimal } from 'decimal.js-light'
import { CONFIG } from '@/config'
import { decimalSeparatorByLanguage } from 'core'
import { sendEmailTranslated } from './sendEmailTranslated'
export interface ContributionEmailCommonData {
firstName: string
lastName: string
email: string
language: string
senderFirstName: string
senderLastName: string
contributionMemo: string
contributionFrontendLink: string
}
function toContributionEmailLocales(data: ContributionEmailCommonData): Record<string, unknown> {
return {
firstName: data.firstName,
lastName: data.lastName,
locale: data.language,
senderFirstName: data.senderFirstName,
senderLastName: data.senderLastName,
contributionMemo: data.contributionMemo,
contributionFrontendLink: data.contributionFrontendLink,
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
}
}
export const sendAddedContributionMessageEmail = (
data: ContributionEmailCommonData & {
message: string
},
): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: {
to: `${data.firstName} ${data.lastName} <${data.email}>`,
},
template: 'addedContributionMessage',
locals: {
...toContributionEmailLocales(data),
message: data.message,
},
})
}
export const sendAccountActivationEmail = (data: {
firstName: string
lastName: string
email: string
language: string
activationLink: string
timeDurationObject: Record<string, unknown>
logoUrl?: string | null
}): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'accountActivation',
locals: {
firstName: data.firstName,
lastName: data.lastName,
locale: data.language,
activationLink: data.activationLink,
timeDurationObject: data.timeDurationObject,
logoUrl: data.logoUrl,
resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD,
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
communityURL: CONFIG.COMMUNITY_URL,
},
})
}
export const sendAccountMultiRegistrationEmail = (data: {
firstName: string
lastName: string
email: string
language: string
}): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'accountMultiRegistration',
locals: {
firstName: data.firstName,
lastName: data.lastName,
locale: data.language,
resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD,
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
communityURL: CONFIG.COMMUNITY_URL,
},
})
}
export const sendContributionConfirmedEmail = (
data: ContributionEmailCommonData & {
contributionAmount: Decimal
},
): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'contributionConfirmed',
locals: {
...toContributionEmailLocales(data),
contributionAmount: decimalSeparatorByLanguage(data.contributionAmount, data.language),
},
})
}
export const sendContributionChangedByModeratorEmail = (
data: ContributionEmailCommonData & {
contributionMemoUpdated: string
},
): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'contributionChangedByModerator',
locals: {
...toContributionEmailLocales(data),
contributionMemoUpdated: data.contributionMemoUpdated,
},
})
}
export const sendContributionDeletedEmail = (
data: ContributionEmailCommonData,
): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'contributionDeleted',
locals: toContributionEmailLocales(data),
})
}
export const sendContributionDeniedEmail = (
data: ContributionEmailCommonData,
): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'contributionDenied',
locals: toContributionEmailLocales(data),
})
}
export const sendResetPasswordEmail = (data: {
firstName: string
lastName: string
email: string
language: string
resetLink: string
timeDurationObject: Record<string, unknown>
}): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'resetPassword',
locals: {
firstName: data.firstName,
lastName: data.lastName,
locale: data.language,
resetLink: data.resetLink,
timeDurationObject: data.timeDurationObject,
resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD,
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
communityURL: CONFIG.COMMUNITY_URL,
},
})
}
export const sendTransactionLinkRedeemedEmail = (data: {
firstName: string
lastName: string
email: string
language: string
senderFirstName: string
senderLastName: string
senderEmail: string
transactionMemo: string
transactionAmount: Decimal
}): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'transactionLinkRedeemed',
locals: {
firstName: data.firstName,
lastName: data.lastName,
locale: data.language,
senderFirstName: data.senderFirstName,
senderLastName: data.senderLastName,
senderEmail: data.senderEmail,
transactionMemo: data.transactionMemo,
transactionAmount: decimalSeparatorByLanguage(data.transactionAmount, data.language),
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
communityURL: CONFIG.COMMUNITY_URL,
},
})
}
export const sendTransactionReceivedEmail = (data: {
firstName: string
lastName: string
email: string
language: string
senderFirstName: string
senderLastName: string
senderEmail: string
memo: string
transactionAmount: Decimal
}): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'transactionReceived',
locals: {
firstName: data.firstName,
lastName: data.lastName,
locale: data.language,
memo: data.memo,
senderFirstName: data.senderFirstName,
senderLastName: data.senderLastName,
senderEmail: data.senderEmail,
transactionAmount: decimalSeparatorByLanguage(data.transactionAmount, data.language),
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
communityURL: CONFIG.COMMUNITY_URL,
},
})
}

View File

@ -1,18 +0,0 @@
extend ../layout.pug
block content
if logoUrl
img(src=logoUrl, alt="Banner", style="max-width: 680px; max-height: 250px;border-radius:20px")
h2= t('emails.accountActivation.title')
.text-block
include ../includes/salutation.pug
p= t('emails.accountActivation.emailRegistered')
.content
h2= t('emails.general.completeRegistration')
div(class="p_content")= t('emails.accountActivation.pleaseClickLink')
a.button-3(href=activationLink) #{t('emails.accountActivation.activateAccount')}
div(class="p_content")= t('emails.general.orCopyLink')
a.clink(href=activationLink) #{activationLink}
include ../includes/requestNewLink.pug

View File

@ -1 +0,0 @@
= t('emails.accountActivation.subject')

View File

@ -1,22 +0,0 @@
extend ../layout.pug
block content
h2= t('emails.accountMultiRegistration.title')
.text-block
include ../includes/salutation.pug
p
= t('emails.accountMultiRegistration.emailReused')
br
= t('emails.accountMultiRegistration.emailExists')
.content
h2= t('emails.resetPassword.title')
div(class="p_content")= t('emails.accountMultiRegistration.onForgottenPasswordClickLink')
a.button-3(href=resendLink) #{t('emails.general.reset')}
div(class="p_content")= t('emails.general.orCopyLink')
a.clink(href=resendLink) #{resendLink}
h2(style="color: red")= t('emails.accountMultiRegistration.contactSupport')
div(class="p_content")= t('emails.accountMultiRegistration.ifYouAreNotTheOne')
a.clink(href='mailto:' + supportEmail)= supportEmail

View File

@ -1 +0,0 @@
= t('emails.accountMultiRegistration.subject')

View File

@ -1,16 +0,0 @@
extend ../layout.pug
block content
h2= t('emails.addedContributionMessage.title')
.text-block
include ../includes/salutation.pug
p= t('emails.addedContributionMessage.commonGoodContributionMessage', { senderFirstName, senderLastName, contributionMemo })
.content
h2= t('emails.addedContributionMessage.readMessage')
div(class="p_content")
p= t('emails.addedContributionMessage.message', { message })
p= t('emails.addedContributionMessage.toSeeAndAnswerMessage')
a.button-3(href=`${contributionFrontendLink}`) #{t('emails.general.toAccount')}
include ../includes/doNotReply.pug

View File

@ -1 +0,0 @@
= t('emails.addedContributionMessage.subject')

View File

@ -1,10 +0,0 @@
extend ../layout.pug
block content
h2= t('emails.contributionChangedByModerator.title')
.text-block
include ../includes/salutation.pug
p= t('emails.contributionChangedByModerator.text', { contributionMemo, senderFirstName, senderLastName, contributionMemoUpdated })
.content
include ../includes/contributionDetailsCTA.pug
include ../includes/doNotReply.pug

View File

@ -1 +0,0 @@
= t('emails.contributionChangedByModerator.subject')

View File

@ -1,10 +0,0 @@
extend ../layout.pug
block content
h2= t('emails.contributionConfirmed.title')
.text-block
include ../includes/salutation.pug
p= t('emails.contributionConfirmed.commonGoodContributionConfirmed', { contributionMemo, senderFirstName, senderLastName, amountGDD: contributionAmount })
.content
include ../includes/contributionDetailsCTA.pug
include ../includes/doNotReply.pug

View File

@ -1 +0,0 @@
= t('emails.contributionConfirmed.subject')

View File

@ -1,10 +0,0 @@
extend ../layout.pug
block content
h2= t('emails.contributionDeleted.title')
.text-block
include ../includes/salutation.pug
p= t('emails.contributionDeleted.commonGoodContributionDeleted', { contributionMemo, senderFirstName, senderLastName })
.content
include ../includes/contributionDetailsCTA.pug
include ../includes/doNotReply.pug

View File

@ -1 +0,0 @@
= t('emails.contributionDeleted.subject')

View File

@ -1,10 +0,0 @@
extend ../layout.pug
block content
h2= t('emails.contributionDenied.title')
.text-block
include ../includes/salutation.pug
p= t('emails.contributionDenied.commonGoodContributionDenied', { contributionMemo, senderFirstName, senderLastName })
.content
include ../includes/contributionDetailsCTA.pug
include ../includes/doNotReply.pug

View File

@ -1 +0,0 @@
= t('emails.contributionDenied.subject')

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M240-400h480v-80H240v80Zm0-120h480v-80H240v80Zm0-120h480v-80H240v80ZM880-80 720-240H160q-33 0-56.5-23.5T80-320v-480q0-33 23.5-56.5T160-880h640q33 0 56.5 23.5T880-800v720ZM160-320h594l46 45v-525H160v480Zm0 0v-480 480Z"/></svg>

Before

Width:  |  Height:  |  Size: 341 B

View File

@ -1,22 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="386.729" height="196.289" viewBox="0 0 386.729 196.289">
<defs>
<linearGradient id="linear-gradient" x1="0.552" y1="-1.481" x2="0.673" y2="1.111" gradientUnits="objectBoundingBox">
<stop offset="0" stop-color="#53900c"/>
<stop offset="1" stop-color="#6e6e6e"/>
</linearGradient>
<filter id="bg_button_copy" x="0" y="0" width="386.729" height="196.289" filterUnits="userSpaceOnUse">
<feOffset dx="20" dy="20" input="SourceAlpha"/>
<feGaussianBlur stdDeviation="25" result="blur"/>
<feFlood flood-color="#383838" flood-opacity="0.306"/>
<feComposite operator="in" in2="blur"/>
<feComposite in="SourceGraphic"/>
</filter>
</defs>
<g id="Antwort_button" transform="translate(55 55)">
<g transform="matrix(1, 0, 0, 1, -55, -55)" filter="url(#bg_button_copy)">
<path id="bg_button_copy-2" data-name="bg button copy" d="M22.44,0H214.289a22.44,22.44,0,0,1,22.44,22.44v1.408a22.44,22.44,0,0,1-22.44,22.44H22.44A22.44,22.44,0,0,1,0,23.848V22.44A22.44,22.44,0,0,1,22.44,0Z" transform="translate(55 55)" fill="url(#linear-gradient)"/>
</g>
<text id="Jetzt_antworten" data-name="Jetzt antworten" transform="translate(130.252 30.144)" fill="#f5f5f5" font-size="20" font-family="WorkSans-SemiBold, Work Sans" font-weight="600"><tspan x="-79.86" y="0">Jetzt antworten</tspan></text>
<path id="Icon_answer" data-name="Icon answer" d="M83.2-870.4h9.6V-872H83.2Zm0-2.4h9.6v-1.6H83.2Zm0-2.4h9.6v-1.6H83.2ZM96-864l-3.2-3.2H81.6a1.541,1.541,0,0,1-1.13-.47A1.541,1.541,0,0,1,80-868.8v-9.6a1.541,1.541,0,0,1,.47-1.13A1.541,1.541,0,0,1,81.6-880H94.4a1.541,1.541,0,0,1,1.13.47A1.541,1.541,0,0,1,96-878.4Zm-14.4-4.8H93.48l.92.9v-10.5H81.6Zm0,0v0Z" transform="translate(-59.497 895.145)" fill="#e8eaed"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 323 B

View File

@ -1,6 +0,0 @@
span.chatbox-wrapper
img.bi-chatbox(
alt="chatbox"
loading="lazy"
src="cid:chatboxicon"
)

View File

@ -1,7 +0,0 @@
//-
h2= t('emails.general.contributionDetails')
div(class="p_content")= t('emails.contribution.toSeeContributionsAndMessages')
a.button-3(href=`${contributionFrontendLink}`) #{t('emails.general.toAccount')}
div(class="p_content")= t('emails.general.orCopyLink')
a.clink(href=`${contributionFrontendLink}`) #{`${contributionFrontendLink}`}

View File

@ -1 +0,0 @@
div(class="p_content")= t('emails.general.pleaseDoNotReply')

View File

@ -1,216 +0,0 @@
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 100;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 100;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 100;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 200;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 200;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 200;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 500;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 500;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 500;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 600;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 600;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 600;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 800;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 800;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 800;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
/* @font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 900;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_c6Dpp_k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} */
/* latin-ext */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 900;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 900;
src: url(https://fonts.gstatic.com/s/worksans/v18/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,77 +0,0 @@
footer
.w-container(class="footer_01")
.socialmedia
a.slink(
target="_blank"
href="https://www.facebook.com/groups/Gradido/"
)
img.bi-facebook(
alt="facebook"
loading="lazy"
src="cid:facebookicon"
)
a.slink(
target="_blank"
href="https://t.me/GradidoGruppe"
)
img.bi-telegram(
alt="Telegram"
loading="lazy"
src="cid:telegramicon"
)
a.slink(
target="_blank"
href="https://twitter.com/gradido"
)
img.bi-twitter(
alt="Twitter"
loading="lazy"
src="cid:twittericon"
)
a.slink(
target="_blank"
href="https://www.youtube.com/c/GradidoNet"
)
img.bi-youtube(
alt="youtube"
loading="lazy"
src="cid:youtubeicon"
)
.line
.footer
div(class="footer_p1")= t("emails.footer.contactOurSupport")
a(
class="footer_p2"
href='mailto:' + t("emails.footer.supportEmail")
)= t("emails.footer.supportEmail")
div
img.image(
alt="Gradido Logo"
src="https://gdd.gradido.net/img/brand/green.png"
)
div
a(
class="terms_of_use"
href="https://gradido.net/de/impressum/"
target="_blank"
)= t("emails.footer.imprint")
br
a(
class="terms_of_use"
href="https://gradido.net/de/datenschutz/"
target="_blank"
)= t("emails.footer.privacyPolicy")
div(class="footer_p1")
| Gradido-Akademie
br
| Institut für Wirtschaftsbionik
br
| Pfarrweg 2
br
| 74653 Künzelsau
br
| Deutschland
br
br
br

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -1,6 +0,0 @@
//- This sets the greeting at the end of every e-mail
.text-block
p
= t('emails.general.sincerelyYours')
br
= t('emails.general.yourGradidoTeam')

View File

@ -1,13 +0,0 @@
header
.head
//- TODO
//- when https://gdd.gradido.net/img/gradido-email-header.jpg is on production,
//- replace this URL by https://gdd.gradido.net/img/brand/gradido-email-header.png
img.head-logo(
alt="Gradido Logo"
loading="lazy"
src="cid:gradidoheader"
)

View File

@ -1,10 +0,0 @@
//-
requestNewLink
h2= t('emails.general.requestNewLink')
if timeDurationObject.minutes == 0
div(class="p_content")= t('emails.general.linkValidity', { hours: timeDurationObject.hours })
else
div(class="p_content")= t('emails.general.linkValidityWithMinutes', { hours: timeDurationObject.hours, minutes: timeDurationObject.minutes })
a.button-4(href=resendLink) #{t('emails.general.newLink')}

View File

@ -1 +0,0 @@
p= t('emails.general.helloName', { firstName, lastName })

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,166 +0,0 @@
body{
display: block;
font-family: "Work Sans", sans-serif;
font-size: 17px;
text-align: center;
text-align: -webkit-center;
justify-content: center;
padding: 0px;
margin: 0px;
}
h2 {
margin-top: 15px;
color: #383838;
}
.container {
max-width: 680px;
margin: 0 auto;
display: block;
}
.head-logo {
width: 100%;
height: auto;
}
.text-block {
margin-top: 20px;
color: #9ca0a8;
}
.content {
display: block;
width: 78%;
margin: 40px 1% 40px 1%;
padding: 20px 10% 40px 10%;
border-radius: 24px;
background-image: linear-gradient(180deg, #f5f5f5, #f5f5f5);
}
.p_content{
margin: 15px 0 15px 0;
line-height: 26px;
color: #696c72;
}
.clink {
line-break: anywhere;
margin-bottom: 40px;
}
.button-3,
.button-4,
.button-5 {
display: inline-block;
padding: 9px 15px;
color: white;
border: 0;
line-height: inherit;
text-decoration: none;
cursor: pointer;
border-radius: 20px;
background-image: radial-gradient(circle farthest-corner at 0% 0%, #f9cd69, #c58d38);
box-shadow: 16px 13px 35px 0 rgba(56, 56, 56, 0.3);
margin: 25px 0 25px 0;
width: 50%;
}
.button-4 {
background-image: radial-gradient(circle farthest-corner at 0% 0%, #616161, #c2c2c2);
}
.button-5 {
background: linear-gradient(135deg, #53900c, #6e6e6e);
font-size: 20px;
font-weight: 600;
color: #f5f5f5;
width: auto;
box-shadow: 20px 20px 25px;
transition: all 0.3s ease;
}
.button-5:hover {
transform: translateY(-5px);
box-shadow: 20px 25px 30px rgba(56, 56, 56, 0.4);
}
.chatbox-wrapper {
margin-right: 8px;
}
.bi-chatbox {
margin-bottom: -5px;
}
.child-right {
text-align: right;
}
.child-left {
text-align: left;
}
.socialmedia {
display: flex;
margin-top: 40px;
max-width: 600px;
}
.slink{
width: 150px;
}
.footer {
padding-bottom: 20px;
}
.footer_p1 {
margin-top: 30px;
color: #9ca0a8;
margin-bottom: 30px;
}
.footer_p2 {
color: #383838;
font-weight: bold;
}
.image {
width: 200px;
margin-top: 30px;
margin-bottom: 30px;
}
.div-block {
display: table;
margin-top: 20px;
margin-bottom: 40px;
flex-direction: row;
justify-content: center;
align-items: center;
}
.terms_of_use {
color: #9ca0a8;
}
.text-block-3 {
color: #9ca0a8;
margin-bottom: 30px;
}
.line_image,
.line {
width: 100%;
height: 13px;
margin-top: 40px;
}
.line_image {
background-image: linear-gradient(90deg, #c58d38, #c58d38 0%, #f3cd7c 35%, #dbb056 54%, #eec05f 63%, #cc9d3d);
}
.line {
background-image: linear-gradient(90deg, #c58d38, #f3cd7c 40%, #dbb056 55%, #eec05f 71%, #cc9d3d);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,26 +0,0 @@
doctype html
html(lang=locale)
head
meta(
content="multipart/html; charset=UTF-8"
http-equiv="content-type"
)
meta(
name="viewport"
content="width=device-width, initial-scale=1"
)
style.
.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}
style
include includes/email.css
include includes/webflow.css
body
div.container
include includes/header.pug
.wrapper
block content
include includes/greeting.pug
include includes/footer.pug

View File

@ -1,16 +0,0 @@
extends ../layout.pug
block content
h2= t('emails.resetPassword.title')
.text-block
include ../includes/salutation.pug
p= t('emails.resetPassword.youOrSomeoneResetPassword')
.content
h2= t('emails.resetPassword.title')
div(class="p_content")= t('emails.resetPassword.pleaseClickLink')
a.button-3(href=resetLink) #{t('emails.general.reset')}
div(class="p_content")= t('emails.general.orCopyLink')
a.clink(href=resetLink) #{resetLink}
include ../includes/requestNewLink.pug

View File

@ -1 +0,0 @@
= t('emails.resetPassword.subject')

View File

@ -1,18 +0,0 @@
extend ../layout.pug
block content
h2= t('emails.transactionLinkRedeemed.title', { senderFirstName, senderLastName })
.text-block
include ../includes/salutation.pug
p= t('emails.transactionLinkRedeemed.hasRedeemedYourLink', { senderFirstName, senderLastName, senderEmail })
.content
h2= t('emails.general.transactionDetails')
div(class="p_content")= t('emails.general.amountGDD', { amountGDD: transactionAmount })
br
= t('emails.transactionLinkRedeemed.memo', { transactionMemo })
br
= t('emails.general.detailsYouFindOnLinkToYourAccount')
a.button-3(href=`${communityURL}/transactions`) #{t('emails.general.toAccount')}
include ../includes/doNotReply.pug

View File

@ -1 +0,0 @@
= t('emails.transactionLinkRedeemed.subject', { senderFirstName, senderLastName })

View File

@ -1,30 +0,0 @@
extend ../layout.pug
block content
mixin mailto(email, subject)
- var formattedSubject = encodeURIComponent(subject)
a(class!=attributes.class href=`mailto:${email}?subject=${formattedSubject}`)
block
- var subject= t('emails.transactionReceived.replySubject', { senderFirstName, senderLastName, transactionAmount })
h2= t('emails.transactionReceived.title', { senderFirstName, senderLastName, transactionAmount })
.text-block
include ../includes/salutation.pug
p
= t('emails.transactionReceived.haveReceivedAmountGDDFrom', { transactionAmount, senderFirstName, senderLastName })
| (
+mailto(senderEmail, subject)=senderEmail
|).
.content
h2= t('emails.general.message')
.child-left
div(class="p_content")= memo
.child-right
+mailto(senderEmail, subject)(class="button-5")
include ../includes/chatbox-icon.pug
span #{t('emails.general.answerNow')}
a.button-3(href=`${communityURL}/transactions`) #{t('emails.general.toAccount')}

View File

@ -1 +0,0 @@
= t('emails.transactionReceived.subject', { senderFirstName, senderLastName, transactionAmount })