Extending the Platform
Adding a new product type or notification.
General principle
New business logic belongs in packages/shared, used identically by
apps/web, apps/mobile, and apps/customer — never duplicated per
app. If you find yourself writing the same pricing or validation logic
twice, that's a sign it belongs in packages/shared instead.
Adding a new notification
- Write the send function in
packages/shared/src/email.ts(or the relevant file), following the existing pattern: accept plain data (not an entity ID to look up, where reasonably avoidable), callsendTemplatedEmail. - Register the template in
packages/shared/src/emailTemplates.ts— this is what makes it appear in /admin/email-templates as owner-editable. - If it's a customer-facing notification, make sure whatever triggers it
respects
Customer.notificationsSuppressed(this is handled centrally bysendTemplatedEmailalready — you don't need to check it yourself). - If it's a staff alert rather than a customer email, use the existing
sendStaffAlertEmail/resolveStaffByRolepattern instead of building a new one.
Adding an approval gate to a new action
Reuse shouldRequireApproval/createPendingApproval from
packages/shared/src/approvals.ts if the action fits the existing
role-based or threshold-based shape. If it doesn't cleanly fit (for
example, no staff member is available to "propose" it, or it needs to
affect more than one record), a hold-and-alert pattern — complete the
action but withhold the risky part and notify staff — is the established
alternative; see how the reschedule-refund gate does this.
Adding a new product type
Look at how an existing similar feature is structured end to end — a
Prisma model, a shared business-logic file in packages/shared, a public
page if it needs one, an admin page for staff management, and its own
notification templates — rather than starting from a blank pattern. Real
money and (often) real compliance concerns mean a new product type should
follow the same rigor as an existing one, not a lighter-weight shortcut.
Testing
packages/shared's test suite (npm run test:shared) covers pricing,
approval logic, and other shared business rules — add to it when you
change any of that, not just when you add something brand new.