From 32c722f42ae6656d45a36335098bfd099ffb93e9 Mon Sep 17 00:00:00 2001 From: Mahdi Date: Sat, 17 Oct 2026 20:47:48 +0330 Subject: [PATCH] docs: spec SMS campaign people/leads tabs and batch send Co-authored-by: Cursor --- ...-10-17-sms-campaign-people-leads-design.md | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 docs/superpowers/specs/2026-10-17-sms-campaign-people-leads-design.md diff --git a/docs/superpowers/specs/2026-10-17-sms-campaign-people-leads-design.md b/docs/superpowers/specs/2026-10-17-sms-campaign-people-leads-design.md new file mode 100644 index 0000000..2f1a45e --- /dev/null +++ b/docs/superpowers/specs/2026-10-17-sms-campaign-people-leads-design.md @@ -0,0 +1,84 @@ +# پیامک گروهی: تب اشخاص/لیدها، فیلتر کامل، انتخاب‌همه، بچ ۵۰ + +تاریخ: ۲۰۲۶-۱۰-۱۷ +وضعیت: تأیید شده؛ آمادهٔ پلن پیاده‌سازی. + +## هدف + +صفحهٔ پیامک گروهی بتواند مخاطب را از **اشخاص** یا **لیدها** (جدا از هم) با فیلترهای واقعی (از جمله استان/شهر) انتخاب کند، همهٔ نتایج فیلتر را یکجا انتخاب کند، متن را با فیلدهای پویا بنویسد، و ساخت/ارسال را در تکه‌های ۵۰تایی انجام دهد. + +## تصمیم‌های تأییدشده + +| موضوع | تصمیم | +| --- | --- | +| نوع مخاطب در یک کمپین | فقط اشخاص **یا** فقط لیدها (تب جدا؛ نه مخلوط) | +| انتخاب‌همه | همهٔ نتایج مطابق فیلتر در کل دیتابیس (با تأیید تعداد) | +| بچ | ارسال سرور ۵۰تایی؛ ساخت کمپین هم در صورت نیاز تکه‌تکهٔ ۵۰تایی | +| فیلتر مکان اشخاص | از `company.province` / `company.city` + جستجو در آدرس‌ها (بدون ستون استان روی Person) | +| فیلتر مکان لیدها | `city` و `region` (+ بقیهٔ فیلترهای موجود لیست لید) | +| فیلدهای پویا | همان توکن‌های اتوماسیون پیام: `{نام}`، `{شماره}`، `{تاریخ}`، `{عنوان}`، `{شرکت}` | + +## UX + +1. دو تب روی فرم کمپین جدید: **اشخاص** | **لیدها**. +2. تعویض تب با انتخاب موجود → تأیید و پاک شدن انتخاب. +3. لیست صفحه‌بندی‌شده با جستجو و فیلترهای هم‌سطح صفحات اصلی هر بخش. +4. نمایش تعداد انتخاب‌شده؛ دکمهٔ «انتخاب همهٔ نتایج فیلتر» → دریافت `total`/`ids` → تأیید → جایگزینی مجموعهٔ انتخاب. +5. بالای textarea دکمهٔ **فیلدهای پویا** (الگوی UI اتوماسیون پیام) برای درج توکن در متن. +6. پس از ذخیره/صف، لیست کمپین‌ها مثل قبل؛ گزارش کلیک اگر از قبل برای لید/شخص آماده است همان مسیر را می‌رود. + +## API + +### فیلتر لیست + +- گسترش `GET /people` با فیلترهای کمپین: حداقل `province`, `city` (از شرکت)، و فیلترهای موجود (`q`, `mood`, `importSource`, …). +- اطمینان از پوشش فیلترهای لید برای کمپین: `q`, `city`, `region`, `stageId`, و فیلترهای موجود دیگر لیست لید. + +### شناسه‌های فیلترشده + +- `GET /people/ids` — همان queryهای لیست؛ پاسخ `{ ids: string[], total: number }`. +- `GET /leads/ids` — همان برای لید. +- هر دو با `ownerWhere` / scope بازیگر. +- سقف سخت برای جلوگیری از پاسخ غول‌آسا (مثلاً ۵۰٬۰۰۰)؛ بیش از سقف → خطای فارسی با پیشنهاد محدود کردن فیلتر. + +### ساخت و تکمیل کمپین + +- `CreateSmsCampaignRequest`: + - `name`, `body`, `queueNow?`, `scheduledAt?` + - دقیقاً یکی از `personIds` یا `leadIds` (آرایهٔ غیرخالی). +- ساخت اولیه حداکثر ۵۰ گیرنده؛ اگر فرانت بیشتر دارد، بعد از ساخت: + - `POST /sms-campaigns/:id/recipients` با `{ personIds }` یا `{ leadIds }` هم‌نوع با کمپین؛ هر درخواست حداکثر ۵۰ id. +- تا پایان تکه‌ها کمپین `draft` می‌ماند؛ سپس در صورت `queueNow` صف می‌شود (یا کاربر دستی queue می‌زند). +- گیرنده بدون موبایل → `skipped`؛ شمارنده‌ها به‌روز می‌شوند. + +### ارسال + +- در `processCampaign` مقدار `take` بچ از ۴۰ به **۵۰**. +- قبل از `sendSmsDetailed`: اعمال `renderSmsText` روی body با متغیرهای گیرنده (شخص یا لید)، سپس `personalizeSmsBody` برای لینک یکتا (ترتیب: ابتدا توکن‌های پویا، بعد لینک‌ها). + +## داده + +- مدل `SmsCampaignRecipient` از قبل `personId` و `leadId` دارد؛ migration جدید لازم نیست مگر برای ایندکس/قید اختیاری. +- کمپین صریحاً audience را از روی گیرندگان مشخص می‌کند؛ نیازی به ستون `audienceType` نیست اگر validation در create/add-recipients نوع را یکدست نگه دارد. + +## خطاها + +- هر دو `personIds` و `leadIds` با هم → `400`. +- افزودن نوع مخالف به کمپین موجود → `400`. +- قطع میانه‌راه تکه‌ها → کمپین `draft` با گیرندگان اضافه‌شده؛ پیام تعداد موفق/ناموفق. +- ماژول غیرفعال → همان `403` فعلی. + +## خارج از محدوده + +- کمپین ترکیبی اشخاص+لیدها +- ستون استان/شهر مستقیم روی Person +- ویرایش متن بعد از شروع ارسال +- زمان‌بندی تقویمی جدید فراتر از `scheduledAt` فعلی + +## معیار پذیرش + +1. تب لید و تب شخص جدا؛ یک کمپین فقط یکی را می‌پذیرد. +2. فیلتر استان/شهر (و فیلترهای کلیدی دیگر) کار می‌کند؛ انتخاب‌همه همهٔ نتایج فیلتر را می‌گیرد نه فقط صفحه. +3. فیلدهای پویا در UI درج می‌شوند و در SMS واقعی جایگزین می‌گردند. +4. ساخت لیست بزرگ در تکه‌های ۵۰؛ ارسال صف هم ۵۰تایی است. +) \ No newline at end of file