newsletter CRUD & pdf
This commit is contained in:
parent
e9b29f8acf
commit
01ce3fdd39
31 changed files with 1185 additions and 23 deletions
43
package-lock.json
generated
43
package-lock.json
generated
|
@ -20,6 +20,7 @@
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"node-schedule": "^2.1.1",
|
"node-schedule": "^2.1.1",
|
||||||
"nodemailer": "^6.9.14",
|
"nodemailer": "^6.9.14",
|
||||||
|
"pdf-lib": "^1.17.1",
|
||||||
"puppeteer": "^23.11.1",
|
"puppeteer": "^23.11.1",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
|
@ -177,6 +178,24 @@
|
||||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@pdf-lib/standard-fonts": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"pako": "^1.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@pdf-lib/upng": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"pako": "^1.0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pkgjs/parseargs": {
|
"node_modules/@pkgjs/parseargs": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
|
@ -2666,6 +2685,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
|
||||||
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw=="
|
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/pako": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||||
|
"license": "(MIT AND Zlib)"
|
||||||
|
},
|
||||||
"node_modules/parent-module": {
|
"node_modules/parent-module": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||||
|
@ -2766,6 +2791,24 @@
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz",
|
||||||
"integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA=="
|
"integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/pdf-lib": {
|
||||||
|
"version": "1.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz",
|
||||||
|
"integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@pdf-lib/standard-fonts": "^1.0.0",
|
||||||
|
"@pdf-lib/upng": "^1.0.1",
|
||||||
|
"pako": "^1.0.11",
|
||||||
|
"tslib": "^1.11.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pdf-lib/node_modules/tslib": {
|
||||||
|
"version": "1.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||||
|
"license": "0BSD"
|
||||||
|
},
|
||||||
"node_modules/pend": {
|
"node_modules/pend": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"node-schedule": "^2.1.1",
|
"node-schedule": "^2.1.1",
|
||||||
"nodemailer": "^6.9.14",
|
"nodemailer": "^6.9.14",
|
||||||
|
"pdf-lib": "^1.17.1",
|
||||||
"puppeteer": "^23.11.1",
|
"puppeteer": "^23.11.1",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
|
|
18
src/command/newsletterCommand.ts
Normal file
18
src/command/newsletterCommand.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
export interface CreateNewsletterCommand {
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SynchronizeNewsletterCommand {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
newsletterTitle: string;
|
||||||
|
newsletterText: string;
|
||||||
|
newsletterSignatur: string;
|
||||||
|
recipientsByQueryId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendNewsletterCommand {
|
||||||
|
id: number;
|
||||||
|
isSent: boolean;
|
||||||
|
}
|
73
src/command/newsletterCommandHandler.ts
Normal file
73
src/command/newsletterCommandHandler.ts
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import { dataSource } from "../data-source";
|
||||||
|
import { newsletter } from "../entity/newsletter";
|
||||||
|
import InternalException from "../exceptions/internalException";
|
||||||
|
import { CreateNewsletterCommand, SendNewsletterCommand, SynchronizeNewsletterCommand } from "./newsletterCommand";
|
||||||
|
|
||||||
|
export default abstract class NewsletterCommandHandler {
|
||||||
|
/**
|
||||||
|
* @description create newsletter
|
||||||
|
* @param CreateNewsletterCommand
|
||||||
|
* @returns {Promise<number>}
|
||||||
|
*/
|
||||||
|
static async create(createNewsletter: CreateNewsletterCommand): Promise<number> {
|
||||||
|
return await dataSource
|
||||||
|
.createQueryBuilder()
|
||||||
|
.insert()
|
||||||
|
.into(newsletter)
|
||||||
|
.values({
|
||||||
|
title: createNewsletter.title,
|
||||||
|
})
|
||||||
|
.execute()
|
||||||
|
.then((result) => {
|
||||||
|
return result.identifiers[0].id;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("Failed creating newsletter", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description sync newsletter
|
||||||
|
* @param SynchronizeNewsletterCommand
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
static async sync(syncNewsletter: SynchronizeNewsletterCommand): Promise<void> {
|
||||||
|
return await dataSource
|
||||||
|
.createQueryBuilder()
|
||||||
|
.update(newsletter)
|
||||||
|
.set({
|
||||||
|
title: syncNewsletter.title,
|
||||||
|
description: syncNewsletter.description,
|
||||||
|
newsletterTitle: syncNewsletter.newsletterTitle,
|
||||||
|
newsletterText: syncNewsletter.newsletterText,
|
||||||
|
newsletterSignatur: syncNewsletter.newsletterSignatur,
|
||||||
|
recipientsByQueryId: syncNewsletter.recipientsByQueryId,
|
||||||
|
})
|
||||||
|
.where("id = :id", { id: syncNewsletter.id })
|
||||||
|
.execute()
|
||||||
|
.then(() => {})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("Failed synching newsletter", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description send newsletter
|
||||||
|
* @param SendNewsletterCommand
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
static async send(syncNewsletter: SendNewsletterCommand): Promise<void> {
|
||||||
|
return await dataSource
|
||||||
|
.createQueryBuilder()
|
||||||
|
.update(newsletter)
|
||||||
|
.set({
|
||||||
|
isSent: syncNewsletter.isSent,
|
||||||
|
})
|
||||||
|
.where("id = :id", { id: syncNewsletter.id })
|
||||||
|
.execute()
|
||||||
|
.then(() => {})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("Failed setting newsletter send state", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
10
src/command/newsletterDatesCommand.ts
Normal file
10
src/command/newsletterDatesCommand.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export interface SynchronizeNewsletterDatesCommand {
|
||||||
|
newsletterId: number;
|
||||||
|
dates: Array<NewsletterDateCommand>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NewsletterDateCommand {
|
||||||
|
calendarId: number;
|
||||||
|
diffTitle?: string;
|
||||||
|
diffDescription?: string;
|
||||||
|
}
|
95
src/command/newsletterDatesCommandHandler.ts
Normal file
95
src/command/newsletterDatesCommandHandler.ts
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
import { DeleteResult, EntityManager, InsertResult, UpdateResult } from "typeorm";
|
||||||
|
import { dataSource } from "../data-source";
|
||||||
|
import InternalException from "../exceptions/internalException";
|
||||||
|
import NewsletterDatesService from "../service/newsletterDatesService";
|
||||||
|
import { NewsletterDateCommand, SynchronizeNewsletterDatesCommand } from "./newsletterDatesCommand";
|
||||||
|
import { newsletterDates } from "../entity/newsletterDates";
|
||||||
|
|
||||||
|
export default abstract class NewsletterDatesCommandHandler {
|
||||||
|
/**
|
||||||
|
* @description sync newsletter dates
|
||||||
|
* @param {SynchronizeNewsletterDatesCommand} syncNewsletterDates
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
static async sync(syncNewsletterDates: SynchronizeNewsletterDatesCommand): Promise<void> {
|
||||||
|
let currentDates = await NewsletterDatesService.getAll(syncNewsletterDates.newsletterId);
|
||||||
|
|
||||||
|
return await dataSource.manager
|
||||||
|
.transaction(async (manager) => {
|
||||||
|
let newDates = syncNewsletterDates.dates.filter(
|
||||||
|
(r) => !currentDates.map((np) => np.calendarId).includes(r.calendarId)
|
||||||
|
);
|
||||||
|
let removeDates = currentDates.filter(
|
||||||
|
(r) => !syncNewsletterDates.dates.map((np) => np.calendarId).includes(r.calendarId)
|
||||||
|
);
|
||||||
|
let keptDates = currentDates.filter((r) =>
|
||||||
|
syncNewsletterDates.dates.map((np) => np.calendarId).includes(r.calendarId)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (newDates.length != 0) {
|
||||||
|
await this.syncPresenceAdd(manager, syncNewsletterDates.newsletterId, newDates);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removeDates.length != 0) {
|
||||||
|
await this.syncPresenceRemove(manager, syncNewsletterDates.newsletterId, removeDates);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const date of keptDates) {
|
||||||
|
await this.syncPresenceUpdate(manager, syncNewsletterDates.newsletterId, date);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => {})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("Failed syncing newsletter dates", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async syncPresenceAdd(
|
||||||
|
manager: EntityManager,
|
||||||
|
newsletterId: number,
|
||||||
|
dates: Array<NewsletterDateCommand>
|
||||||
|
): Promise<InsertResult> {
|
||||||
|
return await manager
|
||||||
|
.createQueryBuilder()
|
||||||
|
.insert()
|
||||||
|
.into(newsletterDates)
|
||||||
|
.values(
|
||||||
|
dates.map((d) => ({
|
||||||
|
...d,
|
||||||
|
newsletterId: newsletterId,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async syncPresenceUpdate(
|
||||||
|
manager: EntityManager,
|
||||||
|
newsletterId: number,
|
||||||
|
date: NewsletterDateCommand
|
||||||
|
): Promise<UpdateResult> {
|
||||||
|
return await manager
|
||||||
|
.createQueryBuilder()
|
||||||
|
.update(newsletterDates)
|
||||||
|
.set({
|
||||||
|
diffTitle: date.diffTitle,
|
||||||
|
diffDescription: date.diffDescription,
|
||||||
|
})
|
||||||
|
.where("calendarId = :calendarId", { calendarId: date.calendarId })
|
||||||
|
.andWhere("newsletterId = :newsletterId", { newsletterId })
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async syncPresenceRemove(
|
||||||
|
manager: EntityManager,
|
||||||
|
newsletterId: number,
|
||||||
|
dates: Array<NewsletterDateCommand>
|
||||||
|
): Promise<DeleteResult> {
|
||||||
|
return await manager
|
||||||
|
.createQueryBuilder()
|
||||||
|
.delete()
|
||||||
|
.from(newsletterDates)
|
||||||
|
.where("calendarId IN (:...ids)", { ids: dates.map((d) => d.calendarId) })
|
||||||
|
.andWhere("newsletterId = :newsletterId", { newsletterId })
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
}
|
9
src/command/newsletterRecipientsCommand.ts
Normal file
9
src/command/newsletterRecipientsCommand.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
export interface SynchronizeNewsletterRecipientsCommand {
|
||||||
|
newsletterId: number;
|
||||||
|
recipients: Array<NewsletterRecipientCommand>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NewsletterRecipientCommand {
|
||||||
|
memberId: number;
|
||||||
|
addedManually: boolean;
|
||||||
|
}
|
94
src/command/newsletterRecipientsCommandHandler.ts
Normal file
94
src/command/newsletterRecipientsCommandHandler.ts
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
import { DeleteResult, EntityManager, InsertResult, UpdateResult } from "typeorm";
|
||||||
|
import { dataSource } from "../data-source";
|
||||||
|
import InternalException from "../exceptions/internalException";
|
||||||
|
import NewsletterRecipientsService from "../service/newsletterRecipientsService";
|
||||||
|
import { NewsletterRecipientCommand, SynchronizeNewsletterRecipientsCommand } from "./newsletterRecipientsCommand";
|
||||||
|
import { newsletterRecipients } from "../entity/newsletterRecipients";
|
||||||
|
|
||||||
|
export default abstract class NewsletterRecipientsCommandHandler {
|
||||||
|
/**
|
||||||
|
* @description sync newsletterRecipients
|
||||||
|
* @param {SynchronizeNewsletterRecipientsCommand} syncNewsletterRecipients
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
static async sync(syncNewsletterRecipients: SynchronizeNewsletterRecipientsCommand): Promise<void> {
|
||||||
|
let currentRecipients = await NewsletterRecipientsService.getAll(syncNewsletterRecipients.newsletterId);
|
||||||
|
|
||||||
|
return await dataSource.manager
|
||||||
|
.transaction(async (manager) => {
|
||||||
|
let newRecipients = syncNewsletterRecipients.recipients.filter(
|
||||||
|
(r) => !currentRecipients.map((np) => np.memberId).includes(r.memberId)
|
||||||
|
);
|
||||||
|
let removeRecipients = currentRecipients.filter(
|
||||||
|
(r) => !syncNewsletterRecipients.recipients.map((np) => np.memberId).includes(r.memberId)
|
||||||
|
);
|
||||||
|
let keptRecipients = currentRecipients.filter((r) =>
|
||||||
|
syncNewsletterRecipients.recipients.map((np) => np.memberId).includes(r.memberId)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (newRecipients.length != 0) {
|
||||||
|
await this.syncPresenceAdd(manager, syncNewsletterRecipients.newsletterId, newRecipients);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removeRecipients.length != 0) {
|
||||||
|
await this.syncPresenceRemove(manager, syncNewsletterRecipients.newsletterId, removeRecipients);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const recipient of keptRecipients) {
|
||||||
|
await this.syncPresenceUpdate(manager, syncNewsletterRecipients.newsletterId, recipient);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => {})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("Failed syncing newsletter recipients", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async syncPresenceAdd(
|
||||||
|
manager: EntityManager,
|
||||||
|
newsletterId: number,
|
||||||
|
recipients: Array<NewsletterRecipientCommand>
|
||||||
|
): Promise<InsertResult> {
|
||||||
|
return await manager
|
||||||
|
.createQueryBuilder()
|
||||||
|
.insert()
|
||||||
|
.into(newsletterRecipients)
|
||||||
|
.values(
|
||||||
|
recipients.map((d) => ({
|
||||||
|
...d,
|
||||||
|
newsletterId: newsletterId,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async syncPresenceUpdate(
|
||||||
|
manager: EntityManager,
|
||||||
|
newsletterId: number,
|
||||||
|
recipient: NewsletterRecipientCommand
|
||||||
|
): Promise<UpdateResult> {
|
||||||
|
return await manager
|
||||||
|
.createQueryBuilder()
|
||||||
|
.update(newsletterRecipients)
|
||||||
|
.set({
|
||||||
|
addedManually: recipient.addedManually,
|
||||||
|
})
|
||||||
|
.where("memberId = :memberId", { memberId: recipient.memberId })
|
||||||
|
.andWhere("newsletterId = :newsletterId", { newsletterId })
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async syncPresenceRemove(
|
||||||
|
manager: EntityManager,
|
||||||
|
newsletterId: number,
|
||||||
|
recipients: Array<NewsletterRecipientCommand>
|
||||||
|
): Promise<DeleteResult> {
|
||||||
|
return await manager
|
||||||
|
.createQueryBuilder()
|
||||||
|
.delete()
|
||||||
|
.from(newsletterRecipients)
|
||||||
|
.where("memberId IN (:...ids)", { ids: recipients.map((d) => d.memberId) })
|
||||||
|
.andWhere("newsletterId = :newsletterId", { newsletterId })
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
}
|
229
src/controller/admin/newsletterController.ts
Normal file
229
src/controller/admin/newsletterController.ts
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
import { Request, Response } from "express";
|
||||||
|
import NewsletterService from "../../service/newsletterService";
|
||||||
|
import NewsletterFactory from "../../factory/admin/newsletter";
|
||||||
|
import NewsletterDatesService from "../../service/newsletterDatesService";
|
||||||
|
import NewsletterDatesFactory from "../../factory/admin/newsletterDates";
|
||||||
|
import NewsletterRecipientsService from "../../service/newsletterRecipientsService";
|
||||||
|
import NewsletterRecipientsFactory from "../../factory/admin/newsletterRecipients";
|
||||||
|
import { FileSystemHelper } from "../../helpers/fileSystemHelper";
|
||||||
|
import { CreateNewsletterCommand, SynchronizeNewsletterCommand } from "../../command/newsletterCommand";
|
||||||
|
import NewsletterCommandHandler from "../../command/newsletterCommandHandler";
|
||||||
|
import { SynchronizeNewsletterDatesCommand } from "../../command/newsletterDatesCommand";
|
||||||
|
import NewsletterDatesCommandHandler from "../../command/newsletterDatesCommandHandler";
|
||||||
|
import { SynchronizeNewsletterRecipientsCommand } from "../../command/newsletterRecipientsCommand";
|
||||||
|
import NewsletterRecipientsCommandHandler from "../../command/newsletterRecipientsCommandHandler";
|
||||||
|
import { NewsletterDatesViewModel } from "../../viewmodel/admin/newsletterDates.models";
|
||||||
|
import { NewsletterRecipientsViewModel } from "../../viewmodel/admin/newsletterRecipients.models";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get all newsletters
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function getAllNewsletters(req: Request, res: Response): Promise<any> {
|
||||||
|
let offset = parseInt((req.query.offset as string) ?? "0");
|
||||||
|
let count = parseInt((req.query.count as string) ?? "25");
|
||||||
|
let [newsletters, total] = await NewsletterService.getAll(offset, count);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
newsletters: NewsletterFactory.mapToBase(newsletters),
|
||||||
|
total: total,
|
||||||
|
offset: offset,
|
||||||
|
count: count,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get newsletter by id
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function getNewsletterById(req: Request, res: Response): Promise<any> {
|
||||||
|
let id = parseInt(req.params.id);
|
||||||
|
let newsletter = await NewsletterService.getById(id);
|
||||||
|
|
||||||
|
res.json(NewsletterFactory.mapToSingle(newsletter));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get newsletter dates by id
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function getNewsletterDatesById(req: Request, res: Response): Promise<any> {
|
||||||
|
let newsletterId = parseInt(req.params.newsletterId);
|
||||||
|
|
||||||
|
let dates = await NewsletterDatesService.getAll(newsletterId);
|
||||||
|
|
||||||
|
res.json(NewsletterDatesFactory.mapToBase(dates));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get newsletter recipientss by id
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function getNewsletterRecipientsById(req: Request, res: Response): Promise<any> {
|
||||||
|
let newsletterId = parseInt(req.params.newsletterId);
|
||||||
|
|
||||||
|
let recipientss = await NewsletterRecipientsService.getAll(newsletterId);
|
||||||
|
|
||||||
|
res.json(NewsletterRecipientsFactory.mapToBase(recipientss));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get newsletter printouts by id
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function getNewsletterPrintoutsById(req: Request, res: Response): Promise<any> {
|
||||||
|
let newsletterId = parseInt(req.params.newsletterId);
|
||||||
|
|
||||||
|
let newsletter = await NewsletterService.getById(newsletterId);
|
||||||
|
|
||||||
|
let folderPath = FileSystemHelper.formatPath("export", "newsletter", `${newsletter.id}_${newsletter.title}`);
|
||||||
|
let filesInFolder = FileSystemHelper.getFilesInDirectory(folderPath);
|
||||||
|
|
||||||
|
res.json(filesInFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get newsletter printout by id and print
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function getNewsletterPrintoutByIdAndPrint(req: Request, res: Response): Promise<any> {
|
||||||
|
let newsletterId = parseInt(req.params.newsletterId);
|
||||||
|
let filename = req.params.filename;
|
||||||
|
|
||||||
|
let newsletter = await NewsletterService.getById(newsletterId);
|
||||||
|
|
||||||
|
let filepath = FileSystemHelper.formatPath("export", "newsletter", `${newsletter.id}_${newsletter.title}`, filename);
|
||||||
|
|
||||||
|
res.sendFile(process.cwd() + filepath, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/pdf",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description create newsletter
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function createNewsletter(req: Request, res: Response): Promise<any> {
|
||||||
|
let title = req.body.title;
|
||||||
|
|
||||||
|
let createNewsletter: CreateNewsletterCommand = {
|
||||||
|
title,
|
||||||
|
};
|
||||||
|
let id = await NewsletterCommandHandler.create(createNewsletter);
|
||||||
|
|
||||||
|
res.send(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description create newsletter printout by id
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function createNewsletterPrintoutById(req: Request, res: Response): Promise<any> {
|
||||||
|
let newsletterId = parseInt(req.params.newsletterId);
|
||||||
|
let newsletter = await NewsletterService.getById(newsletterId);
|
||||||
|
let dates = await NewsletterDatesService.getAll(newsletterId);
|
||||||
|
let recipients = await NewsletterRecipientsService.getAll(newsletterId);
|
||||||
|
|
||||||
|
// print newsletter pdf for every member having newsletter type configured to print or if all members get printout
|
||||||
|
// check if all users have mail or adress
|
||||||
|
// squash all files to single for printing
|
||||||
|
// use Helper for Newsletter printing and mail sending
|
||||||
|
|
||||||
|
res.sendStatus(204);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description synchronize newsletter by id
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function synchronizeNewsletterById(req: Request, res: Response): Promise<any> {
|
||||||
|
let id = parseInt(req.params.id);
|
||||||
|
let title = req.body.title;
|
||||||
|
let description = req.body.description;
|
||||||
|
let newsletterTitle = req.body.newsletterTitle;
|
||||||
|
let newsletterText = req.body.newsletterText;
|
||||||
|
let newsletterSignatur = req.body.newsletterSignatur;
|
||||||
|
let recipientsByQueryId = req.body.recipientsByQueryId ?? null;
|
||||||
|
|
||||||
|
let syncNewsletter: SynchronizeNewsletterCommand = {
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
newsletterTitle,
|
||||||
|
newsletterText,
|
||||||
|
newsletterSignatur,
|
||||||
|
recipientsByQueryId,
|
||||||
|
};
|
||||||
|
await NewsletterCommandHandler.sync(syncNewsletter);
|
||||||
|
|
||||||
|
if (recipientsByQueryId) {
|
||||||
|
// TODO! set all recipients to query selection
|
||||||
|
}
|
||||||
|
|
||||||
|
res.sendStatus(204);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description synchronize newsletter dates by id
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function synchronizeNewsletterDatesById(req: Request, res: Response): Promise<any> {
|
||||||
|
let newsletterId = parseInt(req.params.newsletterId);
|
||||||
|
let dates = req.body.dates as Array<NewsletterDatesViewModel>;
|
||||||
|
|
||||||
|
let syncDates: SynchronizeNewsletterDatesCommand = {
|
||||||
|
newsletterId,
|
||||||
|
dates: dates.map((d) => ({
|
||||||
|
calendarId: d.calendarId,
|
||||||
|
diffTitle: d.diffTitle,
|
||||||
|
diffDescription: d.diffDescription,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
await NewsletterDatesCommandHandler.sync(syncDates);
|
||||||
|
|
||||||
|
res.sendStatus(204);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description synchronize newsletter recipients by id
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function synchronizeNewsletterRecipientsById(req: Request, res: Response): Promise<any> {
|
||||||
|
let newsletterId = parseInt(req.params.newsletterId);
|
||||||
|
let recipients = req.body.recipients as Array<NewsletterRecipientsViewModel>;
|
||||||
|
|
||||||
|
let syncRecipients: SynchronizeNewsletterRecipientsCommand = {
|
||||||
|
newsletterId,
|
||||||
|
recipients: recipients.map((r) => ({
|
||||||
|
memberId: r.memberId,
|
||||||
|
addedManually: r.addedManually,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
await NewsletterRecipientsCommandHandler.sync(syncRecipients);
|
||||||
|
|
||||||
|
res.sendStatus(204);
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ import ProtocolPrintoutService from "../../service/protocolPrintoutService";
|
||||||
import ProtocolPrintoutFactory from "../../factory/admin/protocolPrintout";
|
import ProtocolPrintoutFactory from "../../factory/admin/protocolPrintout";
|
||||||
import { CreateProtocolPrintoutCommand } from "../../command/protocolPrintoutCommand";
|
import { CreateProtocolPrintoutCommand } from "../../command/protocolPrintoutCommand";
|
||||||
import ProtocolPrintoutCommandHandler from "../../command/protocolPrintoutCommandHandler";
|
import ProtocolPrintoutCommandHandler from "../../command/protocolPrintoutCommandHandler";
|
||||||
|
import { FileSystemHelper } from "../../helpers/fileSystemHelper";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description get all protocols
|
* @description get all protocols
|
||||||
|
@ -240,6 +241,7 @@ export async function createProtocolPrintoutById(req: Request, res: Response): P
|
||||||
template: "protocol",
|
template: "protocol",
|
||||||
title,
|
title,
|
||||||
filename,
|
filename,
|
||||||
|
folder: "protocol",
|
||||||
data: {
|
data: {
|
||||||
title: protocol.title,
|
title: protocol.title,
|
||||||
summary: protocol.summary,
|
summary: protocol.summary,
|
||||||
|
@ -262,7 +264,7 @@ export async function createProtocolPrintoutById(req: Request, res: Response): P
|
||||||
let printout: CreateProtocolPrintoutCommand = {
|
let printout: CreateProtocolPrintoutCommand = {
|
||||||
title,
|
title,
|
||||||
iteration: iteration + 1,
|
iteration: iteration + 1,
|
||||||
filename,
|
filename: FileSystemHelper.formatPath("protocol", filename),
|
||||||
protocolId,
|
protocolId,
|
||||||
};
|
};
|
||||||
await ProtocolPrintoutCommandHandler.create(printout);
|
await ProtocolPrintoutCommandHandler.create(printout);
|
||||||
|
|
|
@ -55,6 +55,10 @@ import { template } from "./entity/template";
|
||||||
import { Template1734854680201 } from "./migrations/1734854680201-template";
|
import { Template1734854680201 } from "./migrations/1734854680201-template";
|
||||||
import { templateUsage } from "./entity/templateUsage";
|
import { templateUsage } from "./entity/templateUsage";
|
||||||
import { TemplateUsage1734949173739 } from "./migrations/1734949173739-templateUsage";
|
import { TemplateUsage1734949173739 } from "./migrations/1734949173739-templateUsage";
|
||||||
|
import { newsletter } from "./entity/newsletter";
|
||||||
|
import { newsletterDates } from "./entity/newsletterDates";
|
||||||
|
import { newsletterRecipients } from "./entity/newsletterRecipients";
|
||||||
|
import { Newsletter1735118780511 } from "./migrations/1735118780511-newsletter";
|
||||||
|
|
||||||
const dataSource = new DataSource({
|
const dataSource = new DataSource({
|
||||||
type: DB_TYPE as any,
|
type: DB_TYPE as any,
|
||||||
|
@ -96,6 +100,9 @@ const dataSource = new DataSource({
|
||||||
query,
|
query,
|
||||||
template,
|
template,
|
||||||
templateUsage,
|
templateUsage,
|
||||||
|
newsletter,
|
||||||
|
newsletterDates,
|
||||||
|
newsletterRecipients,
|
||||||
memberView,
|
memberView,
|
||||||
memberExecutivePositionsView,
|
memberExecutivePositionsView,
|
||||||
memberQualificationsView,
|
memberQualificationsView,
|
||||||
|
@ -120,6 +127,7 @@ const dataSource = new DataSource({
|
||||||
MemberDataViews1734520998539,
|
MemberDataViews1734520998539,
|
||||||
Template1734854680201,
|
Template1734854680201,
|
||||||
TemplateUsage1734949173739,
|
TemplateUsage1734949173739,
|
||||||
|
Newsletter1735118780511,
|
||||||
],
|
],
|
||||||
migrationsRun: true,
|
migrationsRun: true,
|
||||||
migrationsTransactionMode: "each",
|
migrationsTransactionMode: "each",
|
||||||
|
|
42
src/entity/newsletter.ts
Normal file
42
src/entity/newsletter.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import { Column, Entity, ManyToOne, OneToMany, PrimaryColumn } from "typeorm";
|
||||||
|
import { newsletterDates } from "./newsletterDates";
|
||||||
|
import { member } from "./member";
|
||||||
|
import { newsletterRecipients } from "./newsletterRecipients";
|
||||||
|
import { query } from "./query";
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class newsletter {
|
||||||
|
@PrimaryColumn({ generated: "increment", type: "int" })
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column({ type: "varchar", length: 255 })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@Column({ type: "varchar", length: 255, default: "" })
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
@Column({ type: "varchar", length: 255, default: "" })
|
||||||
|
newsletterTitle: string;
|
||||||
|
|
||||||
|
@Column({ type: "text", default: "" })
|
||||||
|
newsletterText: string;
|
||||||
|
|
||||||
|
@Column({ type: "varchar", length: 255, default: "" })
|
||||||
|
newsletterSignatur: string;
|
||||||
|
|
||||||
|
@Column({ type: "boolean", default: false })
|
||||||
|
isSent: boolean;
|
||||||
|
|
||||||
|
@OneToMany(() => newsletterDates, (dates) => dates.newsletter)
|
||||||
|
dates: newsletterDates[];
|
||||||
|
|
||||||
|
@OneToMany(() => newsletterRecipients, (recipient) => recipient.newsletter)
|
||||||
|
recipients: newsletterRecipients[];
|
||||||
|
|
||||||
|
@ManyToOne(() => query, {
|
||||||
|
nullable: true,
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
onUpdate: "RESTRICT",
|
||||||
|
})
|
||||||
|
recipientsByQuery?: query;
|
||||||
|
}
|
32
src/entity/newsletterDates.ts
Normal file
32
src/entity/newsletterDates.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { Column, Entity, ManyToOne, PrimaryColumn } from "typeorm";
|
||||||
|
import { newsletter } from "./newsletter";
|
||||||
|
import { calendar } from "./calendar";
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class newsletterDates {
|
||||||
|
@PrimaryColumn({ type: "int" })
|
||||||
|
newsletterId: number;
|
||||||
|
|
||||||
|
@PrimaryColumn({ type: "int" })
|
||||||
|
calendarId: number;
|
||||||
|
|
||||||
|
@Column({ type: "varchar", length: 255, nullable: true })
|
||||||
|
diffTitle: string | null;
|
||||||
|
|
||||||
|
@Column({ type: "text", nullable: true })
|
||||||
|
diffDescription: string | null;
|
||||||
|
|
||||||
|
@ManyToOne(() => newsletter, (newsletter) => newsletter.dates, {
|
||||||
|
nullable: false,
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
onUpdate: "RESTRICT",
|
||||||
|
})
|
||||||
|
newsletter: newsletter;
|
||||||
|
|
||||||
|
@ManyToOne(() => calendar, {
|
||||||
|
nullable: false,
|
||||||
|
onDelete: "RESTRICT",
|
||||||
|
onUpdate: "RESTRICT",
|
||||||
|
})
|
||||||
|
calendar: calendar;
|
||||||
|
}
|
29
src/entity/newsletterRecipients.ts
Normal file
29
src/entity/newsletterRecipients.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { Column, Entity, ManyToOne, PrimaryColumn } from "typeorm";
|
||||||
|
import { newsletter } from "./newsletter";
|
||||||
|
import { member } from "./member";
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class newsletterRecipients {
|
||||||
|
@PrimaryColumn({ type: "int" })
|
||||||
|
newsletterId: number;
|
||||||
|
|
||||||
|
@PrimaryColumn({ type: "int" })
|
||||||
|
memberId: number;
|
||||||
|
|
||||||
|
@Column({ type: "boolean", default: false })
|
||||||
|
addedManually: boolean;
|
||||||
|
|
||||||
|
@ManyToOne(() => newsletter, (newsletter) => newsletter.recipients, {
|
||||||
|
nullable: false,
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
onUpdate: "RESTRICT",
|
||||||
|
})
|
||||||
|
newsletter: newsletter;
|
||||||
|
|
||||||
|
@ManyToOne(() => member, {
|
||||||
|
nullable: false,
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
onUpdate: "RESTRICT",
|
||||||
|
})
|
||||||
|
member: member;
|
||||||
|
}
|
32
src/factory/admin/newsletter.ts
Normal file
32
src/factory/admin/newsletter.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { newsletter } from "../../entity/newsletter";
|
||||||
|
import { NewsletterViewModel } from "../../viewmodel/admin/newsletter.models";
|
||||||
|
import QueryStoreFactory from "./queryStore";
|
||||||
|
|
||||||
|
export default abstract class NewsletterFactory {
|
||||||
|
/**
|
||||||
|
* @description map record to newsletter
|
||||||
|
* @param {newsletter} record
|
||||||
|
* @returns {NewsletterViewModel}
|
||||||
|
*/
|
||||||
|
public static mapToSingle(record: newsletter): NewsletterViewModel {
|
||||||
|
return {
|
||||||
|
id: record.id,
|
||||||
|
title: record.title,
|
||||||
|
description: record.description,
|
||||||
|
newsletterTitle: record.newsletterTitle,
|
||||||
|
newsletterText: record.newsletterText,
|
||||||
|
newsletterSignatur: record.newsletterSignatur,
|
||||||
|
isSent: record.isSent,
|
||||||
|
recipientsByQuery: record?.recipientsByQuery ? QueryStoreFactory.mapToSingle(record.recipientsByQuery) : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description map records to newsletter
|
||||||
|
* @param {Array<newsletter>} records
|
||||||
|
* @returns {Array<NewsletterViewModel>}
|
||||||
|
*/
|
||||||
|
public static mapToBase(records: Array<newsletter>): Array<NewsletterViewModel> {
|
||||||
|
return records.map((r) => this.mapToSingle(r));
|
||||||
|
}
|
||||||
|
}
|
30
src/factory/admin/newsletterDates.ts
Normal file
30
src/factory/admin/newsletterDates.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { newsletterDates } from "../../entity/newsletterDates";
|
||||||
|
import { NewsletterDatesViewModel } from "../../viewmodel/admin/newsletterDates.models";
|
||||||
|
import CalendarFactory from "./calendar";
|
||||||
|
import MemberFactory from "./member";
|
||||||
|
|
||||||
|
export default abstract class NewsletterDatesFactory {
|
||||||
|
/**
|
||||||
|
* @description map record to newsletterDates
|
||||||
|
* @param {newsletterDates} record
|
||||||
|
* @returns {NewsletterDatesViewModel}
|
||||||
|
*/
|
||||||
|
public static mapToSingle(record: newsletterDates): NewsletterDatesViewModel {
|
||||||
|
return {
|
||||||
|
newsletterId: record.newsletterId,
|
||||||
|
calendarId: record.calendarId,
|
||||||
|
diffTitle: record.diffTitle,
|
||||||
|
diffDescription: record.diffDescription,
|
||||||
|
calendar: CalendarFactory.mapToSingle(record.calendar),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description map records to newsletterDates
|
||||||
|
* @param {Array<newsletterDates>} records
|
||||||
|
* @returns {Array<NewsletterDatesViewModel>}
|
||||||
|
*/
|
||||||
|
public static mapToBase(records: Array<newsletterDates>): Array<NewsletterDatesViewModel> {
|
||||||
|
return records.map((r) => this.mapToSingle(r));
|
||||||
|
}
|
||||||
|
}
|
28
src/factory/admin/newsletterRecipients.ts
Normal file
28
src/factory/admin/newsletterRecipients.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { newsletterRecipients } from "../../entity/newsletterRecipients";
|
||||||
|
import { NewsletterRecipientsViewModel } from "../../viewmodel/admin/newsletterRecipients.models";
|
||||||
|
import MemberFactory from "./member";
|
||||||
|
|
||||||
|
export default abstract class NewsletterRecipientsFactory {
|
||||||
|
/**
|
||||||
|
* @description map record to newsletterRecipients
|
||||||
|
* @param {newsletterRecipients} record
|
||||||
|
* @returns {NewsletterRecipientsViewModel}
|
||||||
|
*/
|
||||||
|
public static mapToSingle(record: newsletterRecipients): NewsletterRecipientsViewModel {
|
||||||
|
return {
|
||||||
|
newsletterId: record.newsletterId,
|
||||||
|
memberId: record.memberId,
|
||||||
|
addedManually: record.addedManually,
|
||||||
|
member: MemberFactory.mapToSingle(record.member),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description map records to newsletterRecipients
|
||||||
|
* @param {Array<newsletterRecipients>} records
|
||||||
|
* @returns {Array<NewsletterRecipientsViewModel>}
|
||||||
|
*/
|
||||||
|
public static mapToBase(records: Array<newsletterRecipients>): Array<NewsletterRecipientsViewModel> {
|
||||||
|
return records.map((r) => this.mapToSingle(r));
|
||||||
|
}
|
||||||
|
}
|
31
src/helpers/fileSystemHelper.ts
Normal file
31
src/helpers/fileSystemHelper.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
||||||
|
import { join } from "path";
|
||||||
|
import { readdirSync } from "fs";
|
||||||
|
|
||||||
|
export abstract class FileSystemHelper {
|
||||||
|
static createFolder(newFolder: string) {
|
||||||
|
const exportPath = join(process.cwd(), "export", newFolder);
|
||||||
|
if (!existsSync(exportPath)) {
|
||||||
|
mkdirSync(exportPath, { recursive: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static readFile(filePath: string) {
|
||||||
|
return readFileSync(join(process.cwd(), filePath), "utf8");
|
||||||
|
}
|
||||||
|
|
||||||
|
static writeFile(filePath: string, file: any) {
|
||||||
|
writeFileSync(filePath, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static formatPath(...args: string[]) {
|
||||||
|
return join(...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getFilesInDirectory(directoryPath: string, filetype?: string): string[] {
|
||||||
|
const fullPath = join(process.cwd(), directoryPath);
|
||||||
|
return readdirSync(fullPath, { withFileTypes: true })
|
||||||
|
.filter((dirent) => !dirent.isDirectory() && (!filetype || dirent.name.endsWith(filetype)))
|
||||||
|
.map((dirent) => dirent.name);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
import puppeteer from "puppeteer";
|
import puppeteer from "puppeteer";
|
||||||
import { TemplateHelper } from "./templateHelper";
|
import { TemplateHelper } from "./templateHelper";
|
||||||
import { PermissionModule } from "../type/permissionTypes";
|
import { PermissionModule } from "../type/permissionTypes";
|
||||||
|
import { FileSystemHelper } from "./fileSystemHelper";
|
||||||
|
import { PDFDocument } from "pdf-lib";
|
||||||
|
|
||||||
export abstract class PdfExport {
|
export abstract class PdfExport {
|
||||||
static async renderFile({
|
static async renderFile({
|
||||||
|
@ -10,6 +12,7 @@ export abstract class PdfExport {
|
||||||
data = {},
|
data = {},
|
||||||
saveToDisk = true,
|
saveToDisk = true,
|
||||||
margins = { top: "15mm", bottom: "15mm" },
|
margins = { top: "15mm", bottom: "15mm" },
|
||||||
|
folder = "",
|
||||||
}: {
|
}: {
|
||||||
template: PermissionModule;
|
template: PermissionModule;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
@ -17,7 +20,10 @@ export abstract class PdfExport {
|
||||||
data?: any;
|
data?: any;
|
||||||
saveToDisk?: boolean;
|
saveToDisk?: boolean;
|
||||||
margins?: { top: string; bottom: string };
|
margins?: { top: string; bottom: string };
|
||||||
|
folder?: string;
|
||||||
}) {
|
}) {
|
||||||
|
if (folder != "") FileSystemHelper.createFolder(folder);
|
||||||
|
|
||||||
const { header, footer, body } = await TemplateHelper.renderFileForModule({
|
const { header, footer, body } = await TemplateHelper.renderFileForModule({
|
||||||
module: template,
|
module: template,
|
||||||
bodyData: data,
|
bodyData: data,
|
||||||
|
@ -31,8 +37,10 @@ export abstract class PdfExport {
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
await page.setContent(body, { waitUntil: "domcontentloaded" });
|
await page.setContent(body, { waitUntil: "domcontentloaded" });
|
||||||
|
|
||||||
|
const exportPath = FileSystemHelper.formatPath(process.cwd(), "export", folder, `${filename}.pdf`);
|
||||||
|
|
||||||
let pdf = await page.pdf({
|
let pdf = await page.pdf({
|
||||||
...(saveToDisk ? { path: process.cwd() + `/export/${filename}.pdf` } : {}),
|
...(saveToDisk ? { path: exportPath } : {}),
|
||||||
format: "A4",
|
format: "A4",
|
||||||
printBackground: false,
|
printBackground: false,
|
||||||
margin: {
|
margin: {
|
||||||
|
@ -50,4 +58,25 @@ export abstract class PdfExport {
|
||||||
|
|
||||||
return pdf;
|
return pdf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async sqashToSingleFile(inputFolder: string, outputFile: string, outputFolder: string = "") {
|
||||||
|
if (outputFolder != "") FileSystemHelper.createFolder(outputFolder);
|
||||||
|
|
||||||
|
let pdfFilePaths = FileSystemHelper.getFilesInDirectory(inputFolder, ".pdf");
|
||||||
|
|
||||||
|
const mergedPdf = await PDFDocument.create();
|
||||||
|
|
||||||
|
for (const pdfPath of pdfFilePaths) {
|
||||||
|
const pdfBytes = FileSystemHelper.readFile(pdfPath);
|
||||||
|
const pdf = await PDFDocument.load(pdfBytes);
|
||||||
|
const copiedPages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
||||||
|
copiedPages.forEach((page) => mergedPdf.addPage(page));
|
||||||
|
}
|
||||||
|
|
||||||
|
const mergedPdfBytes = await mergedPdf.save();
|
||||||
|
|
||||||
|
const exportPath = FileSystemHelper.formatPath(process.cwd(), "export", outputFolder, `${outputFile}.pdf`);
|
||||||
|
|
||||||
|
FileSystemHelper.writeFile(exportPath, mergedPdfBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { readFileSync } from "fs";
|
|
||||||
import TemplateService from "../service/templateService";
|
import TemplateService from "../service/templateService";
|
||||||
import { PermissionModule } from "../type/permissionTypes";
|
import { PermissionModule } from "../type/permissionTypes";
|
||||||
import TemplateUsageService from "../service/templateUsageService";
|
import TemplateUsageService from "../service/templateUsageService";
|
||||||
import Handlebars from "handlebars";
|
import Handlebars from "handlebars";
|
||||||
|
import { FileSystemHelper } from "./fileSystemHelper";
|
||||||
|
|
||||||
export abstract class TemplateHelper {
|
export abstract class TemplateHelper {
|
||||||
static getTemplateFromFile(template: string) {
|
static getTemplateFromFile(template: string) {
|
||||||
return readFileSync(`${process.cwd()}/src/templates/${template}.template.html`, "utf8");
|
return FileSystemHelper.readFile(`/src/templates/${template}.template.html`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getTemplateFromStore(templateId: number): Promise<string> {
|
static async getTemplateFromStore(templateId: number): Promise<string> {
|
||||||
|
|
129
src/migrations/1735118780511-newsletter.ts
Normal file
129
src/migrations/1735118780511-newsletter.ts
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
import { MigrationInterface, QueryRunner, Table, TableForeignKey } from "typeorm";
|
||||||
|
import { DB_TYPE } from "../env.defaults";
|
||||||
|
|
||||||
|
export class Newsletter1735118780511 implements MigrationInterface {
|
||||||
|
name = "Newsletter1735118780511";
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
const variableType_int = DB_TYPE == "mysql" ? "int" : "integer";
|
||||||
|
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: "newsletter_dates",
|
||||||
|
columns: [
|
||||||
|
{ name: "newsletterId", type: variableType_int, isPrimary: true },
|
||||||
|
{ name: "calendarId", type: "varchar", length: "255", isPrimary: true },
|
||||||
|
{ name: "diffTitle", type: "varchar", length: "255", isNullable: true },
|
||||||
|
{ name: "diffDescription", type: "text", isNullable: true },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: "newsletter_recipients",
|
||||||
|
columns: [
|
||||||
|
{ name: "newsletterId", type: variableType_int, isPrimary: true },
|
||||||
|
{ name: "memberId", type: variableType_int, isPrimary: true },
|
||||||
|
{ name: "addedManually", type: "tinyint", default: "0" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: "newsletter",
|
||||||
|
columns: [
|
||||||
|
{ name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" },
|
||||||
|
{ name: "title", type: "varchar", length: "255" },
|
||||||
|
{ name: "description", type: "varchar", length: "255", default: "''" },
|
||||||
|
{ name: "newsletterTitle", type: "varchar", length: "255", default: "''" },
|
||||||
|
{ name: "newsletterText", type: "text", default: "''" },
|
||||||
|
{ name: "newsletterSignatur", type: "varchar", length: "255", default: "''" },
|
||||||
|
{ name: "isSent", type: "tinyint", default: "0" },
|
||||||
|
{ name: "recipientsByQueryId", type: variableType_int, isNullable: true },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createForeignKey(
|
||||||
|
"newsletter_dates",
|
||||||
|
new TableForeignKey({
|
||||||
|
columnNames: ["newsletterId"],
|
||||||
|
referencedColumnNames: ["id"],
|
||||||
|
referencedTableName: "newsletter",
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
onUpdate: "RESTRICT",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createForeignKey(
|
||||||
|
"newsletter_dates",
|
||||||
|
new TableForeignKey({
|
||||||
|
columnNames: ["calendarId"],
|
||||||
|
referencedColumnNames: ["id"],
|
||||||
|
referencedTableName: "calendar",
|
||||||
|
onDelete: "RESTRICT",
|
||||||
|
onUpdate: "RESTRICT",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createForeignKey(
|
||||||
|
"newsletter_recipients",
|
||||||
|
new TableForeignKey({
|
||||||
|
columnNames: ["newsletterId"],
|
||||||
|
referencedColumnNames: ["id"],
|
||||||
|
referencedTableName: "newsletter",
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
onUpdate: "RESTRICT",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createForeignKey(
|
||||||
|
"newsletter_recipients",
|
||||||
|
new TableForeignKey({
|
||||||
|
columnNames: ["memberId"],
|
||||||
|
referencedColumnNames: ["id"],
|
||||||
|
referencedTableName: "member",
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
onUpdate: "RESTRICT",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createForeignKey(
|
||||||
|
"newsletter",
|
||||||
|
new TableForeignKey({
|
||||||
|
columnNames: ["recipientsByQueryId"],
|
||||||
|
referencedColumnNames: ["id"],
|
||||||
|
referencedTableName: "query",
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
onUpdate: "RESTRICT",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
const tableN = await queryRunner.getTable("newsletter");
|
||||||
|
const tableNR = await queryRunner.getTable("newsletter_recipients");
|
||||||
|
const tableND = await queryRunner.getTable("newsletter_dates");
|
||||||
|
|
||||||
|
const foreignKeyN = tableN.foreignKeys.find((fk) => fk.columnNames.indexOf("recipientsByQueryId") !== -1);
|
||||||
|
const foreignKeyNR = tableNR.foreignKeys.find((fk) => fk.columnNames.indexOf("newsletterId") !== -1);
|
||||||
|
const foreignKeyNR2 = tableNR.foreignKeys.find((fk) => fk.columnNames.indexOf("memberId") !== -1);
|
||||||
|
const foreignKeyND1 = tableND.foreignKeys.find((fk) => fk.columnNames.indexOf("newsletterId") !== -1);
|
||||||
|
const foreignKeyND2 = tableND.foreignKeys.find((fk) => fk.columnNames.indexOf("calendarId") !== -1);
|
||||||
|
|
||||||
|
await queryRunner.dropForeignKey("newsletter", foreignKeyN);
|
||||||
|
await queryRunner.dropForeignKey("newsletter_recipients", foreignKeyNR);
|
||||||
|
await queryRunner.dropForeignKey("newsletter_recipients", foreignKeyNR2);
|
||||||
|
await queryRunner.dropForeignKey("newsletter_dates", foreignKeyND1);
|
||||||
|
await queryRunner.dropForeignKey("newsletter_dates", foreignKeyND2);
|
||||||
|
|
||||||
|
await queryRunner.dropTable("newsletter");
|
||||||
|
await queryRunner.dropTable("newsletter_recipients");
|
||||||
|
await queryRunner.dropTable("newsletter_dates");
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import member from "./member";
|
||||||
import protocol from "./protocol";
|
import protocol from "./protocol";
|
||||||
import calendar from "./calendar";
|
import calendar from "./calendar";
|
||||||
import queryBuilder from "./queryBuilder";
|
import queryBuilder from "./queryBuilder";
|
||||||
|
import newsletter from "./newsletter";
|
||||||
|
|
||||||
import role from "./role";
|
import role from "./role";
|
||||||
import user from "./user";
|
import user from "./user";
|
||||||
|
@ -48,6 +49,7 @@ router.use("/member", PermissionHelper.passCheckMiddleware("read", "club", "memb
|
||||||
router.use("/protocol", PermissionHelper.passCheckMiddleware("read", "club", "protocol"), protocol);
|
router.use("/protocol", PermissionHelper.passCheckMiddleware("read", "club", "protocol"), protocol);
|
||||||
router.use("/calendar", PermissionHelper.passCheckMiddleware("read", "club", "calendar"), calendar);
|
router.use("/calendar", PermissionHelper.passCheckMiddleware("read", "club", "calendar"), calendar);
|
||||||
router.use("/querybuilder", PermissionHelper.passCheckMiddleware("read", "club", "query"), queryBuilder);
|
router.use("/querybuilder", PermissionHelper.passCheckMiddleware("read", "club", "query"), queryBuilder);
|
||||||
|
router.use("/newsletter", PermissionHelper.passCheckMiddleware("read", "club", "newsletter"), newsletter);
|
||||||
|
|
||||||
router.use("/role", PermissionHelper.passCheckMiddleware("read", "user", "role"), role);
|
router.use("/role", PermissionHelper.passCheckMiddleware("read", "user", "role"), role);
|
||||||
router.use("/user", PermissionHelper.passCheckMiddleware("read", "user", "user"), user);
|
router.use("/user", PermissionHelper.passCheckMiddleware("read", "user", "user"), user);
|
||||||
|
|
85
src/routes/admin/newsletter.ts
Normal file
85
src/routes/admin/newsletter.ts
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import express, { Request, Response } from "express";
|
||||||
|
import {
|
||||||
|
createNewsletter,
|
||||||
|
createNewsletterPrintoutById,
|
||||||
|
getAllNewsletters,
|
||||||
|
getNewsletterDatesById,
|
||||||
|
getNewsletterById,
|
||||||
|
getNewsletterRecipientsById,
|
||||||
|
getNewsletterPrintoutByIdAndPrint,
|
||||||
|
getNewsletterPrintoutsById,
|
||||||
|
synchronizeNewsletterDatesById,
|
||||||
|
synchronizeNewsletterById,
|
||||||
|
synchronizeNewsletterRecipientsById,
|
||||||
|
} from "../../controller/admin/newsletterController";
|
||||||
|
import PermissionHelper from "../../helpers/permissionHelper";
|
||||||
|
|
||||||
|
var router = express.Router({ mergeParams: true });
|
||||||
|
|
||||||
|
router.get("/", async (req: Request, res: Response) => {
|
||||||
|
await getAllNewsletters(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/:id", async (req: Request, res: Response) => {
|
||||||
|
await getNewsletterById(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/:protocolId/dates", async (req: Request, res: Response) => {
|
||||||
|
await getNewsletterDatesById(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/:protocolId/recipients", async (req: Request, res: Response) => {
|
||||||
|
await getNewsletterRecipientsById(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/:protocolId/printouts", async (req: Request, res: Response) => {
|
||||||
|
await getNewsletterPrintoutsById(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/:protocolId/printout/:filename", async (req: Request, res: Response) => {
|
||||||
|
await getNewsletterPrintoutByIdAndPrint(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/",
|
||||||
|
PermissionHelper.passCheckMiddleware("create", "club", "protocol"),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
await createNewsletter(req, res);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/:protocolId/printout",
|
||||||
|
PermissionHelper.passCheckMiddleware("create", "club", "protocol"),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
await createNewsletterPrintoutById(req, res);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/:id/synchronize",
|
||||||
|
PermissionHelper.passCheckMiddleware("update", "club", "protocol"),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
await synchronizeNewsletterById(req, res);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/:protocolId/synchronize/dates",
|
||||||
|
PermissionHelper.passCheckMiddleware("update", "club", "protocol"),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
await synchronizeNewsletterDatesById(req, res);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/:protocolId/synchronize/recipients",
|
||||||
|
PermissionHelper.passCheckMiddleware("update", "club", "protocol"),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
await synchronizeNewsletterRecipientsById(req, res);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: send mails | send mail preview | render preview before print job
|
||||||
|
|
||||||
|
export default router;
|
26
src/service/newsletterDatesService.ts
Normal file
26
src/service/newsletterDatesService.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { dataSource } from "../data-source";
|
||||||
|
import { newsletterDates } from "../entity/newsletterDates";
|
||||||
|
import { member } from "../entity/member";
|
||||||
|
import InternalException from "../exceptions/internalException";
|
||||||
|
|
||||||
|
export default abstract class NewsletterDatesService {
|
||||||
|
/**
|
||||||
|
* @description get all newsletterDates
|
||||||
|
* @returns {Promise<Array<newsletterDates>>}
|
||||||
|
*/
|
||||||
|
static async getAll(newsletterId: number): Promise<Array<newsletterDates>> {
|
||||||
|
return await dataSource
|
||||||
|
.getRepository(newsletterDates)
|
||||||
|
.createQueryBuilder("newsletterDates")
|
||||||
|
.leftJoinAndSelect("newsletterDates.calendar", "calendar")
|
||||||
|
.leftJoinAndSelect("newsletterDates.newsletter", "newsletter")
|
||||||
|
.where("newsletterDates.newsletterId = :id", { id: newsletterId })
|
||||||
|
.getMany()
|
||||||
|
.then((res) => {
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("newsletterDatess not found", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
28
src/service/newsletterRecipientsService.ts
Normal file
28
src/service/newsletterRecipientsService.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { dataSource } from "../data-source";
|
||||||
|
import { newsletterRecipients } from "../entity/newsletterRecipients";
|
||||||
|
import { member } from "../entity/member";
|
||||||
|
import InternalException from "../exceptions/internalException";
|
||||||
|
|
||||||
|
export default abstract class NewsletterRecipientsService {
|
||||||
|
/**
|
||||||
|
* @description get all newsletterRecipients
|
||||||
|
* @returns {Promise<Array<newsletterRecipients>>}
|
||||||
|
*/
|
||||||
|
static async getAll(newsletterId: number): Promise<Array<newsletterRecipients>> {
|
||||||
|
return await dataSource
|
||||||
|
.getRepository(newsletterRecipients)
|
||||||
|
.createQueryBuilder("newsletterRecipients")
|
||||||
|
.leftJoinAndSelect("newsletterRecipients.member", "member")
|
||||||
|
.leftJoinAndSelect("member.sendNewsletter", "sendNewsletter")
|
||||||
|
.leftJoinAndSelect("sendNewsletter.type", "communicationtype")
|
||||||
|
.leftJoinAndSelect("newsletterRecipients.newsletter", "newsletter")
|
||||||
|
.where("newsletterDates.newsletterId = :id", { id: newsletterId })
|
||||||
|
.getMany()
|
||||||
|
.then((res) => {
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("newsletterRecipientss not found", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
44
src/service/newsletterService.ts
Normal file
44
src/service/newsletterService.ts
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { dataSource } from "../data-source";
|
||||||
|
import { newsletter } from "../entity/newsletter";
|
||||||
|
import { member } from "../entity/member";
|
||||||
|
import InternalException from "../exceptions/internalException";
|
||||||
|
|
||||||
|
export default abstract class NewsletterService {
|
||||||
|
/**
|
||||||
|
* @description get all newsletters
|
||||||
|
* @returns {Promise<[Array<newsletter>, number]>}
|
||||||
|
*/
|
||||||
|
static async getAll(offset: number = 0, count: number = 25): Promise<[Array<newsletter>, number]> {
|
||||||
|
return await dataSource
|
||||||
|
.getRepository(newsletter)
|
||||||
|
.createQueryBuilder("newsletter")
|
||||||
|
.offset(offset)
|
||||||
|
.limit(count)
|
||||||
|
.getManyAndCount()
|
||||||
|
.then((res) => {
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("newsletters not found", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get newsletter by id
|
||||||
|
* @returns {Promise<newsletter>}
|
||||||
|
*/
|
||||||
|
static async getById(id: number): Promise<newsletter> {
|
||||||
|
return await dataSource
|
||||||
|
.getRepository(newsletter)
|
||||||
|
.createQueryBuilder("newsletter")
|
||||||
|
.leftJoinAndSelect("newsletter.recipientsByQuery", "query")
|
||||||
|
.where("newsletter.id = :id", { id: id })
|
||||||
|
.getOneOrFail()
|
||||||
|
.then((res) => {
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("newsletter not found by id", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,23 +21,4 @@ export default abstract class ProtocolPresenceService {
|
||||||
throw new InternalException("protocolPresence not found", err);
|
throw new InternalException("protocolPresence not found", err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @description get protocolDecision by id
|
|
||||||
* @returns {Promise<protocolPresence>}
|
|
||||||
*/
|
|
||||||
static async getById(id: number): Promise<protocolPresence> {
|
|
||||||
return await dataSource
|
|
||||||
.getRepository(protocolPresence)
|
|
||||||
.createQueryBuilder("protocolPresence")
|
|
||||||
.leftJoinAndSelect("protocolPresence.member", "member")
|
|
||||||
.where("protocolPresence.id = :id", { id: id })
|
|
||||||
.getOneOrFail()
|
|
||||||
.then((res) => {
|
|
||||||
return res;
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
throw new InternalException("protocolDecision not found by id", err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ export type PermissionModule =
|
||||||
| "member"
|
| "member"
|
||||||
| "calendar"
|
| "calendar"
|
||||||
| "newsletter"
|
| "newsletter"
|
||||||
|
| "newsletter_config"
|
||||||
| "protocol"
|
| "protocol"
|
||||||
| "qualification"
|
| "qualification"
|
||||||
| "award"
|
| "award"
|
||||||
|
@ -44,6 +45,7 @@ export const permissionModules: Array<PermissionModule> = [
|
||||||
"member",
|
"member",
|
||||||
"calendar",
|
"calendar",
|
||||||
"newsletter",
|
"newsletter",
|
||||||
|
"newsletter_config",
|
||||||
"protocol",
|
"protocol",
|
||||||
"qualification",
|
"qualification",
|
||||||
"award",
|
"award",
|
||||||
|
@ -71,6 +73,7 @@ export const sectionsAndModules: SectionsAndModulesObject = {
|
||||||
"query_store",
|
"query_store",
|
||||||
"template",
|
"template",
|
||||||
"template_usage",
|
"template_usage",
|
||||||
|
"newsletter_config",
|
||||||
],
|
],
|
||||||
user: ["user", "role"],
|
user: ["user", "role"],
|
||||||
};
|
};
|
||||||
|
|
12
src/viewmodel/admin/newsletter.models.ts
Normal file
12
src/viewmodel/admin/newsletter.models.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { QueryStoreViewModel } from "./queryStore.models";
|
||||||
|
|
||||||
|
export interface NewsletterViewModel {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
newsletterTitle: string;
|
||||||
|
newsletterText: string;
|
||||||
|
newsletterSignatur: string;
|
||||||
|
isSent: boolean;
|
||||||
|
recipientsByQuery?: QueryStoreViewModel;
|
||||||
|
}
|
9
src/viewmodel/admin/newsletterDates.models.ts
Normal file
9
src/viewmodel/admin/newsletterDates.models.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { CalendarViewModel } from "./calendar.models";
|
||||||
|
|
||||||
|
export interface NewsletterDatesViewModel {
|
||||||
|
newsletterId: number;
|
||||||
|
calendarId: number;
|
||||||
|
diffTitle: string | null;
|
||||||
|
diffDescription: string | null;
|
||||||
|
calendar: CalendarViewModel;
|
||||||
|
}
|
8
src/viewmodel/admin/newsletterRecipients.models.ts
Normal file
8
src/viewmodel/admin/newsletterRecipients.models.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { MemberViewModel } from "./member.models";
|
||||||
|
|
||||||
|
export interface NewsletterRecipientsViewModel {
|
||||||
|
newsletterId: number;
|
||||||
|
memberId: number;
|
||||||
|
addedManually: boolean;
|
||||||
|
member: MemberViewModel;
|
||||||
|
}
|
Loading…
Reference in a new issue