Backoffice stage 5: products, broadcasts, activity log, settings

- Products: services with full setup — content (name, description, extras names
  in 5 languages via text overrides), price tiers and extras, booking window,
  late-cancel fee, on/off, checklist; category discounts by membership level;
  membership plan prices; promo codes; relocation steps and documents.
- Text overrides: TextOverride table on top of the i18n files; API, bots and the
  worker reload them every minute, mini apps fetch public.texts at start.
- Promo codes applied to orders (admin new-order form and client API): not
  stacked with membership discount — the larger wins; once per client; validity,
  limit and categories.
- Broadcasts to the client bot by any client filter, per-language texts with
  Russian fallback, test to yourself, ~25 msg/s, per-recipient status, resume after
  worker restart, history with progress and stop.
- Activity log (who/when/what, filters, details) and Settings page with all
  owner settings grouped, plus payment methods.
- Tests for promo codes and text overrides.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
zabudko-ruandClaude Opus 5.5 committed 2026-10-21 03:17:56 +04:00
1 parent ac786cc5cc
commit 0edc6432b7
34 files changed
+2704 -26

No files matched your search

@@ -0,0 +1,60 @@
-- CreateEnum
CREATE TYPE "BroadcastStatus" AS ENUM ('SENDING', 'DONE', 'CANCELLED');
-- CreateEnum
CREATE TYPE "BroadcastRecipientStatus" AS ENUM ('PENDING', 'SENT', 'BLOCKED', 'FAILED');
-- AlterTable
ALTER TABLE "PromoCode" ADD COLUMN "categories" "CategoryCode"[] DEFAULT ARRAY[]::"CategoryCode"[],
ADD COLUMN "note" TEXT;
-- CreateTable
CREATE TABLE "TextOverride" (
"key" TEXT NOT NULL,
"locale" "Locale" NOT NULL,
"value" TEXT NOT NULL,
"updatedById" TEXT,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "TextOverride_pkey" PRIMARY KEY ("key","locale")
);
-- CreateTable
CREATE TABLE "Broadcast" (
"id" TEXT NOT NULL,
"texts" JSONB NOT NULL,
"appButton" BOOLEAN NOT NULL DEFAULT true,
"filters" JSONB NOT NULL,
"status" "BroadcastStatus" NOT NULL DEFAULT 'SENDING',
"total" INTEGER NOT NULL DEFAULT 0,
"sent" INTEGER NOT NULL DEFAULT 0,
"blocked" INTEGER NOT NULL DEFAULT 0,
"failed" INTEGER NOT NULL DEFAULT 0,
"createdById" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"finishedAt" TIMESTAMP(3),
CONSTRAINT "Broadcast_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "BroadcastRecipient" (
"id" TEXT NOT NULL,
"broadcastId" TEXT NOT NULL,
"clientId" TEXT NOT NULL,
"status" "BroadcastRecipientStatus" NOT NULL DEFAULT 'PENDING',
"error" TEXT,
"sentAt" TIMESTAMP(3),
CONSTRAINT "BroadcastRecipient_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "BroadcastRecipient_broadcastId_status_idx" ON "BroadcastRecipient"("broadcastId", "status");
-- CreateIndex
CREATE UNIQUE INDEX "BroadcastRecipient_broadcastId_clientId_key" ON "BroadcastRecipient"("broadcastId", "clientId");
-- AddForeignKey
ALTER TABLE "BroadcastRecipient" ADD CONSTRAINT "BroadcastRecipient_broadcastId_fkey" FOREIGN KEY ("broadcastId") REFERENCES "Broadcast"("id") ON DELETE CASCADE ON UPDATE CASCADE;