diff options
Diffstat (limited to 'lib/mail')
| -rw-r--r-- | lib/mail/mailer.ts | 146 | ||||
| -rw-r--r-- | lib/mail/sendEmail.ts | 15 | ||||
| -rw-r--r-- | lib/mail/templates/letter-of-regret.hbs | 190 |
3 files changed, 304 insertions, 47 deletions
diff --git a/lib/mail/mailer.ts b/lib/mail/mailer.ts index 61201e99..0387d7dd 100644 --- a/lib/mail/mailer.ts +++ b/lib/mail/mailer.ts @@ -23,54 +23,96 @@ function registerHandlebarsHelpers() { return i18next.t(key, options.hash || {}); }); - // eq 헬퍼 등록 - 두 값을 비교 (블록 헬퍼) - handlebars.registerHelper('eq', function(a: any, b: any, options: any) { - if (a === b) { - return options.fn(this); - } else { - return options.inverse(this); + // eq 헬퍼 등록 - 인라인과 블록 헬퍼 둘 다 지원 + handlebars.registerHelper('eq', function(a: any, b: any, options?: any) { + const result = a === b; + + // 블록 헬퍼로 사용된 경우 (options가 있고 fn 함수가 있는 경우) + if (options && typeof options.fn === 'function') { + if (result) { + return options.fn(this); + } else { + return options.inverse(this); + } } + + // 인라인 헬퍼로 사용된 경우 + return result; }); - // 기타 유용한 헬퍼들 - handlebars.registerHelper('ne', function(a: any, b: any, options: any) { - if (a !== b) { - return options.fn(this); - } else { - return options.inverse(this); + // ne 헬퍼 - 인라인과 블록 헬퍼 둘 다 지원 + handlebars.registerHelper('ne', function(a: any, b: any, options?: any) { + const result = a !== b; + + if (options && typeof options.fn === 'function') { + if (result) { + return options.fn(this); + } else { + return options.inverse(this); + } } + + return result; }); - handlebars.registerHelper('gt', function(a: any, b: any, options: any) { - if (a > b) { - return options.fn(this); - } else { - return options.inverse(this); + // gt 헬퍼 - 인라인과 블록 헬퍼 둘 다 지원 + handlebars.registerHelper('gt', function(a: any, b: any, options?: any) { + const result = a > b; + + if (options && typeof options.fn === 'function') { + if (result) { + return options.fn(this); + } else { + return options.inverse(this); + } } + + return result; }); - handlebars.registerHelper('gte', function(a: any, b: any, options: any) { - if (a >= b) { - return options.fn(this); - } else { - return options.inverse(this); + // gte 헬퍼 - 인라인과 블록 헬퍼 둘 다 지원 + handlebars.registerHelper('gte', function(a: any, b: any, options?: any) { + const result = a >= b; + + if (options && typeof options.fn === 'function') { + if (result) { + return options.fn(this); + } else { + return options.inverse(this); + } } + + return result; }); - handlebars.registerHelper('lt', function(a: any, b: any, options: any) { - if (a < b) { - return options.fn(this); - } else { - return options.inverse(this); + // lt 헬퍼 - 인라인과 블록 헬퍼 둘 다 지원 + handlebars.registerHelper('lt', function(a: any, b: any, options?: any) { + const result = a < b; + + if (options && typeof options.fn === 'function') { + if (result) { + return options.fn(this); + } else { + return options.inverse(this); + } } + + return result; }); - handlebars.registerHelper('lte', function(a: any, b: any, options: any) { - if (a <= b) { - return options.fn(this); - } else { - return options.inverse(this); + // lte 헬퍼 - 인라인과 블록 헬퍼 둘 다 지원 + handlebars.registerHelper('lte', function(a: any, b: any, options?: any) { + const result = a <= b; + + if (options && typeof options.fn === 'function') { + if (result) { + return options.fn(this); + } else { + return options.inverse(this); + } } + + return result; }); // and 헬퍼 - 모든 조건이 true인지 확인 (블록 헬퍼) @@ -78,12 +120,17 @@ function registerHandlebarsHelpers() { // 마지막 인자는 Handlebars 옵션 const options = args[args.length - 1]; const values = args.slice(0, -1); + const result = values.every(Boolean); - if (values.every(Boolean)) { - return options.fn(this); - } else { - return options.inverse(this); + if (typeof options.fn === 'function') { + if (result) { + return options.fn(this); + } else { + return options.inverse(this); + } } + + return result; }); // or 헬퍼 - 하나라도 true인지 확인 (블록 헬퍼) @@ -91,21 +138,32 @@ function registerHandlebarsHelpers() { // 마지막 인자는 Handlebars 옵션 const options = args[args.length - 1]; const values = args.slice(0, -1); + const result = values.some(Boolean); - if (values.some(Boolean)) { - return options.fn(this); - } else { - return options.inverse(this); + if (typeof options.fn === 'function') { + if (result) { + return options.fn(this); + } else { + return options.inverse(this); + } } + + return result; }); // not 헬퍼 - 값 반전 (블록 헬퍼) handlebars.registerHelper('not', function(value: any, options: any) { - if (!value) { - return options.fn(this); - } else { - return options.inverse(this); + const result = !value; + + if (typeof options.fn === 'function') { + if (result) { + return options.fn(this); + } else { + return options.inverse(this); + } } + + return result; }); // formatDate 헬퍼 - 날짜 포맷팅 diff --git a/lib/mail/sendEmail.ts b/lib/mail/sendEmail.ts index 3f88cb04..b4d2707a 100644 --- a/lib/mail/sendEmail.ts +++ b/lib/mail/sendEmail.ts @@ -29,14 +29,23 @@ export async function sendEmail({ // i18n 설정 const { t, i18n } = await useTranslation(context.language ?? "en", "translation"); - // t 헬퍼만 동적으로 등록 (이미 mailer.ts에서 기본 등록되어 있지만, 언어별로 다시 등록) + // t 헬퍼를 언어별로 동적으로 재등록 (기존 헬퍼 덮어쓰기) + // 헬퍼가 이미 등록되어 있더라도 안전하게 재등록 + handlebars.unregisterHelper('t'); // 기존 헬퍼 제거 handlebars.registerHelper("t", function (key: string, options: any) { // 여기서 i18n은 로컬 인스턴스 - return i18n.t(key, options.hash || {}); + return i18n.t(key, options?.hash || {}); }); + // 템플릿 데이터에 i18n 인스턴스와 번역 함수 추가 + const templateData = { + ...context, + t: (key: string, options?: any) => i18n.t(key, options || {}), + i18n: i18n + }; + // 템플릿 컴파일 및 HTML 생성 - const html = loadTemplate(template, context); + const html = loadTemplate(template, templateData); // 이메일 발송 const result = await transporter.sendMail({ diff --git a/lib/mail/templates/letter-of-regret.hbs b/lib/mail/templates/letter-of-regret.hbs new file mode 100644 index 00000000..13fab6fc --- /dev/null +++ b/lib/mail/templates/letter-of-regret.hbs @@ -0,0 +1,190 @@ +<!DOCTYPE html> +<html lang="{{#if (eq language 'ko')}}ko{{else}}en{{/if}}"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>{{#if (eq language 'ko')}}Letter of Regret{{else}}Letter of Regret{{/if}}</title> + <style> + body { + font-family: 'Arial', sans-serif; + line-height: 1.6; + color: #333; + max-width: 800px; + margin: 0 auto; + padding: 20px; + background-color: #f9f9f9; + } + .container { + background-color: white; + padding: 40px; + border-radius: 8px; + box-shadow: 0 2px 10px rgba(0,0,0,0.1); + } + .header { + text-align: center; + border-bottom: 2px solid #e74c3c; + padding-bottom: 20px; + margin-bottom: 30px; + } + .company-logo { + font-size: 24px; + font-weight: bold; + color: #2c3e50; + margin-bottom: 10px; + } + .letter-title { + font-size: 20px; + color: #e74c3c; + font-weight: bold; + margin: 0; + } + .content { + margin-bottom: 30px; + } + .greeting { + font-weight: bold; + margin-bottom: 20px; + font-size: 16px; + } + .body-text { + margin-bottom: 15px; + text-align: justify; + } + .project-info { + background-color: #f8f9fa; + padding: 15px; + border-left: 4px solid #e74c3c; + margin: 20px 0; + } + .project-info strong { + color: #2c3e50; + } + .closing { + margin-top: 30px; + } + .signature { + margin-top: 40px; + border-top: 1px solid #ddd; + padding-top: 20px; + } + .signature-line { + margin-bottom: 5px; + } + .footer { + margin-top: 30px; + padding-top: 20px; + border-top: 1px solid #ddd; + font-size: 12px; + color: #666; + text-align: center; + } + .highlight { + color: #e74c3c; + font-weight: bold; + } + </style> +</head> +<body> + <div class="container"> + <div class="header"> + <div class="company-logo">{{companyName}}</div> + <h1 class="letter-title"> + {{#if (eq language 'ko')}} + 협력 제안 결과 안내서 (Letter of Regret) + {{else}} + Letter of Regret + {{/if}} + </h1> + </div> + + <div class="content"> + <div class="greeting"> + {{#if (eq language 'ko')}} + {{vendorName}} 귀하 + {{else}} + Dear {{vendorName}}, + {{/if}} + </div> + + <div class="body-text"> + {{#if (eq language 'ko')}} + 안녕하십니까. {{companyName}}입니다. + {{else}} + Greetings from {{companyName}}. + {{/if}} + </div> + + <div class="body-text"> + {{#if (eq language 'ko')}} + 먼저 저희 프로젝트에 관심을 가져주시고 귀중한 시간을 할애하여 RFQ에 응답해 주신 점에 대해 깊이 감사드립니다. + {{else}} + First, we would like to express our sincere gratitude for your interest in our project and for taking the time to respond to our RFQ. + {{/if}} + </div> + + <div class="project-info"> + <strong> + {{#if (eq language 'ko')}}프로젝트 정보{{else}}Project Information{{/if}}: + </strong><br> + <strong>RFQ {{#if (eq language 'ko')}}번호{{else}}Number{{/if}}:</strong> {{rfqCode}}<br> + <strong>{{#if (eq language 'ko')}}프로젝트명{{else}}Project Title{{/if}}:</strong> {{projectTitle}}<br> + <strong>{{#if (eq language 'ko')}}통지일{{else}}Notification Date{{/if}}:</strong> {{dateTime}} + </div> + + <div class="body-text"> + {{#if (eq language 'ko')}} + 신중한 검토와 평가를 거쳐 진행한 결과, 아쉽게도 이번 프로젝트에서는 다른 업체와 함께 진행하기로 결정하였음을 알려드립니다. + {{else}} + After careful review and evaluation, we regret to inform you that we have decided to proceed with another vendor for this project. + {{/if}} + </div> + + <div class="body-text"> + {{#if (eq language 'ko')}} + 이번 결정이 귀하의 기술력이나 역량에 대한 평가와는 무관함을 말씀드리며, 향후 다른 프로젝트에서 함께 할 수 있는 기회가 있기를 기대합니다. + {{else}} + Please note that this decision is not a reflection of your technical capabilities or competence, and we look forward to potential collaboration opportunities in future projects. + {{/if}} + </div> + + <div class="body-text"> + {{#if (eq language 'ko')}} + 다시 한번 저희 RFQ에 참여해 주신 것에 대해 진심으로 감사드리며, 앞으로도 지속적인 관심과 협력을 부탁드립니다. + {{else}} + Once again, we sincerely thank you for your participation in our RFQ process and look forward to your continued interest and cooperation. + {{/if}} + </div> + + <div class="closing"> + {{#if (eq language 'ko')}} + 감사합니다. + {{else}} + Thank you for your understanding. + {{/if}} + </div> + </div> + + <div class="signature"> + <div class="signature-line"> + <strong>{{#if (eq language 'ko')}}발신{{else}}From{{/if}}:</strong> {{companyName}} + </div> + <div class="signature-line"> + <strong>{{#if (eq language 'ko')}}구매팀{{else}}Procurement Department{{/if}}</strong> + </div> + <div class="signature-line"> + <strong>{{#if (eq language 'ko')}}일자{{else}}Date{{/if}}:</strong> {{dateTime}} + </div> + </div> + + <div class="footer"> + <p> + {{#if (eq language 'ko')}} + 본 메일은 자동으로 발송된 메일입니다. 문의사항이 있으시면 담당자에게 직접 연락해 주시기 바랍니다. + {{else}} + This is an automatically generated email. For any inquiries, please contact the relevant department directly. + {{/if}} + </p> + </div> + </div> +</body> +</html>
\ No newline at end of file |
