docs: spec unified outbound SMS log in compose modal

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-17 21:16:08 +03:30
co-authored by Cursor
parent d62e004e90
commit 9325d879a5
@@ -0,0 +1,87 @@
# لاگ یکپارچهٔ پیامک خروجی در دیالوگ ارسال
تاریخ: ۲۰۲۶-۱۰-۱۷
وضعیت: تأیید شده؛ آمادهٔ پلن پیاده‌سازی.
## هدف
در دیالوگ ارسال پیامک (لید و شخص)، لیست پیامک‌هایی که از این به بعد برای همان مخاطب رفته نمایش داده شود: دستی، کمپین گروهی، اتوماسیون.
## تصمیم‌ها
| موضوع | تصمیم |
| --- | --- |
| منبع حقیقت | جدول جدید `OutboundSmsLog` |
| تاریخچهٔ قبل از استقرار | مهم نیست؛ فقط از این به بعد |
| پوشش | manual + campaign + automation |
| UI | همان `SmsComposeModal` برای لید و شخص |
## مدل داده
`OutboundSmsLog`:
- `id`, `organizationId`
- `personId` اختیاری، `leadId` اختیاری (حداقل یکی در عمل پر می‌شود)
- `mobile`, `body` (متن نهایی پس از توکن/لینک)
- `source`: `manual` | `campaign` | `automation`
- `sourceId` اختیاری (شناسهٔ کمپین / قانون اتوماسیون)
- `status`: `sent` | `failed`
- `error` اختیاری
- `actorUserId` اختیاری (برای دستی)
- `createdAt`
- ایندکس: `(organizationId, personId, createdAt)`, `(organizationId, leadId, createdAt)`
## نقاط ثبت
1. `PeopleService.sendSms` — source=`manual`
2. `LeadsService.sendSms` — source=`manual` (LeadEvent فعلی می‌تواند بماند)
3. `SmsCampaignsService.processCampaign` پس از هر تلاش ارسال — source=`campaign`, `sourceId`=campaignId
4. ارسال SMS در message-automations — source=`automation`, `sourceId`=ruleId، با `personId` وقتی موجود است
ثبت نباید ارسال را بشکند: اگر لاگ fail شد، فقط log خطا؛ نتیجهٔ SMS حفظ شود.
## API
- `GET /people/:id/sms-history?limit=50`
- `GET /leads/:id/sms-history?limit=50`
`OutboundSmsHistoryResponse`:
```ts
{
items: Array<{
id: string;
body: string;
mobile: string;
source: 'manual' | 'campaign' | 'automation';
sourceLabel: string; // دستی | کمپین | اتوماسیون
status: 'sent' | 'failed';
createdAt: string;
}>;
}
```
مرتب‌سازی: جدیدترین اول. Scope دسترسی همان دسترسی به شخص/لید.
## UI
در `SmsComposeModal`:
- prop اختیاری `historyEndpoint`
- با `open` لود تاریخچه
- بلوک «پیامک‌های قبلی» زیر پیش‌نمایش؛ اسکرول؛ خالی = «هنوز پیامکی ثبت نشده»
- پس از ارسال موفق، رفرش لیست (یا prepend محلی)
`LeadDetailView` و `PersonProfileView` endpoint تاریخچه را پاس می‌دهند.
## خارج از محدوده
- بک‌فیل تاریخچهٔ قدیمی
- نمایش در صفحهٔ جدا / گزارش سازمانی کامل
- ویرایش یا حذف لاگ از UI
## معیار پذیرش
1. بعد از ارسال دستی به لید/شخص، همان متن در دیالوگ دیده شود.
2. پیامک کمپین و اتوماسیون (برای شخص) در تاریخچه ظاهر شود.
3. لید و شخص هر دو از همان مودال تاریخچه بگیرند.