fix: stop SMS UI for terminal delivery statuses (picked_up/delivered/cancelled)

- SmsStatusCard: show "SMS не отправляется" when delivery is terminal, hide countdown + restart buttons
- SmsCampaignPanel fmtAgreement: check terminal status FIRST → " Самовывоз завершён" instead of "Самовывоз (неполно)"
- update_delivery_status RPC: reset notification_status→completed on terminal transition
- Add completed to notification_status check constraint
- SW v60→v61
This commit is contained in:
root 2026-07-04 11:27:03 +00:00
parent 43d9116c51
commit eabf30bceb
3 changed files with 33 additions and 10 deletions

View File

@ -1,8 +1,8 @@
const isLocalhost = self.location.hostname === "localhost" || self.location.hostname === "127.0.0.1";
if (!isLocalhost) {
const STATIC_CACHE = "construction-delivery-static-v60";
const RUNTIME_CACHE = "construction-delivery-runtime-v60";
const STATIC_CACHE = "construction-delivery-static-v61";
const RUNTIME_CACHE = "construction-delivery-runtime-v61";
const APP_SHELL_URLS = ["/", "/index.html", "/manifest.webmanifest", "/icons/icon-192.png", "/icons/icon-512.png"];
self.addEventListener("install", (event) => {

View File

@ -87,6 +87,7 @@ const NOTIF_LABELS = {
"manual_required": "🔧 Ручное управление",
"paid_storage_sending": "📡 Отправляется…",
"paid_storage_sent": "✓ Платное хранение: отправлено",
"completed": "✅ Завершено",
"confirmed": "✅ Согласовано клиентом",
"address_required": "⚠ Требуется адрес",
"draft": "Черновик",
@ -103,6 +104,7 @@ const NOTIF_TONES = {
"manual_required": "warning",
"paid_storage_sending": "info",
"paid_storage_sent": "accent",
"completed": "accent",
"confirmed": "accent",
"address_required": "danger",
"draft": "neutral",
@ -209,7 +211,18 @@ const fmtAgreement = (g) => {
if (!g) return { text: "—", tone: "neutral", icon: "⏳" };
const ds = g.delivery_status;
// If client already chose date/time show it regardless of delivery_status
// Terminal states first delivery already happened or cancelled
if (ds === "picked_up") {
return { text: "✅ Самовывоз завершён", tone: "accent", icon: "✅" };
}
if (ds === "delivered") {
return { text: "✅ Доставлено", tone: "accent", icon: "✅" };
}
if (ds === "cancelled") {
return { text: "❌ Отменён", tone: "danger", icon: "❌" };
}
// If client already chose date/time show it
if (g.delivery_type === "pickup" || g.pickup_date) {
const date = g.pickup_date ? new Date(g.pickup_date).toLocaleDateString("ru-RU", { day: "2-digit", month: "2-digit" }) : "";
const slot = g.pickup_time_slot || "";
@ -236,9 +249,6 @@ const fmtAgreement = (g) => {
if (ds === "paid_storage") {
return { text: "📦 Платное хранение", tone: "warning", icon: "📦" };
}
if (ds === "cancelled") {
return { text: "❌ Отменён", tone: "danger", icon: "❌" };
}
if (ds === "problem") {
return { text: "⚠ Проблема", tone: "danger", icon: "⚠" };
}

View File

@ -21,6 +21,7 @@ const NOTIF_LABELS = {
manual_required: "Требуется ручное управление",
paid_storage_sending: "Отправляется…",
paid_storage_sent: "Платное хранение: отправлено",
completed: "✅ Завершено",
draft: "Черновик",
};
@ -35,6 +36,7 @@ const NOTIF_TONES = {
manual_required: "warning",
paid_storage_sending: "info",
paid_storage_sent: "accent",
completed: "accent",
draft: "neutral",
};
@ -106,6 +108,7 @@ export const SmsStatusCard = ({ order, userRole }) => {
const canManage = ["mega_admin", "admin"].includes(userRole);
const notifStatus = order.notificationStatus || order.notification_status || "not_started";
const deliveryStatus = order.deliveryStatus || order.delivery_status || "";
const nextCheck = order.nextNotificationCheckAt || order.next_notification_check_at;
const firstSmsAt = order.firstSmsSentAt || order.first_sms_sent_at;
const secondSmsAt = order.secondSmsSentAt || order.second_sms_sent_at;
@ -113,6 +116,14 @@ export const SmsStatusCard = ({ order, userRole }) => {
const smsAttempts = order.smsAttempts ?? order.sms_attempts ?? 0;
const lastError = order.lastSmsError || order.last_sms_error;
// Terminal delivery states SMS flow should be fully stopped
const TERMINAL_STATUSES = ["picked_up", "delivered", "cancelled"];
const isTerminal = TERMINAL_STATUSES.includes(deliveryStatus);
const terminalLabel = deliveryStatus === "picked_up" ? "Самовывоз завершён"
: deliveryStatus === "delivered" ? "Доставка завершена"
: deliveryStatus === "cancelled" ? "Заказ отменён"
: "";
// Restart: reset this group's notification_status to link_ready
const handleRestart = async () => {
if (!order?.id) return;
@ -205,11 +216,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 === "second_sms_sending" ? "bg-[var(--color-accent)]" : 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]" : isTerminal ? "bg-[var(--color-border)]" : notifStatus === "second_sms_sending" ? "bg-[var(--color-accent)]" : 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>
) : isTerminal ? (
<div className="text-[var(--color-text-muted)]"> {terminalLabel}, SMS не отправляется</div>
) : notifStatus === "second_sms_sending" && hasSmsSent ? (
<div className="text-[var(--color-text-muted)]">{fmtTime(smsSentAt)} · отправлено, ждём подтверждения</div>
) : notifStatus === "first_sms_sent" && countdown ? (
@ -225,7 +238,7 @@ export const SmsStatusCard = ({ order, userRole }) => {
</div>
{/* Next check countdown */}
{countdown && notifStatus !== "second_sms_sent" && (
{countdown && notifStatus !== "second_sms_sent" && !isTerminal && (
<div className="flex items-center gap-2 rounded-lg bg-[var(--color-surface-strong)] px-2 py-1.5">
<span className="text-[var(--color-text-muted)]"> Следующая проверка:</span>
<span className="font-mono text-[var(--color-accent)]">{countdown}</span>
@ -268,8 +281,8 @@ export const SmsStatusCard = ({ order, userRole }) => {
</div>
)}
{/* Restart buttons */}
{canManage && (
{/* Restart buttons — hidden when delivery is terminal (picked_up/delivered/cancelled) */}
{canManage && !isTerminal && (
<div className="mt-3 flex gap-2 border-t border-[var(--color-border)] pt-3">
<button
type="button"