fix(timeline): StatusSummary/Stepper show real SMS state from sms_campaign_log (delivered/failed), not just first_sms_sent_at

This commit is contained in:
Hermes 2026-08-14 15:07:22 +02:00
parent 6fe0bfbc73
commit 4a4b088949
1 changed files with 28 additions and 10 deletions

View File

@ -172,7 +172,7 @@ const STAGES_PICKUP = [
{ key: "picked_up", icon: "📦", label: "Вывезено" },
];
const getActiveStageIndex = (order) => {
const getActiveStageIndex = (order, smsLogs = []) => {
const deliveryStatus = order?.deliveryStatus || order?.delivery_status || "";
const notifStatus = order?.notificationStatus || order?.notification_status || "";
const firstSms = order?.firstSmsSentAt || order?.first_sms_sent_at;
@ -197,17 +197,19 @@ const getActiveStageIndex = (order) => {
return 2;
}
if (firstSms || ["first_sms_sent", "second_sms_sent", "confirmed"].includes(notifStatus)) {
// SMS stage counts as done when any first_sms record exists (even failed), not just when delivered
const hasFirstSmsLog = smsLogs.some((l) => l.campaign_type === "first_sms");
if (firstSms || hasFirstSmsLog || ["first_sms_sent", "second_sms_sent", "confirmed"].includes(notifStatus)) {
return 1;
}
return 0;
};
const ProgressStepper = ({ order }) => {
const ProgressStepper = ({ order, smsLogs = [] }) => {
const isPickup = (order?.deliveryType || order?.delivery_type) === "pickup";
const stages = isPickup ? STAGES_PICKUP : STAGES_DELIVERY;
const activeIdx = getActiveStageIndex(order);
const activeIdx = getActiveStageIndex(order, smsLogs);
if (activeIdx === -1) {
return (
@ -274,7 +276,7 @@ const ProgressStepper = ({ order }) => {
};
// Current Status Summary (compact card above timeline)
const StatusSummary = ({ order }) => {
const StatusSummary = ({ order, smsLogs = [] }) => {
const isPickup = (order?.deliveryType || order?.delivery_type) === "pickup";
const deliveryStatus = order?.deliveryStatus || order?.delivery_status || "";
const notifStatus = order?.notificationStatus || order?.notification_status || "";
@ -291,13 +293,29 @@ const StatusSummary = ({ order }) => {
const items = [];
// SMS status
if (firstSms) {
// SMS status prefer sms_campaign_log when available (real delivery state)
const firstSmsLog = smsLogs.find((l) => l.campaign_type === "first_sms");
const secondSmsLog = smsLogs.find((l) => l.campaign_type === "second_sms");
const isDelivered = (log) => log && (log.status === "delivered" || log.sms_code === "103");
const isFailed = (log) => log && ["expired", "error", "send_failed", "limit_exceeded"].includes(log.status);
if (isDelivered(firstSmsLog)) {
items.push({ icon: "📨", label: "1-е SMS", value: `доставлено ${fmtTimestamp(firstSmsLog.updated_at || firstSmsLog.created_at)}`, tone: "ok" });
} else if (isFailed(firstSmsLog)) {
items.push({ icon: "📨", label: "1-е SMS", value: `${fmtTimestamp(firstSmsLog.created_at)} · ${firstSmsLog.status === "expired" ? "не доставлено (срок истёк)" : firstSmsLog.status}${firstSmsLog.error_message ? ` · ${firstSmsLog.error_message}` : ""}`, tone: "danger" });
} else if (firstSms) {
items.push({ icon: "📨", label: "1-е SMS", value: fmtTimestamp(firstSms), tone: "ok" });
} else if (firstSmsLog) {
items.push({ icon: "📨", label: "1-е SMS", value: `${fmtTimestamp(firstSmsLog.created_at)} · ${firstSmsLog.status || "отправлено"}`, tone: "warning" });
} else {
items.push({ icon: "📨", label: "1-е SMS", value: "не отправлено", tone: "muted" });
}
if (secondSms) {
if (isDelivered(secondSmsLog)) {
items.push({ icon: "📨", label: "2-е SMS", value: `доставлено ${fmtTimestamp(secondSmsLog.updated_at || secondSmsLog.created_at)}`, tone: "ok" });
} else if (isFailed(secondSmsLog)) {
items.push({ icon: "📨", label: "2-е SMS", value: `${fmtTimestamp(secondSmsLog.created_at)} · ${secondSmsLog.status === "expired" ? "не доставлено (срок истёк)" : secondSmsLog.status}${secondSmsLog.error_message ? ` · ${secondSmsLog.error_message}` : ""}`, tone: "danger" });
} else if (secondSms) {
items.push({ icon: "📨", label: "2-е SMS", value: fmtTimestamp(secondSms), tone: "ok" });
}
@ -600,8 +618,8 @@ export const OrderHistoryTimeline = ({ order, userRole }) => {
return (
<div className="space-y-4">
<ProgressStepper order={order} />
<StatusSummary order={order} />
<ProgressStepper order={order} smsLogs={smsLogs} />
<StatusSummary order={order} smsLogs={smsLogs} />
<div className={`rounded-xl border px-3 py-2.5 ${
activePushSubscriptions.length > 0
? "border-[rgba(18,128,92,0.25)] bg-[var(--color-accent-soft)]"