Recipes
Blocks combined into one product, with the config, the event flow and the UI components to pair them with.
A recipe installs several blocks on one subject table and wires their
events together, so a comment, a tag, a stage move or a reminder reaches the
people watching that row. Each page has the table, the
better-supabase.config.ts, the event flow and the
ReUI components that fit the data. better-supabase types
the data those components take structurally and does not depend on ReUI.
| Recipe | Subject | Blocks |
|---|---|---|
| CRM | deal | comments, tags, custom-fields, rank, reminders, state-machine |
| Project management | task | teams, comments, tags, rank, reminders, number-sequences, state-machine |
| Drive | document | comments, tags, attachments, share-links, rank |
| Scheduling | booking | custom-fields, share-links, reminders, state-machine |
Every recipe also installs organizations, outbox and notifications.
tests/validation-recipes renders each recipe's modules into one migration,
applies it to the local stack and drives the flows on these pages end to
end.
The shared wiring
Three pieces connect the blocks in every recipe.
The subject is declared once in config.subjects. Comments, tags, custom
fields, share links and reminders all read it from the
subject registry, and rank reads its rank entry.
A state machine is named after its subject (deal, task, booking). The
machine name is the subjectType of its history rows and of
status.changed, so a status change reaches the same watchers as a comment.
A cron route hands the outbox to the watchers. watcherHandler reads the
actor from each outbox row, so whoever acts is not notified of their own
change:
import { watcherHandler } from "better-supabase/blocks/notifications";
const watchers = watcherHandler(notifications, {
types: {
"comment.created": "subject.commented",
"subject.tagged": "subject.tagged",
"status.changed": "subject.moved",
"share_link.created": "subject.shared",
"field_value.set": "subject.field_changed",
},
});
export const GET = outbox.relayRoute({
secret: process.env.CRON_SECRET,
consumers: { watchers },
});Users follow a row with notifications.subscribe({ subject, level }).
Watchers at level all get every mapped event. See
watchers for rules with activity,
recipients and summary.
Reminders deliver through the notifications block when createBlocks gets
reminderNotification. The reminder's owner and the subject's watchers get
one notification per occurrence:
const { comments, reminders, stateMachine } = createBlocks(
{ transport, service, reminderNotification: "reminder.due" },
{
notifications: (options) => createNotifications({ ...options, types }),
comments: createComments,
reminders: createReminders,
stateMachine: createStateMachine,
},
);reminders.job runs as the handler of the reminders.deliver queue
(delivery).
Last updated on