Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Occasion Vault (The Diary)

Description / Background

Occasion Vault (customer-facing name: The Diary) is a profile-based occasion system that helps members remember important dates and discover relevant restaurants before those moments arrive. The goal is to turn personal occasions into timely reminders, curated discovery, and higher-intent bookings.

Current State:

  • Waiting to ship
  • Available on web and mobile web/hybrid
  • Native app parity not confirmed in this PRD

Objectives

Markets: TH (Thailand), SG (Singapore), MY (Malaysia)

  • Enable customers to track and manage personal occasions (birthdays, anniversaries, cultural occasions)
  • Provide automatic reminders before occasions (T-14 and T-7)
  • Drive restaurant discovery through 3-tier waterfall resolution
  • Support one-tap Google Calendar export without OAuth

Scope

In Scope

  • Birthday auto-sync from existing user profile
  • Add/edit/delete occasions (anniversaries, cultural occasions, custom dates)
  • Market-aware suggested occasions with prefilled dates
  • Toggle reminders on/off per occasion
  • T-14 and T-7 reminder emails and FCM push notifications
  • 3-tier restaurant discovery waterfall (GLP → Tag-based fallback → Suppression)
  • One-tap Google Calendar export
  • Profile action button with GLP/fallback logic
  • Multi-language support (EN, TH, CN, and other locales)

Out of Scope

  • Native mobile app implementation
  • Netcore event routing
  • In-app deep linking for non-Reservation/Reward content

User Stories

Birthday by Default

  • Auto-sync the existing birthday into a Diary card without forcing setup work from the user
  • Let users toggle reminders on/off while keeping birthday edits anchored in Account Settings

Add and Manage Occasions

  • Show market-aware suggested occasions with prefilled dates
  • Support anniversaries, cultural occasions, Valentine’s Day, and freeform custom dates
  • Provide toggle, edit, and delete behavior based on occasion type

Reminder and Discovery Engine

  • Send T-14 and T-7 reminders for active occasions
  • Resolve each reminder through a 3-tier waterfall:
    • Tier 1: Dedicated GLP (Group Landing Page)
    • Tier 2: Rule-based fallback list
    • Tier 3: Suppression if supply is too weak

Profile Actions

  • Reuse the same GLP/fallback logic for the Diary action button on each card
  • Support one-tap Google Calendar export without OAuth

Location

Web / Hybrid

  • Member profile page (desktop and mobile)
  • Diary section in profile navigation
  • Dedicated diary route

How to Find The Diary

  1. Navigate to HungryHub website or app
  2. Access Profile page
  3. Find Diary section
  4. View existing occasions or add new ones

How to Use

View Occasions

  1. Navigate to Profile > Diary
  2. View all saved occasions with upcoming dates
  3. See suggested occasions based on market

Add Occasion

  1. Click “Add Occasion” button
  2. Select from suggested occasions or create custom
  3. Set date (month/day/year)
  4. Toggle reminder on/off
  5. Save occasion

Manage Occasion

  1. Tap occasion card
  2. Edit date or toggle reminder
  3. Delete occasion if needed

Export to Calendar

  1. Tap calendar icon on occasion card
  2. One-tap Google Calendar event created

3-Tier Restaurant Discovery Waterfall

Occasion Reminder Triggered
           │
           ▼
    ┌──────────────┐
    │  Tier 1:     │
    │  Dedicated   │
    │  GLP         │
    └──────────────┘
           │
    ┌──────┴──────┐
    │ Has valid   │
    │ GLP?        │
    └──────┬──────┘
       Yes │ No
           ▼ ▼
    ┌──────────────┐     ┌──────────────┐
    │ Return GLP   │     │  Tier 2:     │
    │ deep link   │     │  Tag-based   │
    └──────────────┘     │  fallback    │
                         └──────────────┘
                                │
                         ┌───────┴───────┐
                         │ Has matching  │
                         │ restaurants?  │
                         └───────┬───────┘
                            Yes │ No
                                ▼ ▼
                         ┌──────────────┐   ┌──────────────┐
                         │ Return       │   │  Tier 3:     │
                         │ fallback     │   │  Suppress    │
                         │ deep link    │   │  (no link)   │
                         └──────────────┘   └──────────────┘

Backend Implementation

New Models

  • user_occasions — per-user occasion rows
    • occasion_type: birthday, anniversary, cultural, custom
    • occasion_key: unique identifier
    • label: custom label
    • month, day, year: date components
    • is_active: boolean
    • source: system, user_created
    • reminder_last_sent_year: for idempotency
  • occasion_glp_mappings — maps occasion to Group Landing Page
  • cultural_occasion_dates — fixed-date cultural occasions (Songkran, Loy Krathong, etc.)
  • occasion_fallback_rules — cascading fallback rules

New Services

  • AddOccasionService / EditOccasionService / DeleteOccasionService / ToggleOccasionService
  • ListUserOccasionsService — returns active cards + suggested occasions
  • SyncBirthdayOccasionService — auto-syncs birthday from users table
  • ListOccasionTypesService — enumerates supported occasion types
  • MarketResolver + ResolveOccasionService — 3-tier waterfall resolution

API Endpoints (Api::V5::DiaryController)

MethodPathDescription
GET/diaryList user occasions
POST/diaryCreate occasion
PATCH/diary/:idUpdate occasion
DELETE/diary/:idDelete occasion
POST/diary/:id/toggleToggle reminder
GET/diary/occasion_typesList occasion types
POST/diary/calendar_linkGenerate Google Calendar link
GET/diary/suggestedGet suggested occasions

Feature Flag

  • ff_occasion_vault_v1 — gates all user-visible behavior

Migrations

  • 20260806100000_create_user_occasions.rb
  • 20260806100001_create_occasion_glp_mappings.rb
  • 20260806100002_create_cultural_occasion_dates.rb
  • 20260806100003_create_occasion_fallback_rules.rb

PR: https://github.com/hungryhub-team/hh-server/pull/8644


Hybrid Implementation

Components

  • DiarySection.vue — Responsive diary section and interaction orchestration
  • AddOccasion.ts — Occasion forms across modal and sheet layouts
  • CalendarPopup.vue — Date, month, and year picker views
  • fetchDiarySuggested.ts — Suggested occasion selection
  • occasionIcon.ts — Reusable diary cards with occasion icons
  • useDiaryPermissions.ts — Manage diary reminders, permissions, mobile actions
  • DiaryDeleteOccasionConfirm.vue — Help, tooltip, confirmation overlays
  • InfoBanner.vue — Diary controls, notices, loading skeletons

Mobile Components

  • AddOccasionBottomSheet.vue
  • AddOccasionForm.vue
  • DiaryManageBottomSheet.vue
  • DiaryItemCard.vue
  • DiaryTurnOffRemindersConfirm.vue

State Management

  • useDiary.ts — Core diary state
  • useAddOccasionForm.ts — Form handling
  • useDiaryReminders.ts — Reminder management
  • useDiaryToggle.ts — Toggle functionality

API Integration

  • DiaryOccasion.ts — CRUD endpoints
  • createDiaryOccasion.ts — Create API module
  • updateDiaryOccasion.ts — Update API module
  • deleteDiaryOccasion.ts — Delete API module
  • toggleDiaryOccasion.ts — Toggle API module

Routing

  • Desktop: Dedicated diary navigation route
  • Mobile: Embedded in profile page

Localization

  • Multi-language support (EN, TH, CN, and other locales)
  • Localized diary content in profile.json

PR: https://github.com/hungryhub-team/hh-pegasus/pull/3136


PRD & Task


Design

TBD


API Blueprint

MethodPathURLDescriptionPayload
GET/api/v5/diaryList user occasions
POST/api/v5/diaryCreate occasion{ occasion_type, occasion_key, label, month, day, year, is_active }
PATCH/api/v5/diary/:idUpdate occasion{ label, month, day, year, is_active }
DELETE/api/v5/diary/:idDelete occasion
POST/api/v5/diary/:id/toggleToggle reminder{ is_active }
GET/api/v5/diary/occasion_typesList occasion types
POST/api/v5/diary/calendar_linkGenerate calendar link{ occasion_id }
GET/api/v5/diary/suggestedGet suggested occasions

DB Schema / Database Migration

user_occasions

ColumnTypeDescription
idbigintPrimary key
user_idbigintForeign key to users
occasion_typestringbirthday, anniversary, cultural, custom
occasion_keystringUnique identifier
labelstringCustom label
monthintegerMonth (1-12)
dayintegerDay (1-31)
yearintegerYear (nullable)
is_activebooleanReminder active flag
sourcestringsystem, user_created
reminder_last_sent_yearintegerFor idempotency
created_atdatetime
updated_atdatetime

occasion_glp_mappings

ColumnTypeDescription
idbigintPrimary key
occasion_typestring
occasion_keystring
glp_idbigintForeign key to GLPs
created_atdatetime
updated_atdatetime

cultural_occasion_dates

ColumnTypeDescription
idbigintPrimary key
occasion_keystringUnique identifier
namestringDisplay name
monthinteger
dayinteger
created_atdatetime
updated_atdatetime

occasion_fallback_rules

ColumnTypeDescription
idbigintPrimary key
occasion_typestring
country_idbigint
tagstext[]Fallback tags
created_atdatetime
updated_atdatetime

Improvement:

Feature NameDateWhat ChangedDescription
Occasion Vault2026-09Initial ReleaseNew profile-based occasion system with reminders and restaurant discovery

Constraints / Non-Goals

  • Customer-facing name is The Diary; Occasion Vault is only the internal working name
  • No design link provided in the PRD