Kateb Tech Docs
Packages

@katebtech/emails

Reusable email templates, transactional email components, and email-related utilities.

@katebtech/emails

Reusable React Email templates and email sending helpers for Kateb Tech website projects.

This README documents the package purpose, public APIs, import style, package-specific rules, module architecture, and usage examples.

Purpose

@katebtech/emails provides shared email layouts, contact enquiry emails, authentication emails, admin notification emails, and Resend email client helpers.

This package is used by website projects when they need consistent email templates and email delivery logic.

Package Overview

Use @katebtech/emails for:

  • reusable React Email templates
  • contact form notification emails
  • customer confirmation emails
  • authentication emails
  • admin/business approval emails
  • shared email layouts
  • shared email footer components
  • Resend email client setup
  • reusable email sending helpers

File Structure

src
├── auth
│   ├── email-verification.tsx
│   ├── index.ts
│   └── types.ts
├── contact-us
│   ├── BusinessEmail.tsx
│   ├── ConfirmationEmail.tsx
│   ├── EnquiryBody.tsx
│   ├── index.ts
│   ├── sendEnquiryEmails.ts
│   └── types.ts
├── layout
│   ├── EmailFooter.tsx
│   ├── EmailLayout.tsx
│   ├── PoweredByKateb.tsx
│   ├── index.ts
│   └── types.ts
├── _lib
│   ├── constants.ts
│   ├── createEmailSerivce.ts
│   ├── emailClient.ts
│   ├── types.ts
│   └── index.ts
└── projects
    └── little-bamiyan
        ├── activate-business-listing
        │   ├── BusinessApprovedEmailBody.tsx
        │   ├── index.ts
        │   ├── sendActivationEmails.ts
        │   ├── sendBusinessApprovedEmail.tsx
        │   └── types.ts
        └── index.ts

Import Style

Use package subpaths when consuming this package from website projects or other packages.

import { EmailLayout } from "@katebtech/emails/layout";
import { EmailVerificationCode } from "@katebtech/emails/auth";
import { sendEnquiryEmails } from "@katebtech/emails/contact-us";
import { createEmailClient } from "@katebtech/emails/lib";

Import public types from the same subpath as the API they describe.

import type { OrgInfoEmail } from "@katebtech/emails/lib";
import type { SendEnquiryEmails } from "@katebtech/emails/contact-us";

Inside this package, prefer relative imports between local files.

import { EmailLayout } from "../layout";
import type { OrgInfoEmail } from "../_lib";

Do not import @katebtech/emails from inside this package.

Package Rules

Folder Purpose

FolderPurpose
src/layoutShared email layout components used by multiple templates.
src/_libShared email utilities, constants, and email package types.
src/authAuthentication-related emails such as email verification.
src/contact-usContact enquiry email templates and send helpers.
src/projects/little-bamiyanProject-specific emails for Little Bamiyan Directory.
src/projects/little-bamiyan/activate-business-listingBusiness approval/activation email templates and send helpers.

Structure Rule

Use this rule when adding new files:

Type of codePut it in
Shared email wrapper/layoutsrc/layout
Shared email client/helpersrc/_lib
Auth email templatesrc/auth
Contact form email templatesrc/contact-us
Contact form send helpersrc/contact-us
Project workflow emailsrc/projects/<project>/<workflow>
Email-specific typestypes.ts in the module folder

Do not place website-specific server actions inside this package.

A website server action can import templates and send helpers from this package, but the server action itself should stay inside the website project.


Design Principles

This package should contain reusable email templates and email sending helpers.

It should stay focused on email composition and delivery.

What Belongs in @katebtech/emails

Use this package for:

  • React Email templates
  • reusable email layouts
  • reusable email footer components
  • email body components
  • confirmation emails
  • admin notification emails
  • auth emails
  • approval/activation emails
  • reusable send helpers
  • Resend client setup
  • email-specific TypeScript types

Email Package Rule

If the code builds or sends an email, it can belong in @katebtech/emails.

If the code receives a form submission, reads from a database, verifies permissions, or controls website business logic, keep it in the website project.

Good rule:

Website project = server actions, database, env vars, business rules
@katebtech/emails = email templates and send helpers

Module Documentation

Shared Email Layout

Shared email layout components live in:

src/layout
├── EmailFooter.tsx
├── EmailLayout.tsx
├── PoweredByKateb.tsx
├── index.ts
└── types.ts

These components provide the common email wrapper, footer, and Kateb Tech branding used by email templates across the package.

Use this folder for email layout components and layout-related types that are shared by more than one email template.


EmailLayout

Path: src/layout/EmailLayout.tsx

Purpose: Provides the main reusable wrapper for email templates.

EmailLayout creates the full email document structure using React Email components.

It includes:

  • <Html>
  • <Head>
  • <Preview>
  • <Body>
  • main email container
  • optional title section
  • optional intro note
  • main email content area
  • shared organisation footer
  • Kateb Tech powered-by branding

Import:

import { EmailLayout } from "@katebtech/emails/layout";

Basic usage:

import { Text } from "@react-email/components";
import { EmailLayout } from "@katebtech/emails/layout";

export const ExampleEmail = () => {
  return (
    <EmailLayout
      orgInfo={{
        name: "Kateb Tech",
        domain: "katebtech.com.au",
        orgNameHz: "کاتب تک",
        address: "Dandenong VIC, Australia",
        email: "info@example.com",
        phone: "0400 000 000",
      }}
      preview="New enquiry received"
      title="New website enquiry"
      introNote="A visitor submitted the enquiry form."
    >
      <Text>Hello, this is the email content.</Text>
    </EmailLayout>
  );
};

RTL usage:

<EmailLayout
  orgInfo={orgInfo}
  preview="پیام جدید دریافت شد"
  title="پیام جدید"
  introNote="یک پیام جدید از وبسایت دریافت شد."
  dir="rtl"
>
  <Text>متن ایمیل اینجا قرار می‌گیرد.</Text>
</EmailLayout>

Props:

PropTypeDefaultDescription
orgInfoOrgInfoEmailRequiredOrganisation details used by the shared layout and footer.
previewstringRequiredEmail preview text shown by email clients.
titlestringundefinedOptional email title rendered near the top of the email.
childrenReactNodeRequiredMain email body content.
introNotestringundefinedOptional short note displayed under the title.
dir"ltr" | "rtl""ltr"Text direction for the email document.

Related type:

import type { EmailLayoutProps } from "@katebtech/emails/layout";

Notes:

  • Uses @react-email/components.
  • Uses EmailFooter automatically.
  • Uses PoweredByKateb automatically.
  • Use this wrapper for most email templates.
  • Do not place website-specific business logic inside this component.
  • Pass website/organisation data through props.

EmailFooter

Path: src/layout/EmailFooter.tsx

Purpose: Renders a reusable organisation footer for email templates.

It displays:

  • organisation favicon, built from domain
  • organisation name
  • optional Hazaragi organisation name
  • address
  • email link
  • phone number
  • website link, built from domain

Import:

import { EmailFooter } from "@katebtech/emails/layout";

Basic usage:

<EmailFooter
  name="Kateb Tech"
  domain="katebtech.com.au"
  orgNameHz="کاتب تک"
  address="Dandenong VIC, Australia"
  email="info@example.com"
  phone="0400 000 000"
/>

Props:

EmailFooter accepts the OrgInfoEmail fields directly.

Related type:

import type { OrgInfoEmail } from "@katebtech/emails/lib";

Notes:

  • Uses Img and Section from @react-email/components.
  • Email and website are rendered as links.
  • Phone number, address, and Hazaragi organisation name are optional.
  • This component is automatically included inside EmailLayout, so most templates do not need to use it directly.

PoweredByKateb

Path: src/layout/PoweredByKateb.tsx

Purpose: Renders a small “Powered by Kateb Tech” branding block at the bottom of email templates.

It includes:

  • Kateb Tech logo
  • “Powered by Kateb Tech” label
  • link to the Kateb Tech website
  • Kateb Tech brand colours

Import:

import { PoweredByKateb } from "@katebtech/emails/layout";

Basic usage:

<PoweredByKateb />

Notes:

  • Uses KATEB_TECH_LOGO from @katebtech/core.
  • Uses Img, Link, Section, and Text from @react-email/components.
  • This component is automatically included inside EmailLayout.
  • Most email templates do not need to use it directly.
  • Keep this component generic and reusable across all Kateb Tech email templates.

OrgInfoEmail

Path: src/_lib/types.ts

Purpose: Defines the organisation information used by shared email layout components.

OrgInfoEmail is used by:

  • EmailLayout
  • EmailFooter
  • email templates that need organisation branding/contact details

Import:

import type { OrgInfoEmail } from "@katebtech/emails/lib";

Type:

type OrgInfoEmail = {
  email: string;
  name: string;
  domain: string;
  orgNameHz?: string;
  address?: string;
  phone?: string;
};

Example:

const orgInfo: OrgInfoEmail = {
  name: "Kateb Tech",
  domain: "katebtech.com.au",
  orgNameHz: "کاتب تک",
  address: "Dandenong VIC, Australia",
  email: "info@example.com",
  phone: "0400 000 000",
};

Field meaning:

FieldTypeRequiredDescription
emailstringYesContact email shown in the footer.
namestringYesOrganisation name in English.
domainstringYesDomain used for the website link and favicon path.
orgNameHzstringNoOrganisation name in Hazaragi script.
addressstringNoOrganisation address shown in the email footer.
phonestringNoPhone number shown in the footer.

Contact Us Emails

Contact enquiry emails live in:

src/contact-us
├── BusinessEmail.tsx
├── ConfirmationEmail.tsx
├── EnquiryBody.tsx
├── index.ts
├── sendEnquiryEmails.ts
└── types.ts

This module provides reusable email templates and send helpers for website contact/enquiry forms.

Use this module when a website has a standard enquiry flow where:

  1. A visitor submits a contact/enquiry form.
  2. The business receives a notification email.
  3. The visitor receives a confirmation email.
  4. Both emails share the same enquiry details and organisation branding.

sendEnquiryEmails

Path: src/contact-us/sendEnquiryEmails.ts

Purpose: Sends both enquiry emails for a contact form submission.

It sends:

  • business/admin notification email
  • visitor/customer confirmation email

Internally, it calls:

  • sendBusinessEmail
  • sendConfirmationEmail

Import:

import { sendEnquiryEmails } from "@katebtech/emails/contact-us";

Basic usage inside a website server action:

"use server";

import { sendEnquiryEmails } from "@katebtech/emails/contact-us";

export async function submitContactForm(formData: FormData) {
  await sendEnquiryEmails({
    apiKey: process.env.RESEND_API_KEY!,
    orgInfo: {
      name: "Kateb Tech",
      domain: "katebtech.com.au",
      orgNameHz: "کاتب تک",
      address: "Dandenong VIC, Australia",
      email: "info@example.com",
      phone: "0400 000 000",
    },
    enquiry: {
      fullName: "Visitor Name",
      email: "visitor@example.com",
      contactNumber: "0400 000 000",
      qMessage: "I would like to request a quote.",
    },
  });
}

Return value:

Promise<{ message: string }>;

Example success message:

Thanks! We have received your enquiry.

If the visitor confirmation email fails but the business email still runs, the returned message may include:

(Heads-up: we couldn’t send the confirmation email.)

Notes:

  • Uses Promise.allSettled() so one failed email does not automatically stop the other email from completing.
  • Logs business/admin email failure.
  • Logs visitor confirmation email failure.
  • Returns a user-friendly message.
  • The website project should validate form data before calling this helper.
  • The website project should provide the Resend API key through environment variables.

sendBusinessEmail

Path: src/contact-us/BusinessEmail.tsx

Purpose: Sends the business/admin notification email when a website visitor submits an enquiry.

This email is sent to:

data.orgInfo.email;

It uses the visitor’s email as the reply-to address:

replyTo: data.enquiry.email;

This allows the business to reply directly to the visitor from their email client.

Import:

import { sendBusinessEmail } from "@katebtech/emails/contact-us";

Basic usage:

await sendBusinessEmail({
  apiKey: process.env.RESEND_API_KEY!,
  orgInfo,
  enquiry: {
    fullName: "Visitor Name",
    email: "visitor@example.com",
    contactNumber: "0400 000 000",
    qMessage: "I need help with a website.",
  },
});

Email details:

FieldValue
fromWEB_ENQUIRY
todata.orgInfo.email
replyTodata.enquiry.email
subjectWeb Enquiry - ${data.enquiry.fullName}
react<EnquiryBody data={data} title="New Web Enquiry" />

Notes:

  • Uses createEmailClient(data.apiKey).
  • Uses WEB_ENQUIRY from src/_lib/constants.
  • Uses EnquiryBody as the email body.
  • Logs and rethrows errors when sending fails.
  • This is a server-side send helper, not just a React template.

sendConfirmationEmail

Path: src/contact-us/ConfirmationEmail.tsx

Purpose: Sends a confirmation email back to the visitor after they submit an enquiry.

This email is sent to:

data.enquiry.email;

It uses the organisation email as the reply-to address:

replyTo: data.orgInfo.email;

Import:

import { sendConfirmationEmail } from "@katebtech/emails/contact-us";

Basic usage:

await sendConfirmationEmail({
  apiKey: process.env.RESEND_API_KEY!,
  orgInfo,
  enquiry: {
    fullName: "Visitor Name",
    email: "visitor@example.com",
    contactNumber: "0400 000 000",
    qMessage: "I need help with a website.",
  },
});

Email details:

FieldValue
fromFROM_KATEBTECH
todata.enquiry.email
replyTodata.orgInfo.email
subjectYour enquiry is sent to – ${data.orgInfo.name}
react<EnquiryBody dontReplyNote />

Notes:

  • Uses createEmailClient(data.apiKey).
  • Uses FROM_KATEBTECH from src/_lib/constants.
  • Uses EnquiryBody as the email body.
  • Shows a “do not reply” note.
  • Logs and rethrows errors when sending fails.
  • This is a server-side send helper.

EnquiryBody

Path: src/contact-us/EnquiryBody.tsx

Purpose: Renders the shared React Email body for enquiry emails.

It is used by:

  • sendBusinessEmail
  • sendConfirmationEmail

It displays:

  • visitor full name
  • visitor email
  • visitor phone number
  • enquiry message
  • optional “do not reply” note

Import:

import { EnquiryBody } from "@katebtech/emails/contact-us";

Basic usage:

<EnquiryBody data={data} title="New Web Enquiry" />

Confirmation usage with do-not-reply note:

<EnquiryBody
  data={data}
  title="Your enquiry has been sent"
  dontReplyNote
  orgName={data.orgInfo.name}
/>

Props:

EnquiryBodyProps is a union type.

When dontReplyNote is true, orgName is required:

type EnquiryBodyProps = {
  data: SendEnquiryEmails;
  title: string;
  dontReplyNote: true;
  orgName: string;
};

When dontReplyNote is false or omitted, orgName must not be passed:

type EnquiryBodyProps = {
  data: SendEnquiryEmails;
  title: string;
  dontReplyNote?: false;
  orgName?: never;
};

Notes:

  • Uses EmailLayout from src/layout.
  • Uses Hr and Text from @react-email/components.
  • Uses data.orgInfo for organisation branding.
  • Uses data.enquiry for visitor enquiry details.
  • The do-not-reply note is useful for confirmation emails sent from a no-reply or system address.

SendEnquiryEmails

Path: src/contact-us/types.ts

Purpose: Defines the complete payload required to send enquiry emails.

Import:

import type { SendEnquiryEmails } from "@katebtech/emails/contact-us";

Type:

type SendEnquiryEmails = {
  enquiry: {
    fullName: string;
    email: string;
    contactNumber?: string;
    qMessage: string;
  };
  apiKey: string;
  orgInfo: OrgInfoEmail;
};

Field meaning:

FieldTypeDescription
enquiry.fullNamestringVisitor’s full name.
enquiry.emailstringVisitor’s email address.
enquiry.contactNumberstring | undefinedOptional visitor phone number.
enquiry.qMessagestringVisitor’s enquiry message.
apiKeystringResend API key supplied by the website project.
orgInfoOrgInfoEmailOrganisation branding and contact details.

OrgInfoEmail source:

import type { OrgInfoEmail } from "@katebtech/emails/lib";

OrgInfoEmail belongs to the shared email library module because it is used across layout components and send helpers.


EnquiryBodyProps

Path: src/contact-us/types.ts

Purpose: Defines props for the shared EnquiryBody component.

Import:

import type { EnquiryBodyProps } from "@katebtech/emails/contact-us";

Type:

type EnquiryBodyProps =
  | {
      data: SendEnquiryEmails;
      title: string;
      dontReplyNote: true;
      orgName: string;
    }
  | {
      data: SendEnquiryEmails;
      title: string;
      dontReplyNote?: false;
      orgName?: never;
    };

Why it is a union type:

This type prevents accidental usage like:

<EnquiryBody data={data} title="New Web Enquiry" dontReplyNote />

If dontReplyNote is true, TypeScript requires:

orgName={data.orgInfo.name}

This keeps the do-not-reply note complete and avoids missing organisation names in confirmation emails.


Website usage pattern

A website project should usually do this:

  1. Validate form data in the website server action.
  2. Build a SendEnquiryEmails payload.
  3. Call sendEnquiryEmails.
  4. Return the result message to the form state.

Example:

"use server";

import { sendEnquiryEmails } from "@katebtech/emails/contact-us";
import type { SendEnquiryEmails } from "@katebtech/emails/contact-us";

export async function submitContactForm(formData: FormData) {
  const payload: SendEnquiryEmails = {
    apiKey: process.env.RESEND_API_KEY!,
    orgInfo: {
      name: "Kateb Tech",
      domain: "katebtech.com.au",
      orgNameHz: "کاتب تک",
      address: "Dandenong VIC, Australia",
      email: "info@example.com",
      phone: "0400 000 000",
    },
    enquiry: {
      fullName: String(formData.get("fullName") ?? ""),
      email: String(formData.get("email") ?? ""),
      contactNumber: String(formData.get("contactNumber") ?? ""),
      qMessage: String(formData.get("qMessage") ?? ""),
    },
  };

  return sendEnquiryEmails(payload);
}

Auth Emails

Authentication emails live in:

src/auth
├── email-verification.tsx
├── index.ts
└── types.ts

This module provides reusable email templates for authentication-related flows.

Currently, it includes an email verification code template.


EmailVerificationCode

Path: src/auth/email-verification.tsx

Purpose: Renders an email verification code email.

Use this email when a website needs to send a short verification code to confirm a user’s email address.

It displays:

  • English heading
  • recipient greeting
  • short English instruction
  • optional Hazaragi/Dari instruction when otherLanguageKey is "HZ"
  • verification code
  • English ignore-this-email note
  • optional Hazaragi/Dari ignore-this-email note when otherLanguageKey is "HZ"
  • Kateb Tech powered-by branding

Import:

import { EmailVerificationCode } from "@katebtech/emails/auth";

Basic usage:

<EmailVerificationCode
  fullName="Baz Rahimi"
  code="123456"
  otherLanguageKey="HZ"
/>

When fullName is not provided, the greeting falls back to:

Hi there,

EmailVerificationCodeProps

Path: src/auth/types.ts

Purpose: Defines the public props type for the email verification template.

Import:

import type { EmailVerificationCodeProps } from "@katebtech/emails/auth";

Type:

type EmailVerificationCodeProps = {
  fullName?: string;
  code: string;
  otherLanguageKey: OtherLanguageKey;
};

Props:

PropTypeDefaultDescription
fullNamestringundefinedOptional recipient name used in the greeting.
codestringRequiredVerification code shown in the email, usually a 6-digit code.
otherLanguageKeyOtherLanguageKeyRequiredPass the supported secondary language key. Currently "HZ" renders the Hazaragi/Dari RTL text.

OtherLanguageKey comes from @katebtech/core. At the moment, the supported value is "HZ".


Email content

The template includes this English instruction:

Use the 6-digit code below to confirm your email address. It expires in 10 minutes.

It also includes Hazaragi/Dari RTL instructions for bilingual users when otherLanguageKey is "HZ".

The verification code is displayed in a styled code box with:

  • large font size
  • bold text
  • extra letter spacing
  • light border
  • light background

Dark mode handling

This email includes light-mode rendering hints inside <Head>:

<meta name="color-scheme" content="light" />
<meta name="supported-color-schemes" content="light" />

It also includes inline CSS to reduce unwanted dark-mode colour inversion in email clients.

Important classes:

email-container
email-code-box
email-footer

These are used to keep the background and text colours stable across email clients where possible.


Powered by Kateb Tech

EmailVerificationCode includes:

<PoweredByKateb />

from:

src/layout/PoweredByKateb.tsx

This means the email automatically includes the Kateb Tech branding block at the bottom.


Typical website usage

A website project should usually do this:

  1. Generate the verification code in the website project.
  2. Save or store the code securely with an expiry time.
  3. Send the email using the website’s email sending logic.
  4. Render EmailVerificationCode as the React email body.

Example:

"use server";

import { EmailVerificationCode } from "@katebtech/emails/auth";
import { createEmailClient } from "@katebtech/emails/lib";
import type { OtherLanguageKey } from "@katebtech/core/content";

export async function sendVerificationCodeEmail({
  apiKey,
  to,
  fullName,
  code,
  otherLanguageKey,
}: {
  apiKey: string;
  to: string;
  fullName?: string;
  code: string;
  otherLanguageKey: OtherLanguageKey;
}) {
  const emailClient = createEmailClient(apiKey);

  return emailClient.emails.send({
    from: "Kateb Tech <no-reply@example.com>",
    to: [to],
    subject: "Your verification code",
    react: (
      <EmailVerificationCode
        fullName={fullName}
        code={code}
        otherLanguageKey={otherLanguageKey}
      />
    ),
  });
}

Auth email module rules

Use these rules when changing auth email templates.

What belongs here:

email verification templates
password reset email templates
login code email templates
account confirmation email templates
auth email props/types

Project-Specific Emails

Project-specific emails live in:

src/projects
└── little-bamiyan
    ├── activate-business-listing
    │   ├── BusinessApprovedEmailBody.tsx
    │   ├── index.ts
    │   ├── sendActivationEmails.ts
    │   ├── sendBusinessApprovedEmail.tsx
    │   └── types.ts
    └── index.ts

The src/projects folder is used for email workflows that belong to a specific Kateb Tech project.

Use this folder when an email is not generic enough to belong in auth, contact-us, or layout.

Current project modules:

  • little-bamiyan

Project folder rule

Use this structure for project-specific emails:

src/projects/<project-name>/<workflow-name>

Examples:

src/projects/little-bamiyan/activate-business-listing
src/projects/kateb-offices/booking-confirmation
src/projects/hazara-cultural-association/event-registration

Use project folders for emails that are tied to one product, platform, or client workflow.


What belongs in src/projects

Good examples:

Little Bamiyan business approval emails
Little Bamiyan listing activation emails
project-specific admin notification emails
project-specific booking emails
project-specific event emails
project-specific directory emails

Little Bamiyan Emails

Little Bamiyan emails live in:

src/projects/little-bamiyan

Current workflow:

activate-business-listing

This workflow sends an email when a business listing is approved and activated on Little Bamiyan Directory.


activate-business-listing

Path: src/projects/little-bamiyan/activate-business-listing

Purpose: Provides the email body and send helpers for the Little Bamiyan business listing approval workflow.

Use this workflow when a submitted business listing has been reviewed and approved.

It can:

  • send an approval email to the business owner
  • include the approved business listing URL
  • include English and Hazaragi/Dari content
  • use the shared EmailLayout
  • use organisation branding from orgInfo
  • return a friendly status message to the admin workflow

BusinessApprovedEmailBody

Path: src/projects/little-bamiyan/activate-business-listing/BusinessApprovedEmailBody.tsx

Purpose: Renders the email body for a business listing approval email.

It displays:

  • greeting using the business name
  • English approval message
  • Hazaragi approval message
  • listing URL
  • instruction to contact the organisation if details need updating
  • do-not-reply note
  • shared email footer and Kateb Tech branding through EmailLayout

Import:

import { BusinessApprovedEmailBody } from "@katebtech/emails/projects/little-bamiyan";

Basic usage:

<BusinessApprovedEmailBody data={data} />

Notes:

  • Uses EmailLayout from src/layout.
  • Uses Hr, Link, and Text from @react-email/components.
  • Uses RTL styling for Hazaragi/Dari text.
  • Uses data.businessActivation.fullUrl when available.
  • Uses data.orgInfo for organisation branding.

BusinessApprovedEmailBodyProps

Path: src/projects/little-bamiyan/activate-business-listing/types.ts

Purpose: Defines props for the business approval email body component.

Import:

import type { BusinessApprovedEmailBodyProps } from "@katebtech/emails/projects/little-bamiyan";

Type:

type BusinessApprovedEmailBodyProps = {
  data: SendActivationEmails;
};

sendBusinessApprovedEmail

Path: src/projects/little-bamiyan/activate-business-listing/sendBusinessApprovedEmail.tsx

Purpose: Sends one business listing approval email to the approved business owner.

Import:

import { sendBusinessApprovedEmail } from "@katebtech/emails/projects/little-bamiyan";

Basic usage:

await sendBusinessApprovedEmail({
  apiKey: process.env.RESEND_API_KEY!,
  orgInfo,
  businessActivation: {
    name: "Example Business",
    email: "owner@example.com",
    fullUrl: "https://www.littlebamiyan.com.au/business/example-business",
  },
});

Email details:

FieldValue
fromLittle Bamiyan Directory <handler@katebtech.com.au>
todata.businessActivation.email
replyTodata.orgInfo.email
subjectYour business listing has been approved – ${data.orgInfo.name}
react<BusinessApprovedEmailBody data={data} />

Notes:

  • Uses createEmailClient(data.apiKey).
  • Sends to the approved business owner.
  • Uses the organisation email as the reply-to address.
  • Throws the error if sending fails.

sendActivationEmails

Path: src/projects/little-bamiyan/activate-business-listing/sendActivationEmails.ts

Purpose: Runs the business listing activation email workflow.

Currently it sends:

  • business approval email to the business owner

Import:

import { sendActivationEmails } from "@katebtech/emails/projects/little-bamiyan";

Basic usage:

const result = await sendActivationEmails({
  apiKey: process.env.RESEND_API_KEY!,
  orgInfo,
  businessActivation: {
    name: "Example Business",
    email: "owner@example.com",
    fullUrl: "https://www.littlebamiyan.com.au/business/example-business",
  },
});

console.log(result.message);

Return value:

Promise<{ message: string }>;

Default success message:

Business status updated.

If the approval email fails:

Business status updated. (Listing was approved, but approval email could not be sent.)

Notes:

  • Uses Promise.allSettled().
  • The workflow can be extended later if activation needs to send more than one email.
  • The website/admin project should update the business listing status in the database.
  • This helper only handles email sending.

SendActivationEmails

Path: src/projects/little-bamiyan/activate-business-listing/types.ts

Purpose: Defines the payload required for the Little Bamiyan business listing activation email workflow.

Import:

import type { SendActivationEmails } from "@katebtech/emails/projects/little-bamiyan";

Type:

type SendActivationEmails = {
  businessActivation: {
    name: string;
    email: string;
    fullUrl: string;
  };
  apiKey: string;
  orgInfo: OrgInfoEmail;
};

Field meaning:

FieldTypeDescription
businessActivation.namestringApproved business name.
businessActivation.emailstringBusiness owner email address.
businessActivation.fullUrlstringPublic URL of the approved listing.
apiKeystringResend API key supplied by the website/admin project.
orgInfoOrgInfoEmailOrganisation branding and contact details.

Website/admin usage pattern

A website or admin project should usually do this:

  1. Admin approves the business listing in the database.
  2. Website/admin project builds the SendActivationEmails payload.
  3. Website/admin project calls sendActivationEmails.
  4. Website/admin project displays the returned message to the admin user.

Example:

"use server";

import { sendActivationEmails } from "@katebtech/emails/projects/little-bamiyan";
import type { SendActivationEmails } from "@katebtech/emails/projects/little-bamiyan";

export async function approveBusinessListing() {
  // 1. Update listing status in the database inside the website/admin project.

  const payload: SendActivationEmails = {
    apiKey: process.env.RESEND_API_KEY!,
    orgInfo: {
      name: "Little Bamiyan Directory",
      domain: "littlebamiyan.com.au",
      orgNameHz: "دایرکتوری لیتل بامیان",
      address: "Melbourne, Australia",
      email: "info@littlebamiyan.com.au",
      phone: "0400 000 000",
    },
    businessActivation: {
      name: "Example Business",
      email: "owner@example.com",
      fullUrl: "https://www.littlebamiyan.com.au/business/example-business",
    },
  };

  return sendActivationEmails(payload);
}

Email Library Helpers

Email library helpers live in:

src/_lib
├── constants.ts
├── createEmailSerivce.ts
├── emailClient.ts
├── types.ts
└── index.ts

This module provides shared utilities used by email send helpers across the package.

createEmailClient

Path: src/_lib/emailClient.ts

Purpose: Creates a Resend email client using the API key provided by the website project.

Import:

import { createEmailClient } from "@katebtech/emails/lib";

Basic usage:

const emailClient = createEmailClient(process.env.RESEND_API_KEY!);

Send email example:

import { createEmailClient } from "@katebtech/emails/lib";

const emailClient = createEmailClient(process.env.RESEND_API_KEY!);

await emailClient.emails.send({
  from: "Web Enquiry <handler@katebtech.com.au>",
  to: ["business@example.com"],
  subject: "New enquiry",
  react: <div>Hello</div>,
});

Implementation:

import { Resend } from "resend";

export const createEmailClient = (apiKey: string) => {
  return new Resend(apiKey);
};

Notes:

  • Uses the resend package.
  • The API key must come from the website project.
  • Do not hard-code the Resend API key inside this package.
  • Website projects should usually pass process.env.RESEND_API_KEY.
  • This helper keeps Resend setup consistent across email modules.

createEmailService

Path: src/_lib/createEmailSerivce.ts

Purpose: Creates a small email service wrapper around Resend with reusable sender identity helpers.

Use this when a website or project needs a Resend client and wants consistent from values built from the shared Kateb Tech handler address.

Import:

import { createEmailService } from "@katebtech/emails/lib";

Basic usage:

const emailService = createEmailService({
  apiKey: process.env.RESEND_API_KEY!,
  organisationName: "Little Bamiyan Directory",
});

await emailService.resend.emails.send({
  from: emailService.from.organisation,
  to: ["owner@example.com"],
  subject: "Your business listing has been approved",
  react: <div>Your listing is live.</div>,
});

Sender helpers:

HelperReturns
from.organisation${organisationName} <handler@katebtech.com.au>
from.formSubmission()${formName} Submission <handler@katebtech.com.au>
from.custom()${senderName} <handler@katebtech.com.au>

Notes:

  • Throws Email sender name is required. when a custom sender name is blank.
  • Trims sender names before building the final from value.
  • Uses the shared KATEB_EMAIL_HANDLER constant from src/_lib/constants.
  • The source file is currently named createEmailSerivce.ts, while the exported helper is createEmailService.

KATEB_EMAIL_HANDLER

Path: src/_lib/constants.ts

Purpose:
Shared Kateb Tech handler email address used to build sender identities.

Import:

import { KATEB_EMAIL_HANDLER } from "@katebtech/emails/lib";

Value:

export const KATEB_EMAIL_HANDLER = "<handler@katebtech.com.au>";

Notes:

  • Used by shared sender constants and createEmailService.
  • Use this to build project-specific sender identities that still send through the Kateb Tech handler address.

FROM_KATEBTECH

Path: src/_lib/constants.ts

Purpose: Default sender identity for emails sent from Kateb Tech.

Import:

import { FROM_KATEBTECH } from "@katebtech/emails/lib";

Value:

export const FROM_KATEBTECH = "Kateb Tech <handler@katebtech.com.au>";

Typical usage:

await emailClient.emails.send({
  from: FROM_KATEBTECH,
  to: ["user@example.com"],
  subject: "Your verification code",
  react: <EmailVerificationCode code="123456" otherLanguageKey="HZ" />,
});

Notes:

  • Use this for general Kateb Tech system emails.
  • Good examples include verification emails, confirmation emails, and platform notices.
  • For project-specific senders, create a project-specific sender constant inside the project module.

WEB_ENQUIRY

Path: src/_lib/constants.ts

Purpose: Default sender identity for website enquiry emails.

Import:

import { WEB_ENQUIRY } from "@katebtech/emails/lib";

Value:

export const WEB_ENQUIRY = "Web Enquiry <handler@katebtech.com.au>";

Typical usage:

await emailClient.emails.send({
  from: WEB_ENQUIRY,
  to: ["business@example.com"],
  replyTo: "visitor@example.com",
  subject: "Web Enquiry - Visitor Name",
  react: <EnquiryBody data={data} title="New Web Enquiry" />,
});

Notes:

  • Use this for contact form notifications sent to a business/admin.
  • The replyTo should usually be the visitor’s email address.
  • This allows the business to reply directly to the visitor.

On this page