Phase 1 step 2 — introduce the publisher primitive that emits
notification.requested envelopes through the RFC-004 transactional
outbox. Listeners and command handlers will migrate onto this in a
follow-up commit (flag-gated cutover).
- NotificationsPublisher exposes:
* publishWithin(tx, input) — preferred; appends to outbox inside an
existing Prisma transaction so the row commits atomically with the
domain mutation that triggered the notification
* publishStandalone(input) — opens a single-row tx; convenience for
callers that don't already own one
- Builds EventEnvelope<NotificationRequestedPayload> via the Phase 0
envelope builder (UUIDv7 eventId, current trace id, ISO occurredAt)
- Producer string: "goodgo-api/notifications"; eventType:
"notification.requested"
- aggregateId on outbox row = notificationId for downstream tracing
- Optional fields (locale, priority, dedupeKey) only included when set,
matching the JSON Schema's additionalProperties=false contract
Tests (4 specs, all green):
- publishWithin builds a valid envelope (assertValidEnvelope) and writes
to the supplied tx with aggregateId
- publishStandalone opens its own transaction
- Optional fields are omitted when undefined and preserved when provided
- UUIDv7 eventIds are strictly time-ordered between successive calls
Not yet wired in NotificationsModule providers — that lands with the
listener cutover in the next commit so we don't ship dead DI nodes.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Create apps/web/lib/phone.ts with VN_PHONE_REGEX, normalizePhone,
formatPhone, and zaloHref helpers
- Deduplicate phone regex: auth.ts and inquiry.ts now import VN_PHONE_REGEX
from @/lib/phone instead of defining their own local patterns
- Replace raw .replace(/^0/, '84') in inquiry-detail-dialog.tsx and
lead-detail-dialog.tsx with zaloHref(); use formatPhone() for display
Resolves GOO-209
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Rewrite prisma/seed.ts to populate all 27 models with realistic
Vietnamese real estate data (8 users with login, 10 properties,
10 listings, orders, payments, reviews, notifications, etc.)
- Replace all emoji icons with Lucide React SVG icons across frontend
for consistent rendering, sizing, and accessibility
- Redesign dashboard nav: grouped sidebar with section headers,
primary/secondary split on desktop, icon-only secondary items
- Replace language switcher flag emoji with Globe icon
- Replace SVG theme toggle with Lucide Moon/Sun icons
- Fix API startup: graceful fallback for Sentry profiling, Google OAuth,
and Zalo OAuth when credentials are not configured
- Relax rate limiting in development mode (10k req/min)
- Fix listings API to include media[] array in search response
- Add optional chaining for property.media across frontend components
- Update OAuth strategy tests to match graceful fallback behavior
Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>