Skip to Content
FeaturesImportant Days (Occasions) — Feature Doc

Important Days (Occasions) — Feature Doc

Status: [Live — April 2026]


Overview

The Important Days feature enables Leadmetrics to surface upcoming public holidays, cultural occasions, and observance days to both DMs and clients, and makes it one-click to generate a branded occasion post.

Three surfaces are active:

  1. Manage portal /system/important-days — super admins curate the catalogue
  2. DM portal /occasions — DMs browse upcoming days and trigger posts per tenant
  3. Dashboard widget — clients see the next 3 occasions with inline “Create post” buttons

Data Model

model ImportantDay { id String @id @default(cuid()) name String // "Labour Day", "Diwali" description String? month Int // 1–12 day Int // 1–31 year Int? // null = recurring annually isGlobal Boolean @default(false) // shows for all tenants countries String[] // ["IN", "US"] — ISO 3166 platforms String[] // default platforms to suggest isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }

Country matching: Tenant.country (existing field) is matched against ImportantDay.countries. isGlobal = true overrides — shown for all tenants regardless of country.

Seed data: 15 occasions loaded by packages/db/src/seed.ts — India-heavy (Republic Day, Gandhi Jayanti, Independence Day, Children’s Day, Holi, Ganesh Chaturthi, Dussehra, Diwali) + global (New Year, Valentine’s Day, Women’s Day, Earth Day, Labour Day, Christmas, New Year’s Eve).


Scheduler Job

Runs daily at 9:00 AM via the apps/servers/scheduler DB-poll architecture.

Handler: apps/servers/scheduler/src/handlers/occasion-reminders.ts

Logic:

  1. Find all ImportantDay where month + day = today + 4 days
  2. For each day: find all active tenants whose country matches (or isGlobal = true)
  3. Per tenant: guard check — if any Activity with contentType = "occasion" and matching name was created this week, skip (dedup)
  4. If not deduped: call enqueueNotification() with type: "occasion_reminder", templateSlug: "occasion-reminder", containing occasionName, daysUntil, date

Scheduler task record: type: "occasion.daily.check", runs daily. Registered in task-runner.ts.


API Routes

Admin routes (/admin/v1/, requireSuperAdmin)

MethodRouteDescription
GET/admin/v1/important-daysList with ?month=&country=&active= filters
POST/admin/v1/important-daysCreate
PATCH/admin/v1/important-days/:idUpdate
DELETE/admin/v1/important-days/:idDelete

Tenant routes (/tenant/v1/, requireTenantUser)

MethodRouteDescription
GET/tenant/v1/important-days/upcomingNext 30 days for tenant’s country
POST/tenant/v1/important-days/:id/create-postCreate occasion social post

DM routes (/dm/v1/, requireDMAccess)

MethodRouteDescription
GET/dm/v1/important-days/upcoming?tenantId=Next 30 days for tenant’s country
POST/dm/v1/important-days/:id/create-postCreate occasion social post { tenantId, platform, format? }

Occasion Post Flow

Triggered from any surface:

DM clicks "Create post" (or client clicks from widget) → Activity created: deliverableType=social_post, contentType=occasion, dueDate=next occurrence → SocialPost created: contentType=occasion, status=queued → BullMQ job enqueued on social-post-writer → social-post-writer: festive prompt for occasion contentType → status: dm_review → DM approves → social-post-designer: SCENE_MAP["occasion"] visual → status: client_review → Client approves → social-publisher publishes

Writer prompt (social-post-writer.worker.ts line ~145): When contentType === "occasion":

  • Opens with a festive greeting for the occasion name
  • Keeps brand tone warm and celebratory
  • Positions brand as wishing audience well
  • Ends with relevant hashtags

Designer scene (SCENE_MAP[“occasion”]):

“Festive graphic composition with bold celebratory typography, brand colours prominent on a clean minimal background, warm and joyful atmosphere, occasion-themed visual elements, professional yet celebratory — suitable for greeting and brand awareness”


Dashboard Widget

File: apps/dashboard/src/app/(dashboard)/dashboard/UpcomingOccasionsCard.tsx

Shown in: RegularDashboard (stage ≥ 5 tenants only), after the PendingApprovalsCard.

Data fetched: getUpcomingOccasions(tenantId) in page.tsx — direct DB query (no API fetch), returns up to 3 occasions in the next 30 days sorted by daysUntil.

Server action: createOccasionPost(importantDayId, platform) in actions.ts.

UX:

  • Card with violet border, CalendarDays icon header
  • Each occasion shows name + urgency (“in 4 days”) + inline “Create post” button
  • Click expands a platform dropdown + “Generate post” button
  • On success: “Post queued” confirmation replaces the button

DM Portal Page

Route: /occasions (DM portal) File: apps/dm/src/app/(dm)/occasions/page.tsx + OccasionsClient.tsx

Shows all upcoming occasions for the active tenant (next 30 days). Grid layout with search filter. Each card shows occasion name, date, description, urgency badge, and a platform dropdown + “Create post” button.

Proxy API routes:

  • apps/dm/src/app/api/important-days/route.ts (GET)
  • apps/dm/src/app/api/important-days/[id]/create-post/route.ts (POST)

Files Reference

FilePurpose
packages/db/prisma/schema.prismaImportantDay model
packages/db/src/seed.ts15 seeded occasions
apps/api/src/routers/admin/important-days.tsAdmin CRUD
apps/api/src/routers/tenant/important-days.tsDashboard/client API
apps/api/src/routers/dm/important-days.tsDM portal API
apps/manage/src/app/(manage)/system/important-days/page.tsxManage list page
apps/manage/src/app/(manage)/system/important-days/ImportantDaysClient.tsxManage client component
apps/manage/src/app/actions/important-days.tsManage server actions
apps/servers/scheduler/src/handlers/occasion-reminders.tsDaily notification trigger
apps/dashboard/src/app/(dashboard)/dashboard/UpcomingOccasionsCard.tsxDashboard widget
apps/dashboard/src/app/(dashboard)/dashboard/actions.tsDashboard server action
apps/dm/src/app/(dm)/occasions/page.tsxDM occasions page
apps/dm/src/app/(dm)/occasions/OccasionsClient.tsxDM occasions client
apps/dm/src/app/api/important-days/route.tsDM proxy: GET
apps/dm/src/app/api/important-days/[id]/create-post/route.tsDM proxy: POST
packages/agents/src/workers/social-post-writer.worker.tsOccasion prompt (line ~145)
packages/agents/src/workers/social-post-designer.worker.tsSCENE_MAP[“occasion”]

© 2026 Leadmetrics — Internal use only