Built No-show flag and block: Two new numbers under Booking Rules (blank means off). A symbol appears on the clients list, client page, calendar entries and appointment pages. Online booking is refused at the block number, and customers are told at the details step. Staff can still book a blocked customer in, and an owner can reset someone with a two-tap button. The count resets when they attend, and matches the same email or phone, so a guest can’t dodge it with a new profile. Email rules: staff and owners get email only for customer cancellations, schedule changes and invitations. Routine events (new booking, reschedule) are push-only. Nobody is told about their own action. A barber reassignment sends one customer email instead of two. Reward expiry sends one warning. Email redesign: your brand colour, a details card, Add to calendar and View on map links, a preferences link, hidden preview text, and dark mode. Replies now go to the business’s own email. WhatsApp “due now” queue: a list in time order with a highlighted next one, an owner setting for how many hours ahead (12–72), and a once-a-day push telling each barber how many reminders are waiting. Monitoring: /api/health now reports whether notifications are actually going out. The worker resets stuck deliveries and alerts the owner on the first setup failure (such as a bad SMTP login). The app emails your operator address about server errors, and the manager pushes that address to each business. Google scope: now only calendar.app.created. Events go to a calendar the app creates in the staff member’s Google account, called “<Business> bookings”.
This commit is contained in:
116 files changed
+3089
-766
No files matched your search
@@ -138,54 +138,62 @@ this for you), then finish its setup by visiting
|
||||
|
||||
Repeat this step for each additional business, whenever you want one.
|
||||
|
||||
### 6. Notification delivery (required once, covers every business)
|
||||
### 6. Notification delivery (handled by the Manager)
|
||||
|
||||
Staff invitation emails, appointment reminders, loyalty reward emails,
|
||||
and Google Reviews sync are **not sent automatically** — the app only
|
||||
queues them; something has to actually trigger delivery on a schedule by
|
||||
calling two `CRON_SECRET`-gated endpoints
|
||||
(`/api/cron/process-notifications`, `/api/cron/sync-google-reviews` — see
|
||||
their own route files for the full reasoning). Skip this step and every
|
||||
business's queued notifications just sit as `PENDING` forever — the UI
|
||||
still says "an invitation email has been sent" the moment it's queued,
|
||||
regardless of whether anything has actually delivered it yet.
|
||||
Staff invitation emails, appointment reminders, push notifications,
|
||||
loyalty reward emails, WhatsApp-reminder nudges and Google Reviews sync are
|
||||
**not sent by the app itself** — it only queues them; something has to
|
||||
trigger delivery on a schedule by calling two `CRON_SECRET`-gated
|
||||
endpoints (`/api/cron/process-notifications`,
|
||||
`/api/cron/sync-google-reviews` — see their route files for the reasoning).
|
||||
Without a trigger, every business's queued notifications sit as `PENDING`
|
||||
forever, even though the UI says "sent" the moment something is queued.
|
||||
|
||||
[stacks/infra/scripts/process-cron.sh](stacks/infra/scripts/process-cron.sh)
|
||||
calls both endpoints for **every** business under `STACKS_DIR` in one
|
||||
run, so a single crontab entry covers businesses added later too — no
|
||||
per-business cron line to maintain.
|
||||
**The Manager's built-in scheduler does this for you** — nothing to
|
||||
install on the host. Once the Manager is running it calls every
|
||||
business's notification cron every 30 seconds (adjustable at Settings →
|
||||
Monitoring & scheduler, 10 s – 1 hour) and the reviews sync once a day,
|
||||
including for businesses added later. That page lists the last run of each
|
||||
task per business, so you can confirm it's working.
|
||||
|
||||
Many minimal cloud VPS images (including some Debian ones) don't ship
|
||||
`cron` at all — `crontab -e` failing with `command not found` means this
|
||||
first:
|
||||
Each business's `/api/health` also reports whether notifications are
|
||||
actually going out (`status: "degraded"` plus the reasons, when the
|
||||
processor hasn't run for 10 minutes, deliveries are stuck, or every delivery
|
||||
in the last day failed), and the Manager emails you when that happens.
|
||||
|
||||
#### Removing the old crontab
|
||||
|
||||
If you set up the host crontab from an earlier version of this guide, remove
|
||||
it once the Manager scheduler is confirmed working — running both is safe
|
||||
(deliveries are claimed one at a time, so nothing is sent twice) but
|
||||
pointless:
|
||||
|
||||
1. Open Settings → Monitoring & scheduler in the Manager and check that
|
||||
"Process notifications" shows a recent successful run for each business.
|
||||
2. On the VPS, as the user that owns `/opt/nextchair`, run `crontab -e` and
|
||||
delete the two `process-cron.sh` lines (notifications and reviews).
|
||||
3. Optionally delete `/opt/nextchair/nextchair-cron.log`.
|
||||
|
||||
#### Fallback: host crontab
|
||||
|
||||
Only if you run with `MANAGER_SCHEDULER_DISABLED=1`, or want an independent
|
||||
trigger. [stacks/infra/scripts/process-cron.sh](stacks/infra/scripts/process-cron.sh)
|
||||
calls both endpoints for **every** business under `STACKS_DIR` in one run.
|
||||
Many minimal VPS images don't ship `cron` — install it first
|
||||
(`sudo apt update && sudo apt install -y cron && sudo systemctl enable --now cron`),
|
||||
then add this with `crontab -e` as the user that owns `/opt/nextchair`
|
||||
(**not** `sudo crontab -e`, so the log file is writable):
|
||||
|
||||
```bash
|
||||
sudo apt update && sudo apt install -y cron
|
||||
sudo systemctl enable --now cron
|
||||
```
|
||||
|
||||
Then add this to the VPS's crontab (`crontab -e`) once — **not** `sudo
|
||||
crontab -e`: run it as the same non-root user that already owns
|
||||
`/opt/nextchair` (the one you've been running `git pull`/`docker build`
|
||||
as), so the log file below is writable without needing root:
|
||||
|
||||
```bash
|
||||
# Deliver queued notifications every 2 minutes.
|
||||
*/2 * * * * STACKS_DIR=/opt/nextchair/stacks /opt/nextchair/stacks/infra/scripts/process-cron.sh notifications >> /opt/nextchair/nextchair-cron.log 2>&1
|
||||
# Deliver queued notifications every minute (the finest a crontab can do).
|
||||
* * * * * STACKS_DIR=/opt/nextchair/stacks /opt/nextchair/stacks/infra/scripts/process-cron.sh notifications >> /opt/nextchair/nextchair-cron.log 2>&1
|
||||
|
||||
# Refresh Google Reviews once a day.
|
||||
0 3 * * * STACKS_DIR=/opt/nextchair/stacks /opt/nextchair/stacks/infra/scripts/process-cron.sh reviews >> /opt/nextchair/nextchair-cron.log 2>&1
|
||||
```
|
||||
|
||||
(Adjust `STACKS_DIR`/the log path if you cloned this repo somewhere other
|
||||
than `/opt/nextchair`, or don't have write access to it — `/var/log/`
|
||||
itself is **not** a safe default here: it needs root, and a crontab
|
||||
entry that can't write its own log file just silently produces no file
|
||||
at all, which looks identical to "the job never ran.") To confirm it's
|
||||
working, or to flush a notification that's stuck pending right now, run
|
||||
either line's command by hand and check
|
||||
`/opt/nextchair/nextchair-cron.log` — a healthy run logs `<slug>
|
||||
process-notifications ok` for every business.
|
||||
A healthy run logs `<slug> process-notifications ok` for every business. Use
|
||||
only one of the two mechanisms at a time as your normal setup.
|
||||
|
||||
## Updating after a code change
|
||||
|
||||
|
||||
Reference in new issue
Block a user