Remaining features
CI / ci (push) Canceled after 0s

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:
Stefan Lazic committed 2026-10-17 23:10:18 +01:00
1 parent 17eee5b932
commit 68a62e02a4
116 files changed
+3089 -766

No files matched your search

+46 -38
View File
@@ -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