diff --git a/app/(public)/register-company/page.tsx b/app/(public)/register-company/page.tsx index c4a75df..881a0fc 100644 --- a/app/(public)/register-company/page.tsx +++ b/app/(public)/register-company/page.tsx @@ -1,18 +1,23 @@ -"use client"; +'use client'; -import { MedReportLogo } from "@/components/med-report-title"; -import React from "react"; -import { yupResolver } from "@hookform/resolvers/yup"; -import { useForm } from "react-hook-form"; -import { companySchema } from "@/lib/validations/companySchema"; -import { CompanySubmitData } from "@/lib/types/company"; -import { submitCompanyRegistration } from "@/lib/services/register-company.service"; -import { useRouter } from "next/navigation"; -import { Label } from "@kit/ui/label"; -import { Input } from "@kit/ui/input"; -import { SubmitButton } from "@/components/ui/submit-button"; -import { FormItem } from "@kit/ui/form"; -import { Trans } from "@kit/ui/trans"; +import React from 'react'; + +import { useRouter } from 'next/navigation'; + +import { MedReportLogo } from '@/components/med-report-title'; +import { SubmitButton } from '@/components/ui/submit-button'; +import { sendCompanyOfferEmail } from '@/lib/services/mailer.service'; +import { submitCompanyRegistration } from '@/lib/services/register-company.service'; +import { CompanySubmitData } from '@/lib/types/company'; +import { companySchema } from '@/lib/validations/companySchema'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +import { FormItem } from '@kit/ui/form'; +import { Input } from '@kit/ui/input'; +import { Label } from '@kit/ui/label'; +import { Trans } from '@kit/ui/trans'; export default function RegisterCompany() { const router = useRouter(); @@ -21,9 +26,10 @@ export default function RegisterCompany() { handleSubmit, formState: { errors, isValid, isSubmitting }, } = useForm({ - resolver: yupResolver(companySchema), - mode: "onChange", + resolver: zodResolver(companySchema), + mode: 'onChange', }); + const language = useTranslation().i18n.language; async function onSubmit(data: CompanySubmitData) { const formData = new FormData(); @@ -33,57 +39,66 @@ export default function RegisterCompany() { try { await submitCompanyRegistration(formData); - router.push("/register-company/success"); + sendCompanyOfferEmail(data, language) + .then(() => router.push('/register-company/success')) + .catch((error) => alert('error: ' + error)); } catch (err: unknown) { if (err instanceof Error) { - alert("Server validation error: " + err.message); + alert('Server validation error: ' + err.message); } - alert("Server validation error"); + alert('Server validation error'); } } return ( -
-
+
+
-

Ettevõtte andmed

-

- Pakkumise saamiseks palun sisesta ettevõtte andmed millega MedReport - kasutada kavatsed. +

+ +

+

+

- - + + - - + + - - + + - - + + - +
-
-
+
); } diff --git a/app/(public)/register-company/success/page.tsx b/app/(public)/register-company/success/page.tsx index 3ed223d..42f2634 100644 --- a/app/(public)/register-company/success/page.tsx +++ b/app/(public)/register-company/success/page.tsx @@ -1,11 +1,16 @@ -import { MedReportLogo } from "@/components/med-report-title"; -import { Button } from "@kit/ui/button"; -import Image from "next/image"; -import Link from "next/link"; +'use client'; + +import Image from 'next/image'; +import Link from 'next/link'; + +import { MedReportLogo } from '@/components/med-report-title'; + +import { Button } from '@kit/ui/button'; +import { Trans } from '@kit/ui/trans'; export default function CompanyRegistrationSuccess() { return ( -
+
-

Päring edukalt saadetud!

-

Saadame teile esimesel võimalusel vastuse

+

+ +

+

+ +

-
); diff --git a/lib/services/mailer.service.ts b/lib/services/mailer.service.ts new file mode 100644 index 0000000..515bfcc --- /dev/null +++ b/lib/services/mailer.service.ts @@ -0,0 +1,42 @@ +'use server'; + +import { getMailer } from '@kit/mailers'; +import { enhanceAction } from '@kit/next/actions'; + +import { CompanySubmitData } from '../types/company'; +import { emailSchema } from '../validations/email.schema'; + +export const sendCompanyOfferEmail = async ( + data: CompanySubmitData, + language: string, +) => { + const { renderCompanyOfferEmail } = await import('@kit/email-templates'); + const { html, subject, to } = await renderCompanyOfferEmail({ + language, + companyData: data, + }); + + await sendEmail({ + subject, + html, + to, + }); +}; + +export const sendEmail = enhanceAction( + async ({ subject, html, to }) => { + const mailer = await getMailer(); + + await mailer.sendEmail({ + to, + subject, + html, + }); + + return {}; + }, + { + schema: emailSchema, + auth: false, + }, +); diff --git a/lib/services/register-company.service.ts b/lib/services/register-company.service.ts index c030462..7b973bd 100644 --- a/lib/services/register-company.service.ts +++ b/lib/services/register-company.service.ts @@ -1,31 +1,26 @@ -"use server"; +'use server'; -import * as yup from "yup"; -import { companySchema } from "@/lib/validations/companySchema"; +import { companySchema } from '@/lib/validations/companySchema'; export async function submitCompanyRegistration(formData: FormData) { const data = { - companyName: formData.get("companyName")?.toString() || "", - contactPerson: formData.get("contactPerson")?.toString() || "", - email: formData.get("email")?.toString() || "", - phone: formData.get("phone")?.toString() || "", + companyName: formData.get('companyName')?.toString() || '', + contactPerson: formData.get('contactPerson')?.toString() || '', + email: formData.get('email')?.toString() || '', + phone: formData.get('phone')?.toString() || '', }; - try { - await companySchema.validate(data, { abortEarly: false }); + const result = companySchema.safeParse(data); - console.log("Valid data:", data); - } catch (validationError) { - if (validationError instanceof yup.ValidationError) { - const errors = validationError.inner.map((err) => ({ - path: err.path, - message: err.message, - })); - throw new Error( - "Validation failed: " + - errors.map((e) => `${e.path}: ${e.message}`).join(", ") - ); - } - throw validationError; + if (!result.success) { + const errors = result.error.errors.map((err) => ({ + path: err.path.join('.'), + message: err.message, + })); + + throw new Error( + 'Validation failed: ' + + errors.map((e) => `${e.path}: ${e.message}`).join(', '), + ); } } diff --git a/lib/validations/companySchema.ts b/lib/validations/companySchema.ts index b265f81..aa35ebc 100644 --- a/lib/validations/companySchema.ts +++ b/lib/validations/companySchema.ts @@ -1,8 +1,16 @@ -import * as yup from "yup"; +import { z } from 'zod'; -export const companySchema = yup.object({ - companyName: yup.string().required("Company name is required"), - contactPerson: yup.string().required("Contact person is required"), - email: yup.string().email("Invalid email").required("Email is required"), - phone: yup.string().optional(), +export const companySchema = z.object({ + companyName: z.string({ + required_error: 'Company name is required', + }), + contactPerson: z.string({ + required_error: 'Contact person is required', + }), + email: z + .string({ + required_error: 'Email is required', + }) + .email('Invalid email'), + phone: z.string().optional(), }); diff --git a/lib/validations/email.schema.ts b/lib/validations/email.schema.ts new file mode 100644 index 0000000..58cc00d --- /dev/null +++ b/lib/validations/email.schema.ts @@ -0,0 +1,7 @@ +import { z } from 'zod'; + +export const emailSchema = z.object({ + to: z.string().email(), + subject: z.string().min(1).max(200), + html: z.string().min(1).max(5000), +}); diff --git a/packages/email-templates/README.md b/packages/email-templates/README.md index dbd6ada..83ec48a 100644 --- a/packages/email-templates/README.md +++ b/packages/email-templates/README.md @@ -2,4 +2,4 @@ This package is responsible for managing email templates using the react.email library. -Here you can define email templates using React components and export them as a function that returns the email content. \ No newline at end of file +Here you can define email templates using React components and export them as a function that returns the email content. diff --git a/packages/email-templates/src/emails/company-offer.email.tsx b/packages/email-templates/src/emails/company-offer.email.tsx new file mode 100644 index 0000000..fa9f30f --- /dev/null +++ b/packages/email-templates/src/emails/company-offer.email.tsx @@ -0,0 +1,90 @@ +import { + Body, + Head, + Html, + Preview, + Tailwind, + Text, + render, +} from '@react-email/components'; + +import { BodyStyle } from '../components/body-style'; +import { EmailContent } from '../components/content'; +import { EmailHeader } from '../components/header'; +import { EmailHeading } from '../components/heading'; +import { EmailWrapper } from '../components/wrapper'; +import { initializeEmailI18n } from '../lib/i18n'; + +export async function renderCompanyOfferEmail({ + language, + companyData, +}: { + language?: string; + companyData: { + companyName: string; + contactPerson: string; + email: string; + phone?: string; + }; +}) { + const namespace = 'company-offer-email'; + + const { t } = await initializeEmailI18n({ + language, + namespace, + }); + + const to = process.env.CONTACT_EMAIL || ''; + + const previewText = t(`${namespace}:previewText`, { + companyName: companyData.companyName, + }); + + const subject = t(`${namespace}:subject`, { + companyName: companyData.companyName, + }); + + const html = await render( + + + + + + {previewText} + + + + + + {previewText} + + + + + {t(`${namespace}:companyName`)} {companyData.companyName} + + + + {t(`${namespace}:contactPerson`)} {companyData.contactPerson} + + + + {t(`${namespace}:email`)} {companyData.email} + + + + {t(`${namespace}:phone`)} {companyData.phone || 'N/A'} + + + + + + , + ); + + return { + html, + subject, + to, + }; +} diff --git a/packages/email-templates/src/index.ts b/packages/email-templates/src/index.ts index c7c2eb4..80e5f8a 100644 --- a/packages/email-templates/src/index.ts +++ b/packages/email-templates/src/index.ts @@ -1,3 +1,4 @@ export * from './emails/invite.email'; export * from './emails/account-delete.email'; export * from './emails/otp.email'; +export * from './emails/company-offer.email'; diff --git a/packages/email-templates/src/locales/et/company-offer-email.json b/packages/email-templates/src/locales/et/company-offer-email.json new file mode 100644 index 0000000..3a39792 --- /dev/null +++ b/packages/email-templates/src/locales/et/company-offer-email.json @@ -0,0 +1,8 @@ +{ + "subject": "Uus ettevõtte liitumispäring", + "previewText": "Ettevõte {{companyName}} soovib pakkumist", + "companyName": "Ettevõtte nimi:", + "contactPerson": "Kontaktisik:", + "email": "E-mail:", + "phone": "Telefon:" +} diff --git a/packages/mailers/shared/src/schema/mailer.schema.ts b/packages/mailers/shared/src/schema/mailer.schema.ts index 1fd1f58..dd0e652 100644 --- a/packages/mailers/shared/src/schema/mailer.schema.ts +++ b/packages/mailers/shared/src/schema/mailer.schema.ts @@ -5,7 +5,7 @@ export const MailerSchema = z to: z.string().email(), // this is not necessarily formatted // as an email so we type it loosely - from: z.string().min(1), + from: z.string().min(1).optional(), subject: z.string(), }) .and( diff --git a/public/locales/en/account.json b/public/locales/en/account.json index 28ee8d9..dbaa182 100644 --- a/public/locales/en/account.json +++ b/public/locales/en/account.json @@ -113,5 +113,7 @@ "createTeam": "Create a team to get started.", "createTeamButtonLabel": "Create a Team", "createCompanyAccount": "Create Company Account", - "requestCompanyAccount": "Request Company Account" + "requestCompanyAccount": { + "title": "Company details" + } } diff --git a/public/locales/et/account.json b/public/locales/et/account.json index 28ee8d9..a6af3a3 100644 --- a/public/locales/et/account.json +++ b/public/locales/et/account.json @@ -113,5 +113,12 @@ "createTeam": "Create a team to get started.", "createTeamButtonLabel": "Create a Team", "createCompanyAccount": "Create Company Account", - "requestCompanyAccount": "Request Company Account" + "requestCompanyAccount": { + "title": "Ettevõtte andmed", + "description": "Pakkumise saamiseks palun sisesta ettevõtte andmed millega MedReport kasutada kavatsed.", + "button": "Küsi pakkumist", + "successTitle": "Päring edukalt saadetud!", + "successDescription": "Saadame teile esimesel võimalusel vastuse", + "successButton": "Tagasi kodulehele" + } } diff --git a/public/locales/et/common.json b/public/locales/et/common.json index 8aade69..6242a03 100644 --- a/public/locales/et/common.json +++ b/public/locales/et/common.json @@ -92,5 +92,11 @@ "description": "This website uses cookies to ensure you get the best experience on our website.", "reject": "Reject", "accept": "Accept" + }, + "formField": { + "companyName": "Ettevõtte nimi", + "contactPerson": "Kontaktisik", + "email": "E-mail", + "phone": "Telefon" } } diff --git a/public/locales/ru/account.json b/public/locales/ru/account.json index 28ee8d9..1b0924e 100644 --- a/public/locales/ru/account.json +++ b/public/locales/ru/account.json @@ -112,6 +112,5 @@ "noTeamsYet": "You don't have any teams yet.", "createTeam": "Create a team to get started.", "createTeamButtonLabel": "Create a Team", - "createCompanyAccount": "Create Company Account", - "requestCompanyAccount": "Request Company Account" + "createCompanyAccount": "Create Company Account" } diff --git a/tsconfig.json b/tsconfig.json index 87b31f1..f3c8d06 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "baseUrl": ".", "paths": { - "@/*":["./*"], + "@/*": ["./*"], "~/*": ["./app/*"], "~/config/*": ["./config/*"], "~/components/*": ["./components/*"],