fix: SMS status — show real delivery state from log, expandable SMS text in history
This commit is contained in:
parent
a5817110c9
commit
d11ccd4f76
|
|
@ -66,6 +66,38 @@ const fmtCountdown = (targetTs) => {
|
|||
return `${m}м ${s}с`;
|
||||
};
|
||||
|
||||
// ── SMS log entry (expandable to show text) ──────────────────────────────────
|
||||
const SmsLogEntry = ({ log }) => {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const hasText = !!log.sms_text;
|
||||
return (
|
||||
<div className="rounded-lg border border-[var(--color-border)] bg-[var(--color-surface)] px-2.5 py-1.5">
|
||||
<div
|
||||
className={`flex items-center gap-2 text-[11px] ${hasText ? "cursor-pointer" : ""}`}
|
||||
onClick={hasText ? () => setExpanded(e => !e) : undefined}
|
||||
>
|
||||
<span className="text-[var(--color-text-muted)]">{fmtTime(log.created_at)}</span>
|
||||
<Badge tone={log.status === "delivered" ? "accent" : log.status === "sent" || log.status === "checking" ? "info" : "danger"}>
|
||||
{log.status === "delivered" ? "доставлено" : log.status === "sent" ? "отправлено" : log.status === "checking" ? "проверка" : log.status === "expired" ? "истёк" : log.status}
|
||||
</Badge>
|
||||
{log.campaign_type && (
|
||||
<span className="text-[var(--color-text-muted)]">
|
||||
{log.campaign_type === "first_sms" ? "1-е SMS" : log.campaign_type === "second_sms" ? "2-е SMS" : log.campaign_type === "manual" ? "ручное" : log.campaign_type === "paid_storage" ? "платное хранение" : log.campaign_type}
|
||||
</span>
|
||||
)}
|
||||
{hasText && (
|
||||
<span className="ml-auto text-[var(--color-accent)] text-[10px]">{expanded ? "▲" : "▼"}</span>
|
||||
)}
|
||||
</div>
|
||||
{hasText && expanded && (
|
||||
<div className="mt-1.5 rounded-md bg-[var(--color-surface-strong)] px-2.5 py-2 text-[11px] text-[var(--color-text)] leading-relaxed whitespace-pre-wrap">
|
||||
{log.sms_text}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// ── Component ────────────────────────────────────────────────────────────────
|
||||
export const SmsStatusCard = ({ order, userRole }) => {
|
||||
const [restarting, setRestarting] = useState(false);
|
||||
|
|
@ -170,10 +202,14 @@ export const SmsStatusCard = ({ order, userRole }) => {
|
|||
};
|
||||
|
||||
const countdown = nextCheck ? fmtCountdown(nextCheck) : null;
|
||||
// Determine SMS display state
|
||||
const hasSmsSent = !!smsSentAt; // SMS physically sent (may not be delivered yet)
|
||||
const hasFirstSms = !!firstSmsAt; // First SMS confirmed delivered (103)
|
||||
const hasSecondSms = !!secondSmsAt; // Second SMS confirmed delivered (103)
|
||||
// Determine SMS display state from sms_log (source of truth)
|
||||
const firstSmsLog = smsLog.find(l => l.campaign_type === "first_sms");
|
||||
const secondSmsLog = smsLog.find(l => l.campaign_type === "second_sms");
|
||||
const isDelivered = (log) => log && (log.status === "delivered" || log.sms_code === "103");
|
||||
const hasFirstSms = isDelivered(firstSmsLog);
|
||||
const hasSecondSms = isDelivered(secondSmsLog);
|
||||
const firstSmsFailed = firstSmsLog && ["expired", "error", "send_failed", "limit_exceeded"].includes(firstSmsLog.status);
|
||||
const secondSmsFailed = secondSmsLog && ["expired", "error", "send_failed", "limit_exceeded"].includes(secondSmsLog.status);
|
||||
|
||||
return (
|
||||
<Panel className="p-4">
|
||||
|
|
@ -185,13 +221,15 @@ export const SmsStatusCard = ({ order, userRole }) => {
|
|||
<div className="space-y-2 text-xs">
|
||||
{/* 1st SMS */}
|
||||
<div className="flex items-start gap-2">
|
||||
<span className={`mt-0.5 h-2 w-2 rounded-full ${hasFirstSms ? "bg-[#22c55e]" : hasSmsSent && notifStatus === "sms_sending" ? "bg-[var(--color-accent)]" : notifStatus === "link_ready" || notifStatus === "not_started" ? "bg-[var(--color-warning)]" : "bg-[var(--color-border)]"}`} />
|
||||
<span className={`mt-0.5 h-2 w-2 rounded-full ${hasFirstSms ? "bg-[#22c55e]" : firstSmsFailed ? "bg-[var(--color-danger)]" : firstSmsAt ? "bg-[var(--color-accent)]" : notifStatus === "link_ready" || notifStatus === "not_started" ? "bg-[var(--color-warning)]" : "bg-[var(--color-border)]"}`} />
|
||||
<div className="flex-1">
|
||||
<div className="text-[var(--color-text)]">1-е SMS</div>
|
||||
{hasFirstSms ? (
|
||||
<div className="text-[var(--color-text-muted)]">{fmtTime(firstSmsAt)} ✓ получено клиентом</div>
|
||||
) : hasSmsSent && notifStatus === "sms_sending" ? (
|
||||
<div className="text-[var(--color-text-muted)]">{fmtTime(smsSentAt)} · отправлено, ждём ответ оператора…</div>
|
||||
<div className="text-[var(--color-text-muted)]">{fmtTime(firstSmsLog.created_at)} ✓ получено клиентом</div>
|
||||
) : firstSmsFailed ? (
|
||||
<div className="text-[var(--color-danger)]">{fmtTime(firstSmsLog.created_at)} ⚠ {firstSmsLog.status === "expired" ? "срок доставки истёк" : firstSmsLog.status}</div>
|
||||
) : firstSmsAt ? (
|
||||
<div className="text-[var(--color-text-muted)]">{fmtTime(firstSmsAt)} · отправлено, ждём ответ оператора…</div>
|
||||
) : notifStatus === "link_ready" ? (
|
||||
<div className="text-[var(--color-text-muted)]">в очереди на отправку</div>
|
||||
) : (
|
||||
|
|
@ -202,11 +240,13 @@ export const SmsStatusCard = ({ order, userRole }) => {
|
|||
|
||||
{/* 2nd SMS */}
|
||||
<div className="flex items-start gap-2">
|
||||
<span className={`mt-0.5 h-2 w-2 rounded-full ${hasSecondSms ? "bg-[#22c55e]" : notifStatus === "first_sms_sent" ? "bg-[var(--color-warning)]" : "bg-[var(--color-border)]"}`} />
|
||||
<span className={`mt-0.5 h-2 w-2 rounded-full ${hasSecondSms ? "bg-[#22c55e]" : secondSmsFailed ? "bg-[var(--color-danger)]" : notifStatus === "first_sms_sent" ? "bg-[var(--color-warning)]" : "bg-[var(--color-border)]"}`} />
|
||||
<div className="flex-1">
|
||||
<div className="text-[var(--color-text)]">2-е SMS</div>
|
||||
{hasSecondSms ? (
|
||||
<div className="text-[var(--color-text-muted)]">{fmtTime(secondSmsAt)} ✓ получено клиентом</div>
|
||||
<div className="text-[var(--color-text-muted)]">{fmtTime(secondSmsLog.created_at)} ✓ получено клиентом</div>
|
||||
) : secondSmsFailed ? (
|
||||
<div className="text-[var(--color-danger)]">{fmtTime(secondSmsLog.created_at)} ⚠ {secondSmsLog.status === "expired" ? "срок доставки истёк" : secondSmsLog.status}</div>
|
||||
) : notifStatus === "first_sms_sent" && countdown ? (
|
||||
<div className="text-[var(--color-text-muted)]">
|
||||
отправка через <span className="font-mono text-[var(--color-accent)]">{countdown}</span>
|
||||
|
|
@ -244,18 +284,7 @@ export const SmsStatusCard = ({ order, userRole }) => {
|
|||
<div className="mb-2 text-[10px] font-semibold uppercase tracking-wider text-[var(--color-text-muted)]">История SMS</div>
|
||||
<div className="space-y-1.5">
|
||||
{smsLog.map((log) => (
|
||||
<div key={log.id} className="flex items-center gap-2 text-[11px]">
|
||||
<span className="text-[var(--color-text-muted)]">{fmtTime(log.created_at)}</span>
|
||||
<Badge tone={log.status === "delivered" ? "accent" : log.status === "sent" || log.status === "checking" ? "info" : "danger"}>
|
||||
{log.status === "delivered" ? "доставлено" : log.status === "sent" ? "отправлено" : log.status === "checking" ? "проверка" : log.status === "expired" ? "истёк" : log.status}
|
||||
</Badge>
|
||||
{log.sent_to && (
|
||||
<span className="text-[var(--color-text-muted)]">→ {log.sent_to}</span>
|
||||
)}
|
||||
{log.sms_code && (
|
||||
<span className="text-[var(--color-text-muted)]">код: {log.sms_code}</span>
|
||||
)}
|
||||
</div>
|
||||
<SmsLogEntry key={log.id} log={log} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue