fix: SMS city profiles UI — контраст цветов, выравнивание
- bg-[var(--color-surface-strong)] вместо --color-bg внутри карточек - Карточки профилей: --color-surface-strong, вложенные блоки: --color-surface - Текст всегда var(--color-text) — виден в светлой и тёмной теме - Toggle: компактнее (h-5 w-10) - Выравнивание: flex-wrap, gap, border-t для разделения секций - SMS ON/OFF badge вместо toggle в строке заголовка
This commit is contained in:
parent
484f9efcba
commit
6c9e13d19a
|
|
@ -147,9 +147,9 @@ export const SmsCityProfilesPanel = () => {
|
||||||
// ── UI helpers ──────────────────────────────────────────────────────────
|
// ── UI helpers ──────────────────────────────────────────────────────────
|
||||||
const ModeBadge = ({ mode }) => {
|
const ModeBadge = ({ mode }) => {
|
||||||
if (mode === "notification") {
|
if (mode === "notification") {
|
||||||
return <span className="rounded-lg px-2 py-0.5 text-[10px] font-semibold uppercase" style={{ backgroundColor: "rgba(59,130,246,0.12)", color: "#3b82f6" }}>Уведомление</span>;
|
return <span className="rounded-md px-2 py-0.5 text-[10px] font-semibold" style={{ backgroundColor: "rgba(59,130,246,0.15)", color: "#3b82f6" }}>Уведомление</span>;
|
||||||
}
|
}
|
||||||
return <span className="rounded-lg px-2 py-0.5 text-[10px] font-semibold uppercase" style={{ backgroundColor: "rgba(34,197,94,0.12)", color: "#22c55e" }}>Ссылка</span>;
|
return <span className="rounded-md px-2 py-0.5 text-[10px] font-semibold" style={{ backgroundColor: "rgba(34,197,94,0.15)", color: "#22c55e" }}>Ссылка</span>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Toggle = ({ checked, onChange, label }) => (
|
const Toggle = ({ checked, onChange, label }) => (
|
||||||
|
|
@ -157,15 +157,16 @@ export const SmsCityProfilesPanel = () => {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onChange}
|
onClick={onChange}
|
||||||
className={`relative h-6 w-11 rounded-full transition shrink-0 ${checked ? "bg-[#22c55e]" : "bg-[var(--color-border)]"}`}
|
className={`relative h-5 w-10 rounded-full transition shrink-0 ${checked ? "bg-[#22c55e]" : "bg-[var(--color-border)]"}`}
|
||||||
>
|
>
|
||||||
<span className={`absolute top-0.5 h-5 w-5 rounded-full bg-white transition-all ${checked ? "left-[22px]" : "left-0.5"}`} />
|
<span className={`absolute top-0.5 h-4 w-4 rounded-full bg-white transition-all ${checked ? "left-[20px]" : "left-0.5"}`} />
|
||||||
</button>
|
</button>
|
||||||
<span className="text-xs text-[var(--color-text-muted)]">{label}</span>
|
{label && <span className="text-xs text-[var(--color-text)]">{label}</span>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const modeLabel = (mode) => mode === "notification" ? "Уведомление без ссылки" : "Согласование по ссылке";
|
const inputCls = "w-full rounded-lg border border-[var(--color-border)] bg-[var(--color-surface-strong)] px-3 py-2 text-sm text-[var(--color-text)] placeholder:text-[var(--color-text-muted)]";
|
||||||
|
const cardCls = "rounded-xl border border-[var(--color-border)] bg-[var(--color-surface-strong)] p-4 space-y-3";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
|
@ -191,58 +192,58 @@ export const SmsCityProfilesPanel = () => {
|
||||||
{showForm ? "Отмена" : "+ Профиль"}
|
{showForm ? "Отмена" : "+ Профиль"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{message && <span className="text-sm text-[var(--color-text-muted)]">{message}</span>}
|
{message && <span className="text-sm text-[var(--color-text)]">{message}</span>}
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
{/* Add/Edit form */}
|
{/* Add/Edit form */}
|
||||||
{showForm && (
|
{showForm && (
|
||||||
<Panel className="p-5 space-y-5">
|
<Panel className="p-5 space-y-4">
|
||||||
<h3 className="text-sm font-semibold text-[var(--color-text)]">
|
<h3 className="text-sm font-semibold text-[var(--color-text)]">
|
||||||
{editingId ? "Редактировать профиль" : "Новый профиль города"}
|
{editingId ? "Редактировать профиль" : "Новый профиль города"}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{/* City */}
|
{/* City + master toggle */}
|
||||||
<div className="space-y-1">
|
<div className="grid gap-4 sm:grid-cols-2">
|
||||||
<label className="text-xs font-medium text-[var(--color-text-muted)]">Город</label>
|
<div className="space-y-1">
|
||||||
<select
|
<label className="text-xs font-medium text-[var(--color-text-muted)]">Город</label>
|
||||||
value={formCity}
|
<select
|
||||||
onChange={(e) => setFormCity(e.target.value)}
|
value={formCity}
|
||||||
disabled={!!editingId}
|
onChange={(e) => setFormCity(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)] disabled:opacity-50"
|
disabled={!!editingId}
|
||||||
>
|
className={`${inputCls} disabled:opacity-50`}
|
||||||
{CRIMEAN_CITIES.map((c) => (
|
>
|
||||||
<option key={c} value={c}>{c}</option>
|
{CRIMEAN_CITIES.map((c) => (
|
||||||
))}
|
<option key={c} value={c}>{c}</option>
|
||||||
</select>
|
))}
|
||||||
</div>
|
</select>
|
||||||
|
</div>
|
||||||
{/* SMS enabled master toggle */}
|
<div className={cardCls}>
|
||||||
<div className="rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)] p-3">
|
<Toggle checked={formSmsEnabled} onChange={() => setFormSmsEnabled(!formSmsEnabled)} label="SMS включены для города" />
|
||||||
<Toggle checked={formSmsEnabled} onChange={() => setFormSmsEnabled(!formSmsEnabled)} label="SMS включены для этого города" />
|
{!formSmsEnabled && (
|
||||||
{!formSmsEnabled && (
|
<p className="text-xs text-[var(--color-danger)]">⚠ Все SMS в этот город отключены</p>
|
||||||
<p className="text-xs text-[var(--color-danger)] mt-2">⚠ Все SMS в этот город отключены</p>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Delivery */}
|
{/* Delivery */}
|
||||||
<div className="rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)] p-4 space-y-3">
|
<div className={cardCls}>
|
||||||
<div className="text-xs font-semibold uppercase tracking-wider text-[var(--color-text-muted)]">📦 Доставка</div>
|
<div className="text-xs font-semibold uppercase tracking-wider text-[var(--color-text-muted)]">📦 Доставка</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setFormDeliveryMode("link")}
|
onClick={() => setFormDeliveryMode("link")}
|
||||||
className={["flex-1 rounded-xl border px-3 py-2 text-sm font-medium transition",
|
className={["flex-1 rounded-lg border px-3 py-2 text-sm font-medium transition",
|
||||||
formDeliveryMode === "link" ? "border-[#22c55e] bg-[rgba(34,197,94,0.12)] text-[#22c55e]" : "border-[var(--color-border)] text-[var(--color-text-muted)]"].join(" ")}
|
formDeliveryMode === "link" ? "border-[#22c55e] bg-[rgba(34,197,94,0.1)] text-[#22c55e]" : "border-[var(--color-border)] text-[var(--color-text-muted)] hover:bg-[var(--color-surface)]"].join(" ")}
|
||||||
>
|
>
|
||||||
🔗 Ссылка на согласование
|
🔗 Ссылка
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setFormDeliveryMode("notification")}
|
onClick={() => setFormDeliveryMode("notification")}
|
||||||
className={["flex-1 rounded-xl border px-3 py-2 text-sm font-medium transition",
|
className={["flex-1 rounded-lg border px-3 py-2 text-sm font-medium transition",
|
||||||
formDeliveryMode === "notification" ? "border-[#3b82f6] bg-[rgba(59,130,246,0.12)] text-[#3b82f6]" : "border-[var(--color-border)] text-[var(--color-text-muted)]"].join(" ")}
|
formDeliveryMode === "notification" ? "border-[#3b82f6] bg-[rgba(59,130,246,0.1)] text-[#3b82f6]" : "border-[var(--color-border)] text-[var(--color-text-muted)] hover:bg-[var(--color-surface)]"].join(" ")}
|
||||||
>
|
>
|
||||||
📢 Уведомление без ссылки
|
📢 Уведомление
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{formDeliveryMode === "notification" && (
|
{formDeliveryMode === "notification" && (
|
||||||
|
|
@ -251,33 +252,33 @@ export const SmsCityProfilesPanel = () => {
|
||||||
onChange={(e) => setFormDeliveryText(e.target.value)}
|
onChange={(e) => setFormDeliveryText(e.target.value)}
|
||||||
placeholder="Ваш заказ готов. Доставим по графику."
|
placeholder="Ваш заказ готов. Доставим по графику."
|
||||||
rows={2}
|
rows={2}
|
||||||
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)] resize-none"
|
className={`${inputCls} resize-none`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<p className="text-xs text-[var(--color-text-muted)]">
|
<p className="text-xs text-[var(--color-text-muted)]">
|
||||||
{formDeliveryMode === "link" ? "Клиент получит ссылку для выбора даты доставки" : "Клиент получит уведомление — без согласования, без 2-й SMS"}
|
{formDeliveryMode === "link" ? "Клиент получит ссылку для выбора даты доставки" : "Уведомление без ссылки, без 2-й SMS"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Pickup */}
|
{/* Pickup */}
|
||||||
<div className="rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)] p-4 space-y-3">
|
<div className={cardCls}>
|
||||||
<div className="text-xs font-semibold uppercase tracking-wider text-[var(--color-text-muted)]">🏪 Самовывоз</div>
|
<div className="text-xs font-semibold uppercase tracking-wider text-[var(--color-text-muted)]">🏪 Самовывоз</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setFormPickupMode("link")}
|
onClick={() => setFormPickupMode("link")}
|
||||||
className={["flex-1 rounded-xl border px-3 py-2 text-sm font-medium transition",
|
className={["flex-1 rounded-lg border px-3 py-2 text-sm font-medium transition",
|
||||||
formPickupMode === "link" ? "border-[#22c55e] bg-[rgba(34,197,94,0.12)] text-[#22c55e]" : "border-[var(--color-border)] text-[var(--color-text-muted)]"].join(" ")}
|
formPickupMode === "link" ? "border-[#22c55e] bg-[rgba(34,197,94,0.1)] text-[#22c55e]" : "border-[var(--color-border)] text-[var(--color-text-muted)] hover:bg-[var(--color-surface)]"].join(" ")}
|
||||||
>
|
>
|
||||||
🔗 Ссылка на согласование
|
🔗 Ссылка
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setFormPickupMode("notification")}
|
onClick={() => setFormPickupMode("notification")}
|
||||||
className={["flex-1 rounded-xl border px-3 py-2 text-sm font-medium transition",
|
className={["flex-1 rounded-lg border px-3 py-2 text-sm font-medium transition",
|
||||||
formPickupMode === "notification" ? "border-[#3b82f6] bg-[rgba(59,130,246,0.12)] text-[#3b82f6]" : "border-[var(--color-border)] text-[var(--color-text-muted)]"].join(" ")}
|
formPickupMode === "notification" ? "border-[#3b82f6] bg-[rgba(59,130,246,0.1)] text-[#3b82f6]" : "border-[var(--color-border)] text-[var(--color-text-muted)] hover:bg-[var(--color-surface)]"].join(" ")}
|
||||||
>
|
>
|
||||||
📢 Уведомление без ссылки
|
📢 Уведомление
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{formPickupMode === "notification" && (
|
{formPickupMode === "notification" && (
|
||||||
|
|
@ -286,18 +287,18 @@ export const SmsCityProfilesPanel = () => {
|
||||||
onChange={(e) => setFormPickupText(e.target.value)}
|
onChange={(e) => setFormPickupText(e.target.value)}
|
||||||
placeholder="Ваш заказ готов. Заберите со склада."
|
placeholder="Ваш заказ готов. Заберите со склада."
|
||||||
rows={2}
|
rows={2}
|
||||||
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)] resize-none"
|
className={`${inputCls} resize-none`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Toggles row */}
|
{/* Toggles row */}
|
||||||
<div className="grid gap-3 sm:grid-cols-2">
|
<div className="grid gap-3 sm:grid-cols-2">
|
||||||
<div className="rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)] p-3">
|
<div className={cardCls}>
|
||||||
<Toggle checked={formSecondSms} onChange={() => setFormSecondSms(!formSecondSms)} label="2-я SMS (повторная отправка)" />
|
<Toggle checked={formSecondSms} onChange={() => setFormSecondSms(!formSecondSms)} label="2-я SMS (повтор)" />
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)] p-3">
|
<div className={cardCls}>
|
||||||
<Toggle checked={formPaidStorage} onChange={() => setFormPaidStorage(!formPaidStorage)} label="SMS о платном хранении" />
|
<Toggle checked={formPaidStorage} onChange={() => setFormPaidStorage(!formPaidStorage)} label="SMS: платное хранение" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -309,7 +310,7 @@ export const SmsCityProfilesPanel = () => {
|
||||||
value={formNote}
|
value={formNote}
|
||||||
onChange={(e) => setFormNote(e.target.value)}
|
onChange={(e) => setFormNote(e.target.value)}
|
||||||
placeholder="Например: Ялта — доставка без согласования"
|
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)]"
|
className={inputCls}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -340,28 +341,29 @@ export const SmsCityProfilesPanel = () => {
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{profiles.map((p) => (
|
{profiles.map((p) => (
|
||||||
<div key={p.id} className="rounded-2xl border border-[var(--color-border)] bg-[var(--color-surface)] p-4 space-y-3">
|
<div key={p.id} className="rounded-xl border border-[var(--color-border)] bg-[var(--color-surface-strong)] p-4 space-y-3">
|
||||||
{/* Header */}
|
{/* Header row */}
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-3">
|
||||||
<span className="text-base font-semibold text-[var(--color-text)]">{p.city}</span>
|
<span className="text-base font-semibold text-[var(--color-text)]">{p.city}</span>
|
||||||
<Toggle checked={p.sms_enabled} onChange={() => handleQuickToggle(p, "sms_enabled")} label="" />
|
{!p.sms_enabled ? (
|
||||||
{!p.sms_enabled && (
|
<span className="rounded-md px-2 py-0.5 text-[10px] font-semibold" style={{ backgroundColor: "rgba(239,68,68,0.15)", color: "#ef4444" }}>SMS OFF</span>
|
||||||
<span className="rounded-lg px-2 py-0.5 text-[10px] font-semibold uppercase" style={{ backgroundColor: "rgba(239,68,68,0.12)", color: "#ef4444" }}>SMS отключены</span>
|
) : (
|
||||||
|
<span className="rounded-md px-2 py-0.5 text-[10px] font-semibold" style={{ backgroundColor: "rgba(34,197,94,0.15)", color: "#22c55e" }}>SMS ON</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => handleEdit(p)}
|
onClick={() => handleEdit(p)}
|
||||||
className="rounded-lg border border-[var(--color-border)] px-2 py-1 text-xs font-medium text-[var(--color-text-muted)] hover:bg-[var(--color-surface-strong)]"
|
className="rounded-lg border border-[var(--color-border)] px-2.5 py-1 text-xs font-medium text-[var(--color-text)] hover:bg-[var(--color-surface)]"
|
||||||
>
|
>
|
||||||
✏️ Изменить
|
✏️ Изменить
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => handleDelete(p.id)}
|
onClick={() => handleDelete(p.id)}
|
||||||
className="rounded-lg p-1.5 text-[var(--color-text-muted)] hover:bg-[var(--color-surface-strong)] hover:text-[var(--color-danger)]"
|
className="rounded-lg p-1.5 text-[var(--color-text-muted)] hover:bg-[var(--color-surface)] hover:text-[var(--color-danger)]"
|
||||||
title="Удалить"
|
title="Удалить"
|
||||||
>
|
>
|
||||||
✕
|
✕
|
||||||
|
|
@ -370,41 +372,41 @@ export const SmsCityProfilesPanel = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Config grid */}
|
{/* Config grid */}
|
||||||
<div className="grid gap-2 sm:grid-cols-2 text-xs">
|
<div className="grid gap-2 sm:grid-cols-2">
|
||||||
{/* Delivery */}
|
{/* Delivery */}
|
||||||
<div className="rounded-lg bg-[var(--color-bg)] p-3 space-y-1">
|
<div className="rounded-lg border border-[var(--color-border)] bg-[var(--color-surface)] p-3 space-y-1.5">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
<span className="font-medium text-[var(--color-text)]">📦 Доставка:</span>
|
<span className="text-xs font-medium text-[var(--color-text)]">📦 Доставка</span>
|
||||||
<ModeBadge mode={p.delivery_mode} />
|
<ModeBadge mode={p.delivery_mode} />
|
||||||
</div>
|
</div>
|
||||||
{p.delivery_text && (
|
{p.delivery_text && (
|
||||||
<p className="text-[var(--color-text-muted)] italic">"{p.delivery_text}"</p>
|
<p className="text-xs text-[var(--color-text-muted)] italic leading-snug">"{p.delivery_text}"</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* Pickup */}
|
{/* Pickup */}
|
||||||
<div className="rounded-lg bg-[var(--color-bg)] p-3 space-y-1">
|
<div className="rounded-lg border border-[var(--color-border)] bg-[var(--color-surface)] p-3 space-y-1.5">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
<span className="font-medium text-[var(--color-text)]">🏪 Самовывоз:</span>
|
<span className="text-xs font-medium text-[var(--color-text)]">🏪 Самовывоз</span>
|
||||||
<ModeBadge mode={p.pickup_mode} />
|
<ModeBadge mode={p.pickup_mode} />
|
||||||
</div>
|
</div>
|
||||||
{p.pickup_text && (
|
{p.pickup_text && (
|
||||||
<p className="text-[var(--color-text-muted)] italic">"{p.pickup_text}"</p>
|
<p className="text-xs text-[var(--color-text-muted)] italic leading-snug">"{p.pickup_text}"</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Toggles */}
|
{/* Toggles row */}
|
||||||
<div className="flex items-center gap-4 text-xs">
|
<div className="flex flex-wrap items-center gap-4 pt-1 border-t border-[var(--color-border)]">
|
||||||
<span className={`font-medium ${p.second_sms_enabled ? "text-[var(--color-text)]" : "text-[var(--color-text-muted)] line-through"}`}>
|
<span className={`text-xs font-medium ${p.second_sms_enabled ? "text-[var(--color-text)]" : "text-[var(--color-text-muted)]"}`}>
|
||||||
2-я SMS: {p.second_sms_enabled ? "✅" : "❌"}
|
2-я SMS: {p.second_sms_enabled ? "✅" : "❌"}
|
||||||
</span>
|
</span>
|
||||||
<span className={`font-medium ${p.paid_storage_enabled ? "text-[var(--color-text)]" : "text-[var(--color-text-muted)] line-through"}`}>
|
<span className={`text-xs font-medium ${p.paid_storage_enabled ? "text-[var(--color-text)]" : "text-[var(--color-text-muted)]"}`}>
|
||||||
Платное хранение: {p.paid_storage_enabled ? "✅" : "❌"}
|
Платное хранение: {p.paid_storage_enabled ? "✅" : "❌"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{p.note && (
|
{p.note && (
|
||||||
<p className="text-xs text-[var(--color-text-muted)]">💬 {p.note}</p>
|
<p className="text-xs text-[var(--color-text-muted)] pt-1">💬 {p.note}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue