fix: SMS city profiles — жёсткие цвета вместо CSS variables
- Карточки: #e8f0ec (светло-зелёный) / #0d1a16 (тёмный) — чётко виден на Panel - Внутренние блоки: #ffffff / #142822 — контраст с карточкой - Название города: #10211b / #ecf5f2 — тёмный/светлый, жирный - Все цвета заданы через CSS классы с [data-theme=dark] — без var() - Toggle: #22c55e / #c8d8d2 — виден в обеих темах
This commit is contained in:
parent
2103fe2afe
commit
d8930925be
|
|
@ -7,6 +7,93 @@ import { Panel } from "../UI/Panel";
|
||||||
import { supabase } from "../../supabaseClient";
|
import { supabase } from "../../supabaseClient";
|
||||||
import { CRIMEAN_CITIES } from "../../constants/cities.js";
|
import { CRIMEAN_CITIES } from "../../constants/cities.js";
|
||||||
|
|
||||||
|
const THEME_STYLES = `
|
||||||
|
.sms-card {
|
||||||
|
background: #e8f0ec;
|
||||||
|
border: 1px solid #c8d8d2;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .sms-card {
|
||||||
|
background: #0d1a16;
|
||||||
|
border: 1px solid rgba(236,245,242,0.12);
|
||||||
|
}
|
||||||
|
.sms-inner {
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #d0dad6;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .sms-inner {
|
||||||
|
background: #142822;
|
||||||
|
border: 1px solid rgba(236,245,242,0.08);
|
||||||
|
}
|
||||||
|
.sms-input {
|
||||||
|
background: #f4f7f5;
|
||||||
|
border: 1px solid #c8d8d2;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
color: #10211b;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .sms-input {
|
||||||
|
background: #09110f;
|
||||||
|
border: 1px solid rgba(236,245,242,0.12);
|
||||||
|
color: #ecf5f2;
|
||||||
|
}
|
||||||
|
.sms-input::placeholder { color: #5d6d66; }
|
||||||
|
[data-theme="dark"] .sms-input::placeholder { color: #9eb0aa; }
|
||||||
|
.sms-label { font-size: 12px; font-weight: 500; color: #5d6d66; }
|
||||||
|
[data-theme="dark"] .sms-label { color: #9eb0aa; }
|
||||||
|
.sms-city-name { font-size: 16px; font-weight: 700; color: #10211b; }
|
||||||
|
[data-theme="dark"] .sms-city-name { color: #ecf5f2; }
|
||||||
|
.sms-section-title { font-size: 14px; font-weight: 600; color: #10211b; }
|
||||||
|
[data-theme="dark"] .sms-section-title { color: #ecf5f2; }
|
||||||
|
.sms-muted { font-size: 12px; color: #5d6d66; }
|
||||||
|
[data-theme="dark"] .sms-muted { color: #9eb0aa; }
|
||||||
|
.sms-text { font-size: 13px; color: #10211b; }
|
||||||
|
[data-theme="dark"] .sms-text { color: #ecf5f2; }
|
||||||
|
.sms-italic { font-size: 12px; color: #5d6d66; font-style: italic; line-height: 1.4; margin-top: 6px; }
|
||||||
|
[data-theme="dark"] .sms-italic { color: #9eb0aa; }
|
||||||
|
.sms-toggle-text { font-size: 13px; color: #10211b; }
|
||||||
|
[data-theme="dark"] .sms-toggle-text { color: #ecf5f2; }
|
||||||
|
.sms-btn {
|
||||||
|
border-radius: 12px; padding: 8px 16px; font-size: 12px; font-weight: 600;
|
||||||
|
color: #fff; border: none; cursor: pointer; background: #12805c;
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .sms-btn { background: #57d8a9; color: #06120d; }
|
||||||
|
.sms-btn-sm {
|
||||||
|
border-radius: 8px; border: 1px solid #c8d8d2; padding: 4px 10px;
|
||||||
|
font-size: 12px; font-weight: 500; color: #10211b; cursor: pointer; background: transparent;
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .sms-btn-sm { border: 1px solid rgba(236,245,242,0.12); color: #ecf5f2; }
|
||||||
|
.sms-btn-del {
|
||||||
|
border-radius: 8px; padding: 4px 8px; font-size: 14px;
|
||||||
|
color: #5d6d66; cursor: pointer; background: transparent; border: none;
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .sms-btn-del { color: #9eb0aa; }
|
||||||
|
.sms-mode-btn-active-g {
|
||||||
|
flex: 1; border-radius: 10px; border: 1px solid #22c55e;
|
||||||
|
background: rgba(34,197,94,0.1); color: #22c55e;
|
||||||
|
padding: 8px 12px; font-size: 14px; font-weight: 500; cursor: pointer;
|
||||||
|
}
|
||||||
|
.sms-mode-btn-inactive {
|
||||||
|
flex: 1; border-radius: 10px; border: 1px solid #c8d8d2;
|
||||||
|
background: transparent; color: #5d6d66;
|
||||||
|
padding: 8px 12px; font-size: 14px; font-weight: 500; cursor: pointer;
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .sms-mode-btn-inactive {
|
||||||
|
border: 1px solid rgba(236,245,242,0.12); color: #9eb0aa;
|
||||||
|
}
|
||||||
|
.sms-mode-btn-active-b {
|
||||||
|
flex: 1; border-radius: 10px; border: 1px solid #3b82f6;
|
||||||
|
background: rgba(59,130,246,0.1); color: #3b82f6;
|
||||||
|
padding: 8px 12px; font-size: 14px; font-weight: 500; cursor: pointer;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const SmsCityProfilesPanel = () => {
|
export const SmsCityProfilesPanel = () => {
|
||||||
const [profiles, setProfiles] = useState([]);
|
const [profiles, setProfiles] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
@ -44,31 +131,20 @@ export const SmsCityProfilesPanel = () => {
|
||||||
useEffect(() => { fetchProfiles(); }, [fetchProfiles]);
|
useEffect(() => { fetchProfiles(); }, [fetchProfiles]);
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
setFormCity("Ялта");
|
setFormCity("Ялта"); setFormDeliveryMode("notification"); setFormDeliveryText("");
|
||||||
setFormDeliveryMode("notification");
|
setFormPickupMode("notification"); setFormPickupText("");
|
||||||
setFormDeliveryText("");
|
setFormSecondSms(true); setFormPaidStorage(true); setFormSmsEnabled(true);
|
||||||
setFormPickupMode("notification");
|
setFormNote(""); setEditingId(null);
|
||||||
setFormPickupText("");
|
|
||||||
setFormSecondSms(true);
|
|
||||||
setFormPaidStorage(true);
|
|
||||||
setFormSmsEnabled(true);
|
|
||||||
setFormNote("");
|
|
||||||
setEditingId(null);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
try {
|
try {
|
||||||
const payload = {
|
const payload = {
|
||||||
city: formCity,
|
city: formCity, delivery_mode: formDeliveryMode, delivery_text: formDeliveryText || null,
|
||||||
delivery_mode: formDeliveryMode,
|
pickup_mode: formPickupMode, pickup_text: formPickupText || null,
|
||||||
delivery_text: formDeliveryText || null,
|
second_sms_enabled: formSecondSms, paid_storage_enabled: formPaidStorage,
|
||||||
pickup_mode: formPickupMode,
|
sms_enabled: formSmsEnabled, note: formNote || "",
|
||||||
pickup_text: formPickupText || null,
|
|
||||||
second_sms_enabled: formSecondSms,
|
|
||||||
paid_storage_enabled: formPaidStorage,
|
|
||||||
sms_enabled: formSmsEnabled,
|
|
||||||
note: formNote || "",
|
|
||||||
};
|
};
|
||||||
if (editingId) {
|
if (editingId) {
|
||||||
const { error } = await supabase.from("sms_city_profiles").update(payload).eq("id", editingId);
|
const { error } = await supabase.from("sms_city_profiles").update(payload).eq("id", editingId);
|
||||||
|
|
@ -79,264 +155,126 @@ export const SmsCityProfilesPanel = () => {
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
setMessage("✅ Профиль добавлен");
|
setMessage("✅ Профиль добавлен");
|
||||||
}
|
}
|
||||||
setShowForm(false);
|
setShowForm(false); resetForm(); fetchProfiles();
|
||||||
resetForm();
|
|
||||||
fetchProfiles();
|
|
||||||
setTimeout(() => setMessage(""), 3000);
|
setTimeout(() => setMessage(""), 3000);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setMessage("❌ Ошибка: " + e.message);
|
setMessage("❌ Ошибка: " + e.message);
|
||||||
} finally {
|
} finally { setSaving(false); }
|
||||||
setSaving(false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEdit = (profile) => {
|
const handleEdit = (p) => {
|
||||||
setEditingId(profile.id);
|
setEditingId(p.id); setFormCity(p.city);
|
||||||
setFormCity(profile.city);
|
setFormDeliveryMode(p.delivery_mode || "link"); setFormDeliveryText(p.delivery_text || "");
|
||||||
setFormDeliveryMode(profile.delivery_mode || "link");
|
setFormPickupMode(p.pickup_mode || "link"); setFormPickupText(p.pickup_text || "");
|
||||||
setFormDeliveryText(profile.delivery_text || "");
|
setFormSecondSms(p.second_sms_enabled ?? true); setFormPaidStorage(p.paid_storage_enabled ?? true);
|
||||||
setFormPickupMode(profile.pickup_mode || "link");
|
setFormSmsEnabled(p.sms_enabled ?? true); setFormNote(p.note || "");
|
||||||
setFormPickupText(profile.pickup_text || "");
|
|
||||||
setFormSecondSms(profile.second_sms_enabled ?? true);
|
|
||||||
setFormPaidStorage(profile.paid_storage_enabled ?? true);
|
|
||||||
setFormSmsEnabled(profile.sms_enabled ?? true);
|
|
||||||
setFormNote(profile.note || "");
|
|
||||||
setShowForm(true);
|
setShowForm(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = async (id) => {
|
const handleDelete = async (id) => {
|
||||||
try {
|
try { const { error } = await supabase.from("sms_city_profiles").delete().eq("id", id); if (error) throw error; fetchProfiles(); }
|
||||||
const { error } = await supabase.from("sms_city_profiles").delete().eq("id", id);
|
catch (e) { console.error("delete error", e); }
|
||||||
if (error) throw error;
|
|
||||||
fetchProfiles();
|
|
||||||
} catch (e) {
|
|
||||||
console.error("delete error", e);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleQuickToggle = async (profile, field) => {
|
const ModeBadge = ({ mode }) => mode === "notification"
|
||||||
try {
|
? <span style={{ backgroundColor: "rgba(59,130,246,0.15)", color: "#3b82f6", borderRadius: "6px", padding: "2px 8px", fontSize: "10px", fontWeight: 600 }}>Уведомление</span>
|
||||||
const { error } = await supabase.from("sms_city_profiles").update({ [field]: !profile[field] }).eq("id", profile.id);
|
: <span style={{ backgroundColor: "rgba(34,197,94,0.15)", color: "#22c55e", borderRadius: "6px", padding: "2px 8px", fontSize: "10px", fontWeight: 600 }}>Ссылка</span>;
|
||||||
if (error) throw error;
|
|
||||||
fetchProfiles();
|
|
||||||
} catch (e) {
|
|
||||||
console.error("toggle error", e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ── Styles ──────────────────────────────────────────────────────────────
|
|
||||||
// Карточка профиля — лёгкий зелёный оттенок, виден на белом Panel
|
|
||||||
const profileCardStyle = {
|
|
||||||
backgroundColor: "var(--color-accent-soft)",
|
|
||||||
border: "1px solid var(--color-border)",
|
|
||||||
borderRadius: "16px",
|
|
||||||
padding: "16px",
|
|
||||||
};
|
|
||||||
|
|
||||||
// Внутренний блок (Доставка/Самовывоз) — непрозрачный, контраст с карточкой
|
|
||||||
const innerBlockStyle = {
|
|
||||||
backgroundColor: "var(--color-surface-strong)",
|
|
||||||
border: "1px solid var(--color-border)",
|
|
||||||
borderRadius: "10px",
|
|
||||||
padding: "12px",
|
|
||||||
};
|
|
||||||
|
|
||||||
// Инпут — фон базовый
|
|
||||||
const inputStyle = {
|
|
||||||
backgroundColor: "var(--color-base)",
|
|
||||||
border: "1px solid var(--color-border)",
|
|
||||||
borderRadius: "10px",
|
|
||||||
padding: "8px 12px",
|
|
||||||
color: "var(--color-text)",
|
|
||||||
width: "100%",
|
|
||||||
fontSize: "14px",
|
|
||||||
};
|
|
||||||
|
|
||||||
const labelStyle = {
|
|
||||||
fontSize: "12px",
|
|
||||||
fontWeight: 500,
|
|
||||||
color: "var(--color-text-muted)",
|
|
||||||
};
|
|
||||||
|
|
||||||
// ── UI helpers ──────────────────────────────────────────────────────────
|
|
||||||
const ModeBadge = ({ mode }) => {
|
|
||||||
if (mode === "notification") {
|
|
||||||
return <span style={{ backgroundColor: "rgba(59,130,246,0.15)", color: "#3b82f6", borderRadius: "6px", padding: "2px 8px", fontSize: "10px", fontWeight: 600 }}>Уведомление</span>;
|
|
||||||
}
|
|
||||||
return <span style={{ backgroundColor: "rgba(34,197,94,0.15)", color: "#22c55e", borderRadius: "6px", padding: "2px 8px", fontSize: "10px", fontWeight: 600 }}>Ссылка</span>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Toggle = ({ checked, onChange, label }) => (
|
const Toggle = ({ checked, onChange, label }) => (
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||||
<button
|
<button type="button" onClick={onChange} style={{
|
||||||
type="button"
|
position: "relative", height: "20px", width: "40px", borderRadius: "9999px",
|
||||||
onClick={onChange}
|
transition: "all 0.2s", flexShrink: 0, border: "none", cursor: "pointer",
|
||||||
style={{
|
backgroundColor: checked ? "#22c55e" : "#c8d8d2",
|
||||||
position: "relative", height: "20px", width: "40px", borderRadius: "9999px",
|
}}>
|
||||||
transition: "all 0.2s", flexShrink: 0,
|
<span style={{ position: "absolute", top: "2px", height: "16px", width: "16px", borderRadius: "9999px", backgroundColor: "#fff", transition: "all 0.2s", left: checked ? "20px" : "2px" }} />
|
||||||
backgroundColor: checked ? "#22c55e" : "var(--color-border)",
|
|
||||||
border: "none", cursor: "pointer",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span style={{
|
|
||||||
position: "absolute", top: "2px", height: "16px", width: "16px", borderRadius: "9999px",
|
|
||||||
backgroundColor: "#fff", transition: "all 0.2s",
|
|
||||||
left: checked ? "20px" : "2px",
|
|
||||||
}} />
|
|
||||||
</button>
|
</button>
|
||||||
{label && <span style={{ fontSize: "13px", color: "var(--color-text)" }}>{label}</span>}
|
{label && <span className="sms-toggle-text">{label}</span>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const modeButton = (active, activeColor, activeBg, label, onClick) => ({
|
|
||||||
type: "button",
|
|
||||||
onClick,
|
|
||||||
style: {
|
|
||||||
flex: 1,
|
|
||||||
borderRadius: "10px",
|
|
||||||
border: `1px solid ${active ? activeColor : "var(--color-border)"}`,
|
|
||||||
backgroundColor: active ? activeBg : "transparent",
|
|
||||||
color: active ? activeColor : "var(--color-text-muted)",
|
|
||||||
padding: "8px 12px",
|
|
||||||
fontSize: "14px",
|
|
||||||
fontWeight: 500,
|
|
||||||
cursor: "pointer",
|
|
||||||
transition: "all 0.15s",
|
|
||||||
},
|
|
||||||
children: label,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
|
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
|
||||||
|
<style>{THEME_STYLES}</style>
|
||||||
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Panel className="p-5">
|
<Panel className="p-5">
|
||||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "12px" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "12px" }}>
|
||||||
<span style={{ fontSize: "20px" }}>🏙️</span>
|
<span style={{ fontSize: "20px" }}>🏙️</span>
|
||||||
<div>
|
<div>
|
||||||
<h2 style={{ fontSize: "14px", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.16em", color: "var(--color-text)" }}>
|
<h2 className="sms-section-title" style={{ textTransform: "uppercase", letterSpacing: "0.16em" }}>
|
||||||
Профили SMS по городам
|
Профили SMS по городам
|
||||||
</h2>
|
</h2>
|
||||||
<p style={{ fontSize: "12px", color: "var(--color-text-muted)", marginTop: "4px" }}>
|
<p className="sms-muted" style={{ marginTop: "4px" }}>
|
||||||
Настройка поведения SMS для каждого города: согласование по ссылке или уведомление.
|
Настройка поведения SMS для каждого города.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button type="button" className="sms-btn" onClick={() => { setShowForm(!showForm); if (!showForm) resetForm(); }}>
|
||||||
type="button"
|
|
||||||
onClick={() => { setShowForm(!showForm); if (!showForm) resetForm(); }}
|
|
||||||
style={{
|
|
||||||
borderRadius: "12px", backgroundColor: "var(--color-accent)", padding: "8px 16px",
|
|
||||||
fontSize: "12px", fontWeight: 600, color: "#fff", border: "none", cursor: "pointer",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{showForm ? "Отмена" : "+ Профиль"}
|
{showForm ? "Отмена" : "+ Профиль"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{message && <span style={{ fontSize: "14px", color: "var(--color-text)", display: "block", marginTop: "8px" }}>{message}</span>}
|
{message && <span className="sms-text" style={{ display: "block", marginTop: "8px" }}>{message}</span>}
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
{/* Add/Edit form */}
|
{/* Form */}
|
||||||
{showForm && (
|
{showForm && (
|
||||||
<Panel className="p-5" style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
|
<Panel className="p-5" style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
|
||||||
<h3 style={{ fontSize: "14px", fontWeight: 600, color: "var(--color-text)" }}>
|
<h3 className="sms-section-title">{editingId ? "Редактировать профиль" : "Новый профиль города"}</h3>
|
||||||
{editingId ? "Редактировать профиль" : "Новый профиль города"}
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
{/* City + master toggle */}
|
|
||||||
<div style={{ display: "grid", gap: "16px", gridTemplateColumns: "1fr 1fr" }}>
|
<div style={{ display: "grid", gap: "16px", gridTemplateColumns: "1fr 1fr" }}>
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: "4px" }}>
|
<div style={{ display: "flex", flexDirection: "column", gap: "4px" }}>
|
||||||
<label style={labelStyle}>Город</label>
|
<label className="sms-label">Город</label>
|
||||||
<select
|
<select value={formCity} onChange={(e) => setFormCity(e.target.value)} disabled={!!editingId}
|
||||||
value={formCity}
|
className="sms-input" style={{ opacity: editingId ? 0.5 : 1 }}>
|
||||||
onChange={(e) => setFormCity(e.target.value)}
|
{CRIMEAN_CITIES.map((c) => <option key={c} value={c}>{c}</option>)}
|
||||||
disabled={!!editingId}
|
|
||||||
style={{ ...inputStyle, opacity: editingId ? 0.5 : 1 }}
|
|
||||||
>
|
|
||||||
{CRIMEAN_CITIES.map((c) => (
|
|
||||||
<option key={c} value={c}>{c}</option>
|
|
||||||
))}
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div style={innerBlockStyle}>
|
<div className="sms-inner">
|
||||||
<Toggle checked={formSmsEnabled} onChange={() => setFormSmsEnabled(!formSmsEnabled)} label="SMS включены для города" />
|
<Toggle checked={formSmsEnabled} onChange={() => setFormSmsEnabled(!formSmsEnabled)} label="SMS включены для города" />
|
||||||
{!formSmsEnabled && (
|
{!formSmsEnabled && <p style={{ fontSize: "12px", color: "#c93d3d", marginTop: "8px" }}>⚠ Все SMS в этот город отключены</p>}
|
||||||
<p style={{ fontSize: "12px", color: "var(--color-danger)", marginTop: "8px" }}>⚠ Все SMS в этот город отключены</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Delivery */}
|
{/* Delivery */}
|
||||||
<div style={innerBlockStyle}>
|
<div className="sms-inner">
|
||||||
<div style={{ fontSize: "12px", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.05em", color: "var(--color-text-muted)", marginBottom: "12px" }}>📦 Доставка</div>
|
<div className="sms-muted" style={{ fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: "12px" }}>📦 Доставка</div>
|
||||||
<div style={{ display: "flex", gap: "8px" }}>
|
<div style={{ display: "flex", gap: "8px" }}>
|
||||||
<button {...modeButton(formDeliveryMode === "link", "#22c55e", "rgba(34,197,94,0.1)", "🔗 Ссылка", () => setFormDeliveryMode("link"))} />
|
<button type="button" className={formDeliveryMode === "link" ? "sms-mode-btn-active-g" : "sms-mode-btn-inactive"} onClick={() => setFormDeliveryMode("link")}>🔗 Ссылка</button>
|
||||||
<button {...modeButton(formDeliveryMode === "notification", "#3b82f6", "rgba(59,130,246,0.1)", "📢 Уведомление", () => setFormDeliveryMode("notification"))} />
|
<button type="button" className={formDeliveryMode === "notification" ? "sms-mode-btn-active-b" : "sms-mode-btn-inactive"} onClick={() => setFormDeliveryMode("notification")}>📢 Уведомление</button>
|
||||||
</div>
|
</div>
|
||||||
{formDeliveryMode === "notification" && (
|
{formDeliveryMode === "notification" && (
|
||||||
<textarea
|
<textarea value={formDeliveryText} onChange={(e) => setFormDeliveryText(e.target.value)} placeholder="Ваш заказ готов. Доставим по графику." rows={2} className="sms-input" style={{ resize: "none", marginTop: "8px" }} />
|
||||||
value={formDeliveryText}
|
|
||||||
onChange={(e) => setFormDeliveryText(e.target.value)}
|
|
||||||
placeholder="Ваш заказ готов. Доставим по графику."
|
|
||||||
rows={2}
|
|
||||||
style={{ ...inputStyle, resize: "none", marginTop: "8px" }}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<p style={{ fontSize: "12px", color: "var(--color-text-muted)", marginTop: "8px" }}>
|
<p className="sms-muted" style={{ marginTop: "8px" }}>
|
||||||
{formDeliveryMode === "link" ? "Клиент получит ссылку для выбора даты доставки" : "Уведомление без ссылки, без 2-й SMS"}
|
{formDeliveryMode === "link" ? "Клиент получит ссылку для выбора даты" : "Уведомление без ссылки, без 2-й SMS"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Pickup */}
|
{/* Pickup */}
|
||||||
<div style={innerBlockStyle}>
|
<div className="sms-inner">
|
||||||
<div style={{ fontSize: "12px", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.05em", color: "var(--color-text-muted)", marginBottom: "12px" }}>🏪 Самовывоз</div>
|
<div className="sms-muted" style={{ fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: "12px" }}>🏪 Самовывоз</div>
|
||||||
<div style={{ display: "flex", gap: "8px" }}>
|
<div style={{ display: "flex", gap: "8px" }}>
|
||||||
<button {...modeButton(formPickupMode === "link", "#22c55e", "rgba(34,197,94,0.1)", "🔗 Ссылка", () => setFormPickupMode("link"))} />
|
<button type="button" className={formPickupMode === "link" ? "sms-mode-btn-active-g" : "sms-mode-btn-inactive"} onClick={() => setFormPickupMode("link")}>🔗 Ссылка</button>
|
||||||
<button {...modeButton(formPickupMode === "notification", "#3b82f6", "rgba(59,130,246,0.1)", "📢 Уведомление", () => setFormPickupMode("notification"))} />
|
<button type="button" className={formPickupMode === "notification" ? "sms-mode-btn-active-b" : "sms-mode-btn-inactive"} onClick={() => setFormPickupMode("notification")}>📢 Уведомление</button>
|
||||||
</div>
|
</div>
|
||||||
{formPickupMode === "notification" && (
|
{formPickupMode === "notification" && (
|
||||||
<textarea
|
<textarea value={formPickupText} onChange={(e) => setFormPickupText(e.target.value)} placeholder="Ваш заказ готов. Заберите со склада." rows={2} className="sms-input" style={{ resize: "none", marginTop: "8px" }} />
|
||||||
value={formPickupText}
|
|
||||||
onChange={(e) => setFormPickupText(e.target.value)}
|
|
||||||
placeholder="Ваш заказ готов. Заберите со склада."
|
|
||||||
rows={2}
|
|
||||||
style={{ ...inputStyle, resize: "none", marginTop: "8px" }}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Toggles row */}
|
|
||||||
<div style={{ display: "grid", gap: "12px", gridTemplateColumns: "1fr 1fr" }}>
|
<div style={{ display: "grid", gap: "12px", gridTemplateColumns: "1fr 1fr" }}>
|
||||||
<div style={innerBlockStyle}>
|
<div className="sms-inner"><Toggle checked={formSecondSms} onChange={() => setFormSecondSms(!formSecondSms)} label="2-я SMS (повтор)" /></div>
|
||||||
<Toggle checked={formSecondSms} onChange={() => setFormSecondSms(!formSecondSms)} label="2-я SMS (повтор)" />
|
<div className="sms-inner"><Toggle checked={formPaidStorage} onChange={() => setFormPaidStorage(!formPaidStorage)} label="SMS: платное хранение" /></div>
|
||||||
</div>
|
|
||||||
<div style={innerBlockStyle}>
|
|
||||||
<Toggle checked={formPaidStorage} onChange={() => setFormPaidStorage(!formPaidStorage)} label="SMS: платное хранение" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Note */}
|
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: "4px" }}>
|
<div style={{ display: "flex", flexDirection: "column", gap: "4px" }}>
|
||||||
<label style={labelStyle}>Комментарий</label>
|
<label className="sms-label">Комментарий</label>
|
||||||
<input
|
<input type="text" value={formNote} onChange={(e) => setFormNote(e.target.value)} placeholder="Например: Ялта — доставка без согласования" className="sms-input" />
|
||||||
type="text"
|
|
||||||
value={formNote}
|
|
||||||
onChange={(e) => setFormNote(e.target.value)}
|
|
||||||
placeholder="Например: Ялта — доставка без согласования"
|
|
||||||
style={inputStyle}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button type="button" disabled={saving} onClick={handleSave} className="sms-btn" style={{ padding: "10px 20px", fontSize: "14px", opacity: saving ? 0.4 : 1 }}>
|
||||||
type="button"
|
|
||||||
disabled={saving}
|
|
||||||
onClick={handleSave}
|
|
||||||
style={{
|
|
||||||
borderRadius: "12px", backgroundColor: "var(--color-accent)", padding: "10px 20px",
|
|
||||||
fontSize: "14px", fontWeight: 600, color: "#fff", border: "none", cursor: "pointer",
|
|
||||||
opacity: saving ? 0.4 : 1,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{saving ? "Сохранение..." : editingId ? "Обновить профиль" : "Сохранить профиль"}
|
{saving ? "Сохранение..." : editingId ? "Обновить профиль" : "Сохранить профиль"}
|
||||||
</button>
|
</button>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
@ -344,93 +282,62 @@ export const SmsCityProfilesPanel = () => {
|
||||||
|
|
||||||
{/* Profiles list */}
|
{/* Profiles list */}
|
||||||
<Panel className="p-5" style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
|
<Panel className="p-5" style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
|
||||||
<h2 style={{ fontSize: "14px", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.16em", color: "var(--color-text)" }}>
|
<h2 className="sms-section-title" style={{ textTransform: "uppercase", letterSpacing: "0.16em" }}>
|
||||||
Профили ({profiles.length})
|
Профили ({profiles.length})
|
||||||
</h2>
|
</h2>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<p style={{ fontSize: "14px", color: "var(--color-text-muted)" }}>Загрузка...</p>
|
<p className="sms-muted">Загрузка...</p>
|
||||||
) : profiles.length === 0 ? (
|
) : profiles.length === 0 ? (
|
||||||
<div style={{ border: "1px dashed var(--color-border)", borderRadius: "12px", padding: "24px", textAlign: "center" }}>
|
<div style={{ border: "1px dashed #c8d8d2", borderRadius: "12px", padding: "24px", textAlign: "center" }}>
|
||||||
<p style={{ fontSize: "14px", color: "var(--color-text-muted)" }}>
|
<p className="sms-muted">Профилей нет. Города без профиля используют стандартный флоу (ссылка на согласование).</p>
|
||||||
Профилей нет. Города без профиля используют стандартный флоу (ссылка на согласование).
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
|
<div style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
|
||||||
{profiles.map((p) => (
|
{profiles.map((p) => (
|
||||||
<div key={p.id} style={profileCardStyle}>
|
<div key={p.id} className="sms-card">
|
||||||
{/* Header row */}
|
{/* Header */}
|
||||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "12px" }}>
|
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "12px" }}>
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "12px" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "12px" }}>
|
||||||
<span style={{ fontSize: "16px", fontWeight: 600, color: "var(--color-text)" }}>{p.city}</span>
|
<span className="sms-city-name">{p.city}</span>
|
||||||
{!p.sms_enabled ? (
|
{!p.sms_enabled
|
||||||
<span style={{ backgroundColor: "rgba(239,68,68,0.15)", color: "#ef4444", borderRadius: "6px", padding: "2px 8px", fontSize: "10px", fontWeight: 600 }}>SMS OFF</span>
|
? <span style={{ backgroundColor: "rgba(239,68,68,0.15)", color: "#ef4444", borderRadius: "6px", padding: "2px 8px", fontSize: "10px", fontWeight: 600 }}>SMS OFF</span>
|
||||||
) : (
|
: <span style={{ backgroundColor: "rgba(34,197,94,0.15)", color: "#22c55e", borderRadius: "6px", padding: "2px 8px", fontSize: "10px", fontWeight: 600 }}>SMS ON</span>}
|
||||||
<span style={{ backgroundColor: "rgba(34,197,94,0.15)", color: "#22c55e", borderRadius: "6px", padding: "2px 8px", fontSize: "10px", fontWeight: 600 }}>SMS ON</span>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||||
<button
|
<button type="button" className="sms-btn-sm" onClick={() => handleEdit(p)}>✏️ Изменить</button>
|
||||||
type="button"
|
<button type="button" className="sms-btn-del" onClick={() => handleDelete(p.id)} title="Удалить">✕</button>
|
||||||
onClick={() => handleEdit(p)}
|
|
||||||
style={{
|
|
||||||
borderRadius: "8px", border: "1px solid var(--color-border)", padding: "4px 10px",
|
|
||||||
fontSize: "12px", fontWeight: 500, color: "var(--color-text)", cursor: "pointer",
|
|
||||||
backgroundColor: "transparent",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
✏️ Изменить
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => handleDelete(p.id)}
|
|
||||||
style={{
|
|
||||||
borderRadius: "8px", padding: "4px 8px", fontSize: "14px",
|
|
||||||
color: "var(--color-text-muted)", cursor: "pointer", backgroundColor: "transparent",
|
|
||||||
border: "none",
|
|
||||||
}}
|
|
||||||
title="Удалить"
|
|
||||||
>
|
|
||||||
✕
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Config grid */}
|
{/* Config */}
|
||||||
<div style={{ display: "grid", gap: "8px", gridTemplateColumns: "1fr 1fr", marginBottom: "12px" }}>
|
<div style={{ display: "grid", gap: "8px", gridTemplateColumns: "1fr 1fr", marginBottom: "12px" }}>
|
||||||
<div style={innerBlockStyle}>
|
<div className="sms-inner">
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" }}>
|
||||||
<span style={{ fontSize: "12px", fontWeight: 500, color: "var(--color-text)" }}>📦 Доставка</span>
|
<span className="sms-text" style={{ fontSize: "12px", fontWeight: 500 }}>📦 Доставка</span>
|
||||||
<ModeBadge mode={p.delivery_mode} />
|
<ModeBadge mode={p.delivery_mode} />
|
||||||
</div>
|
</div>
|
||||||
{p.delivery_text && (
|
{p.delivery_text && <p className="sms-italic">"{p.delivery_text}"</p>}
|
||||||
<p style={{ fontSize: "12px", color: "var(--color-text-muted)", fontStyle: "italic", lineHeight: "1.4", marginTop: "6px" }}>"{p.delivery_text}"</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div style={innerBlockStyle}>
|
<div className="sms-inner">
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" }}>
|
<div style={{ display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" }}>
|
||||||
<span style={{ fontSize: "12px", fontWeight: 500, color: "var(--color-text)" }}>🏪 Самовывоз</span>
|
<span className="sms-text" style={{ fontSize: "12px", fontWeight: 500 }}>🏪 Самовывоз</span>
|
||||||
<ModeBadge mode={p.pickup_mode} />
|
<ModeBadge mode={p.pickup_mode} />
|
||||||
</div>
|
</div>
|
||||||
{p.pickup_text && (
|
{p.pickup_text && <p className="sms-italic">"{p.pickup_text}"</p>}
|
||||||
<p style={{ fontSize: "12px", color: "var(--color-text-muted)", fontStyle: "italic", lineHeight: "1.4", marginTop: "6px" }}>"{p.pickup_text}"</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Toggles row */}
|
{/* Toggles */}
|
||||||
<div style={{ display: "flex", flexWrap: "wrap", alignItems: "center", gap: "16px", paddingTop: "8px", borderTop: "1px solid var(--color-border)" }}>
|
<div style={{ display: "flex", flexWrap: "wrap", alignItems: "center", gap: "16px", paddingTop: "8px", borderTop: "1px solid #c8d8d2" }}>
|
||||||
<span style={{ fontSize: "12px", fontWeight: 500, color: p.second_sms_enabled ? "var(--color-text)" : "var(--color-text-muted)" }}>
|
<span className="sms-text" style={{ fontSize: "12px", fontWeight: 500, opacity: p.second_sms_enabled ? 1 : 0.5 }}>
|
||||||
2-я SMS: {p.second_sms_enabled ? "✅" : "❌"}
|
2-я SMS: {p.second_sms_enabled ? "✅" : "❌"}
|
||||||
</span>
|
</span>
|
||||||
<span style={{ fontSize: "12px", fontWeight: 500, color: p.paid_storage_enabled ? "var(--color-text)" : "var(--color-text-muted)" }}>
|
<span className="sms-text" style={{ fontSize: "12px", fontWeight: 500, opacity: p.paid_storage_enabled ? 1 : 0.5 }}>
|
||||||
Платное хранение: {p.paid_storage_enabled ? "✅" : "❌"}
|
Платное хранение: {p.paid_storage_enabled ? "✅" : "❌"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{p.note && (
|
{p.note && <p className="sms-muted" style={{ paddingTop: "8px" }}>💬 {p.note}</p>}
|
||||||
<p style={{ fontSize: "12px", color: "var(--color-text-muted)", paddingTop: "8px" }}>💬 {p.note}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue