/** * @file SmsCityRulesPanel.jsx * @description Управление SMS-отправкой по городам. * Админ может: * - Включить/выключить SMS для города × кампания (постоянно) * - Указать период (с/по дату) — временно * - Добавить комментарий * - «Все города» (*) или «Все кампании» (*) — массовое правило */ import React, { useState, useEffect, useCallback } from "react"; import { Panel } from "../UI/Panel"; import { supabase } from "../../supabaseClient"; import { CRIMEAN_CITIES } from "../../constants/cities.js"; const CAMPAIGN_LABELS = { first_sms: "1-е SMS", second_sms: "2-е SMS", paid_storage: "Платное хранение", "*": "Все кампании", }; const CITY_ALL = "*"; export const SmsCityRulesPanel = () => { const [rules, setRules] = useState([]); const [loading, setLoading] = useState(true); const [showForm, setShowForm] = useState(false); const [saving, setSaving] = useState(false); const [message, setMessage] = useState(""); // Form state const [formCity, setFormCity] = useState("Ялта"); const [formCampaign, setFormCampaign] = useState("*"); const [formEnabled, setFormEnabled] = useState(false); const [formFrom, setFormFrom] = useState(""); const [formUntil, setFormUntil] = useState(""); const [formNote, setFormNote] = useState(""); const fetchRules = useCallback(async () => { setLoading(true); try { const { data, error } = await supabase .from("sms_city_rules") .select("*") .order("city", { ascending: true }) .order("campaign_type", { ascending: true }); if (error) throw error; setRules(data || []); } catch (e) { console.error("fetch sms_city_rules error", e); } finally { setLoading(false); } }, []); useEffect(() => { fetchRules(); }, [fetchRules]); const handleSave = async () => { setSaving(true); try { const { error } = await supabase.from("sms_city_rules").insert({ city: formCity, campaign_type: formCampaign, enabled: formEnabled, valid_from: formFrom || null, valid_until: formUntil || null, note: formNote || "", }); if (error) throw error; setShowForm(false); setFormNote(""); setFormFrom(""); setFormUntil(""); setMessage("✅ Правило добавлено"); fetchRules(); setTimeout(() => setMessage(""), 3000); } catch (e) { setMessage("❌ Ошибка: " + e.message); } finally { setSaving(false); } }; const handleToggle = async (rule) => { try { const { error } = await supabase .from("sms_city_rules") .update({ enabled: !rule.enabled }) .eq("id", rule.id); if (error) throw error; fetchRules(); } catch (e) { console.error("toggle error", e); } }; const handleDelete = async (id) => { try { const { error } = await supabase .from("sms_city_rules") .delete() .eq("id", id); if (error) throw error; fetchRules(); } catch (e) { console.error("delete error", e); } }; const fmtDate = (d) => { if (!d) return "—"; try { return new Date(d).toLocaleDateString("ru-RU", { day: "2-digit", month: "short", year: "numeric" }); } catch { return d; } }; return (
{/* Header */}
🏙️

SMS по городам

Управление отправкой SMS по городам и кампаниям. Выключенное правило = SMS не отправляется.

{message && {message}}
{/* Add rule form */} {showForm && (

Новое правило

{/* City */}
{/* Campaign */}
{/* Enabled toggle */}
{/* Period */}
setFormFrom(e.target.value)} className="w-full rounded-xl border border-[var(--color-border)] bg-[var(--color-bg)] px-3 py-2 text-sm text-[var(--color-text)]" />
setFormUntil(e.target.value)} className="w-full rounded-xl border border-[var(--color-border)] bg-[var(--color-bg)] px-3 py-2 text-sm text-[var(--color-text)]" />
{/* Note */}
setFormNote(e.target.value)} placeholder="Например: Ялта временно отключена" className="w-full rounded-xl border border-[var(--color-border)] bg-[var(--color-bg)] px-3 py-2 text-sm text-[var(--color-text)] placeholder:text-[var(--color-text-muted)]" />

Пустые даты = правило действует постоянно. Указав даты, можно временно отключить SMS на период.

)} {/* Rules table */}

Правила ({rules.length})

{loading ? (

Загрузка...

) : rules.length === 0 ? (

Правил пока нет. SMS отправляются во все города.

Нажмите «+ Правило» чтобы отключить SMS для конкретного города.

) : (
{rules.map((rule) => { const isActive = rule.enabled && (!rule.valid_from || new Date(rule.valid_from) <= new Date()) && (!rule.valid_until || new Date(rule.valid_until) >= new Date()); return (
{/* Toggle */} {/* Info */}
{rule.city === CITY_ALL ? "⭐ Все города" : rule.city} · {CAMPAIGN_LABELS[rule.campaign_type] || rule.campaign_type} {isActive ? "Активно" : rule.enabled ? "Запланировано" : "Выключено"}
{(rule.valid_from || rule.valid_until) && (
📅 {fmtDate(rule.valid_from)} — {fmtDate(rule.valid_until)}
)} {rule.note && (
💬 {rule.note}
)}
{/* Delete */}
); })}
)}
); };