HEL-230: 按确认样图通栏重排提醒管理页
页面改为待提醒清单 → 提醒历史 → 发送处理提醒三块通栏;发送区按样图做成三步横向流程,窄屏竖排,保留原发送与历史留痕能力。 Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Cursor
multica-agent
parent
85e11a4f72
commit
f5e0915f63
@@ -0,0 +1,171 @@
|
||||
"""HEL-230: 提醒管理页通栏三块与三步发送流的结构/响应式契约。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import threading
|
||||
import unittest
|
||||
from functools import partial
|
||||
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
WEB = ROOT / "web"
|
||||
|
||||
try:
|
||||
from playwright.sync_api import sync_playwright
|
||||
except ImportError: # pragma: no cover
|
||||
sync_playwright = None
|
||||
|
||||
|
||||
class RemindersPageSourceContractTests(unittest.TestCase):
|
||||
def test_admin_html_order_and_send_flow(self) -> None:
|
||||
html = (WEB / "admin.html").read_text(encoding="utf-8")
|
||||
self.assertIn('data-page="reminders"', html)
|
||||
self.assertIn('id="pending-reminders-card"', html)
|
||||
self.assertIn('id="reminder-history-card"', html)
|
||||
self.assertIn('id="send-reminder-card"', html)
|
||||
self.assertIn('class="send-flow"', html)
|
||||
self.assertIn('class="sf-step"', html)
|
||||
self.assertIn('id="pick-count"', html)
|
||||
self.assertIn('id="reminderForm"', html)
|
||||
self.assertIn('id="reminderCompanyList"', html)
|
||||
self.assertIn('id="reminder-type"', html)
|
||||
self.assertIn('id="reminder-content"', html)
|
||||
self.assertIn('id="reminder-deadline"', html)
|
||||
self.assertIn('id="send-btn"', html)
|
||||
self.assertIn('id="send-hint"', html)
|
||||
self.assertIn('id="reminder-tbody"', html)
|
||||
self.assertIn('id="reminder-tabs"', html)
|
||||
self.assertIn('id="reminder-detail-drawer"', html)
|
||||
self.assertIn("design-system.css?v=9", html)
|
||||
self.assertIn("app.js?v=14", html)
|
||||
pending = html.index('id="pending-reminders-card"')
|
||||
history = html.index('id="reminder-history-card"')
|
||||
send = html.index('id="send-reminder-card"')
|
||||
self.assertLess(pending, history)
|
||||
self.assertLess(history, send)
|
||||
reminders = html[html.index('data-page="reminders"') : html.index("新增公司弹窗")]
|
||||
self.assertNotIn("grid-1-2", reminders)
|
||||
self.assertEqual(reminders.count('class="sf-step"'), 3)
|
||||
self.assertIn("选择接收公司", reminders)
|
||||
self.assertIn("填写提醒内容", reminders)
|
||||
self.assertIn("设定截止并发送", reminders)
|
||||
|
||||
def test_app_js_keeps_real_reminder_apis(self) -> None:
|
||||
js = (WEB / "app.js").read_text(encoding="utf-8")
|
||||
self.assertIn('label.className = "pick"', js)
|
||||
self.assertIn("function updatePickCount", js)
|
||||
self.assertIn("function syncPendingPicks", js)
|
||||
self.assertIn("/api/admin/reminders/pending", js)
|
||||
self.assertIn("/api/admin/reminders/manual", js)
|
||||
self.assertIn("/api/admin/reminders/send", js)
|
||||
self.assertIn("/api/admin/reminders/scan", js)
|
||||
self.assertIn("function loadAdminRemindersPending", js)
|
||||
self.assertIn("function loadAdminRemindersHistory", js)
|
||||
self.assertIn("function openReminderDetailDrawer", js)
|
||||
self.assertIn("initAdminReminders()", js)
|
||||
self.assertIn("dataset.companyId", js)
|
||||
|
||||
def test_design_system_send_flow_uses_tokens(self) -> None:
|
||||
css = (WEB / "design-system.css").read_text(encoding="utf-8")
|
||||
self.assertIn(".send-flow", css)
|
||||
self.assertIn(".sf-step", css)
|
||||
self.assertIn(".pick", css)
|
||||
self.assertIn("minmax(0, 1.5fr)", css)
|
||||
block_start = css.index("提醒管理:三步发送流")
|
||||
block = css[block_start:]
|
||||
self.assertIn("var(--border)", block)
|
||||
self.assertIn("var(--accent-soft)", block)
|
||||
self.assertIn("var(--accent)", block)
|
||||
self.assertIn("@media (max-width: 1100px)", block)
|
||||
self.assertNotRegex(block, r"#[0-9a-fA-F]{3,8}")
|
||||
self.assertNotRegex(block, r"rgb\(")
|
||||
self.assertNotIn("box-shadow: 0 0", block)
|
||||
|
||||
|
||||
def _chromium_available() -> bool:
|
||||
try:
|
||||
import ctypes.util
|
||||
|
||||
return bool(ctypes.util.find_library("atk-1.0"))
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
@unittest.skipUnless(sync_playwright, "playwright 未安装,跳过布局冒烟")
|
||||
@unittest.skipUnless(_chromium_available(), "系统缺少 chromium 依赖库(如 libatk),跳过布局冒烟")
|
||||
class RemindersPageLayoutSmokeTests(unittest.TestCase):
|
||||
"""桌面三列 / 窄屏竖排,360 / 820 / 1440 无页面级横向溢出。"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
handler = partial(SimpleHTTPRequestHandler, directory=str(WEB))
|
||||
cls.httpd = ThreadingHTTPServer(("127.0.0.1", 0), handler)
|
||||
cls.port = cls.httpd.server_address[1]
|
||||
cls.thread = threading.Thread(target=cls.httpd.serve_forever, daemon=True)
|
||||
cls.thread.start()
|
||||
cls.base = f"http://127.0.0.1:{cls.port}"
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls) -> None:
|
||||
cls.httpd.shutdown()
|
||||
cls.httpd.server_close()
|
||||
|
||||
def _open_reminders(self, page, width: int) -> None:
|
||||
html = (WEB / "admin.html").read_text(encoding="utf-8")
|
||||
page.set_viewport_size({"width": width, "height": 900})
|
||||
page.set_content(
|
||||
html.replace('src="app.js?v=14"', 'src=""'),
|
||||
base_url=self.base,
|
||||
)
|
||||
page.evaluate(
|
||||
"""() => {
|
||||
document.querySelectorAll('.app-view').forEach((el) => {
|
||||
el.classList.toggle('is-active', el.dataset.page === 'reminders');
|
||||
});
|
||||
const list = document.getElementById('reminderCompanyList');
|
||||
if (list && !list.children.length) {
|
||||
['A公司', 'B公司', '郑州金牛建业煤炭有限责任公司'].forEach((name, i) => {
|
||||
const label = document.createElement('label');
|
||||
label.className = 'pick';
|
||||
label.innerHTML = '<input type="checkbox" name="company" value="' + (i + 1) + '">' + name;
|
||||
if (i < 2) label.querySelector('input').checked = true;
|
||||
list.append(label);
|
||||
});
|
||||
}
|
||||
}"""
|
||||
)
|
||||
|
||||
def test_send_flow_columns_and_no_page_overflow(self) -> None:
|
||||
html = (WEB / "admin.html").read_text(encoding="utf-8")
|
||||
self.assertIn("app.js?v=14", html)
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch()
|
||||
page = browser.new_page()
|
||||
self._open_reminders(page, 1440)
|
||||
cols_wide = page.evaluate(
|
||||
"() => getComputedStyle(document.querySelector('.send-flow')).gridTemplateColumns"
|
||||
)
|
||||
self.assertEqual(len(cols_wide.split()), 3, f"桌面端应为三列,实际: {cols_wide}")
|
||||
overflow_wide = page.evaluate(
|
||||
"() => document.documentElement.scrollWidth > document.documentElement.clientWidth + 1"
|
||||
)
|
||||
self.assertFalse(overflow_wide, "1440px 出现页面级横向溢出")
|
||||
|
||||
self._open_reminders(page, 900)
|
||||
cols_narrow = page.evaluate(
|
||||
"() => getComputedStyle(document.querySelector('.send-flow')).gridTemplateColumns"
|
||||
)
|
||||
self.assertEqual(len(cols_narrow.split()), 1, f"窄屏应为单列,实际: {cols_narrow}")
|
||||
|
||||
for width in (360, 820, 1440):
|
||||
self._open_reminders(page, width)
|
||||
overflow = page.evaluate(
|
||||
"() => document.documentElement.scrollWidth > document.documentElement.clientWidth + 1"
|
||||
)
|
||||
self.assertFalse(overflow, f"{width}px 出现页面级横向溢出")
|
||||
browser.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
+88
-66
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="金牛集团管理端" />
|
||||
<title>管理端 · 金牛集团</title>
|
||||
<link rel="stylesheet" href="design-system.css?v=8" />
|
||||
<link rel="stylesheet" href="design-system.css?v=9" />
|
||||
</head>
|
||||
<body data-portal="admin">
|
||||
<a class="skip-link" href="#main-content">跳到主要内容</a>
|
||||
@@ -638,7 +638,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" id="pending-reminders-card" style="margin-bottom: 16px;">
|
||||
<div class="stack">
|
||||
<div class="card" id="pending-reminders-card">
|
||||
<div class="card-head">
|
||||
<span class="card-title">待提醒清单<span class="sub">系统按流水提交、断档、待确认自动发现,点发送即送达对应公司</span></span>
|
||||
<div class="row" style="gap: 10px; align-items: center;">
|
||||
@@ -651,72 +652,93 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-1-2">
|
||||
<div class="card">
|
||||
<div class="card-head">
|
||||
<span class="card-title">发送处理提醒<span class="sub">选择公司后系统自动列出待提醒事项,核对后一键发送</span></span>
|
||||
</div>
|
||||
<form id="reminderForm" novalidate>
|
||||
<div class="field" style="margin-bottom: 14px;">
|
||||
<label>接收公司(可多选)</label>
|
||||
<div class="row" id="reminderCompanyList" style="flex-wrap: wrap; gap: 8px 16px;"></div>
|
||||
<span class="hint error" id="company-error" style="display: none;">请至少选择一家接收公司</span>
|
||||
</div>
|
||||
<div class="field" style="margin-bottom: 14px;">
|
||||
<label for="reminder-type">提醒类型</label>
|
||||
<select class="select" id="reminder-type">
|
||||
<option selected>流水未提交</option>
|
||||
<option>单边待确认</option>
|
||||
<option>科目待确认</option>
|
||||
<option>账户登记</option>
|
||||
<option>其他</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field" style="margin-bottom: 14px;">
|
||||
<label for="reminder-content">提醒内容</label>
|
||||
<textarea class="textarea" id="reminder-content">请于截止日期前完成 2026 年 7 月银行流水上传与待确认事项处理。</textarea>
|
||||
<span class="hint error" id="content-error" style="display: none;">提醒内容不能为空</span>
|
||||
</div>
|
||||
<div class="field" style="margin-bottom: 18px;">
|
||||
<label for="reminder-deadline">截止日期</label>
|
||||
<input class="input" type="date" id="reminder-deadline" value="2026-08-29" min="2026-08-20" />
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" id="send-btn" style="width: 100%;">发送提醒</button>
|
||||
<p class="hint" id="send-hint" style="margin-top: 10px; display: none;"></p>
|
||||
</form>
|
||||
<div class="card" id="reminder-history-card">
|
||||
<div class="card-head">
|
||||
<span class="card-title">提醒历史<span class="sub">含系统自动触发与人工发送的全部提醒记录</span></span>
|
||||
</div>
|
||||
<div class="tabs" id="reminder-tabs">
|
||||
<button type="button" class="active" data-filter="all">全部<span class="tab-count" id="count-all">0</span></button>
|
||||
<button type="button" data-filter="system">系统提醒<span class="tab-count" id="count-system">0</span></button>
|
||||
<button type="button" data-filter="manual">人工提醒<span class="tab-count" id="count-manual">0</span></button>
|
||||
</div>
|
||||
<div class="table-wrap history-table">
|
||||
<table class="ds-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>公司</th>
|
||||
<th>来源</th>
|
||||
<th>类型</th>
|
||||
<th class="wrap">内容摘要</th>
|
||||
<th>发送时间</th>
|
||||
<th>截止日期</th>
|
||||
<th>处理状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="reminder-tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="table-foot">
|
||||
<span id="table-foot-count">共 0 条提醒记录</span>
|
||||
<span id="table-foot-state">未读 0 · 处理中 0 · 已完成 0</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-head">
|
||||
<span class="card-title">提醒历史<span class="sub">含系统自动触发与人工发送的全部提醒记录</span></span>
|
||||
</div>
|
||||
<div class="tabs" id="reminder-tabs">
|
||||
<button type="button" class="active" data-filter="all">全部<span class="tab-count" id="count-all">0</span></button>
|
||||
<button type="button" data-filter="system">系统提醒<span class="tab-count" id="count-system">0</span></button>
|
||||
<button type="button" data-filter="manual">人工提醒<span class="tab-count" id="count-manual">0</span></button>
|
||||
</div>
|
||||
<div class="table-wrap" style="border: 0;">
|
||||
<table class="ds-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>公司</th>
|
||||
<th>来源</th>
|
||||
<th>类型</th>
|
||||
<th class="wrap">内容摘要</th>
|
||||
<th>发送时间</th>
|
||||
<th>截止日期</th>
|
||||
<th>处理状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="reminder-tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="table-foot">
|
||||
<span id="table-foot-count">共 0 条提醒记录</span>
|
||||
<span id="table-foot-state">未读 0 · 处理中 0 · 已完成 0</span>
|
||||
</div>
|
||||
<div class="card" id="send-reminder-card">
|
||||
<div class="card-head">
|
||||
<span class="card-title">发送处理提醒<span class="sub">将以人工提醒形式送达所选公司出纳与财务负责人</span></span>
|
||||
</div>
|
||||
<form id="reminderForm" novalidate>
|
||||
<div class="send-flow">
|
||||
<div class="sf-step">
|
||||
<div class="sf-step-head">
|
||||
<span class="sf-idx">01</span>
|
||||
<span>选择接收公司</span>
|
||||
<span class="meta" id="pick-count">已选 0 家</span>
|
||||
</div>
|
||||
<div class="pick-list" id="reminderCompanyList"></div>
|
||||
<span class="hint error" id="company-error" style="display: none;">请至少选择一家接收公司</span>
|
||||
<p class="hint">每家公司将分别生成一条提醒,可在上方「待提醒清单」勾选后自动带入。</p>
|
||||
</div>
|
||||
<div class="sf-step">
|
||||
<div class="sf-step-head">
|
||||
<span class="sf-idx">02</span>
|
||||
<span>填写提醒内容</span>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="reminder-type">提醒类型</label>
|
||||
<select class="select" id="reminder-type">
|
||||
<option selected>流水未提交</option>
|
||||
<option>单边待确认</option>
|
||||
<option>科目待确认</option>
|
||||
<option>账户登记</option>
|
||||
<option>其他</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="reminder-content">提醒内容</label>
|
||||
<textarea class="textarea" id="reminder-content">请于截止日期前完成 2026 年 7 月银行流水上传与待确认事项处理。</textarea>
|
||||
<span class="hint error" id="content-error" style="display: none;">提醒内容不能为空</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sf-step">
|
||||
<div class="sf-step-head">
|
||||
<span class="sf-idx">03</span>
|
||||
<span>设定截止并发送</span>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="reminder-deadline">截止日期</label>
|
||||
<input class="input" type="date" id="reminder-deadline" value="2026-08-29" min="2026-08-20" />
|
||||
</div>
|
||||
<div class="sf-send">
|
||||
<button type="submit" class="btn btn-primary" id="send-btn">发送提醒</button>
|
||||
<p class="hint" id="send-result-hint">发送后可在上方「提醒历史」中跟踪触达与处理状态。</p>
|
||||
<p class="hint" id="send-hint" style="display: none;"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
@@ -1011,6 +1033,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="toast-region" id="toastRegion" aria-live="polite"></div>
|
||||
<script src="app.js?v=13"></script>
|
||||
<script src="app.js?v=14"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+32
-5
@@ -1143,16 +1143,15 @@ function fillCompanySelects(names, companies) {
|
||||
if (reminderList && companies?.length) {
|
||||
reminderList.replaceChildren(...companies.map((company) => {
|
||||
const label = document.createElement("label");
|
||||
label.className = "row";
|
||||
label.style.cssText = "gap:6px;font-size:13px;";
|
||||
label.className = "pick";
|
||||
const input = document.createElement("input");
|
||||
input.type = "checkbox";
|
||||
input.name = "company";
|
||||
input.value = String(company.id);
|
||||
input.style.width = "auto";
|
||||
label.append(input, document.createTextNode(company.name));
|
||||
return label;
|
||||
}));
|
||||
syncPendingPicks();
|
||||
}
|
||||
setSelectOptions($('#openingDialog [name="from"]'), names);
|
||||
setSelectOptions($('#openingDialog [name="to"]'), names);
|
||||
@@ -1809,6 +1808,23 @@ function reminderStatusLabel(statusUi) {
|
||||
return { unread: "未读", doing: "处理中", done: "已完成" }[statusUi] || statusUi;
|
||||
}
|
||||
|
||||
function updatePickCount() {
|
||||
const countEl = $("#pick-count");
|
||||
if (!countEl) return;
|
||||
const n = $$('#reminderCompanyList input[name="company"]:checked').length;
|
||||
countEl.textContent = "已选 " + n + " 家";
|
||||
}
|
||||
|
||||
function syncPendingPicks() {
|
||||
$$(".pending-check:checked").forEach((input) => {
|
||||
const companyId = input.closest(".list-row")?.dataset.companyId;
|
||||
if (!companyId) return;
|
||||
const chip = document.querySelector('#reminderCompanyList input[name="company"][value="' + companyId + '"]');
|
||||
if (chip) chip.checked = true;
|
||||
});
|
||||
updatePickCount();
|
||||
}
|
||||
|
||||
async function loadAdminRemindersPending() {
|
||||
const list = $("#pending-list");
|
||||
const empty = $("#pending-empty");
|
||||
@@ -1839,6 +1855,7 @@ async function loadAdminRemindersPending() {
|
||||
const row = document.createElement("div");
|
||||
row.className = "list-row";
|
||||
row.dataset.dedupeKey = item.dedupe_key;
|
||||
row.dataset.companyId = String(item.company_id);
|
||||
const sentHint = item.send_count > 0 ? ` · 已提醒 ${item.send_count} 次` : "";
|
||||
row.innerHTML =
|
||||
'<label class="row" style="gap:6px;flex:none;"><input type="checkbox" class="pending-check" style="width:auto;" /></label>' +
|
||||
@@ -1994,8 +2011,17 @@ function initAdminReminders() {
|
||||
if (!event.target.classList.contains("pending-check")) return;
|
||||
const checked = $$(".pending-check:checked").length;
|
||||
const sendAll = $("#pending-send-all");
|
||||
if (!sendAll) return;
|
||||
sendAll.textContent = checked ? `发送选中(${checked})` : "全部一键发送";
|
||||
if (sendAll) sendAll.textContent = checked ? `发送选中(${checked})` : "全部一键发送";
|
||||
if (event.target.checked) {
|
||||
const companyId = event.target.closest(".list-row")?.dataset.companyId;
|
||||
const chip = companyId && document.querySelector('#reminderCompanyList input[name="company"][value="' + companyId + '"]');
|
||||
if (chip) chip.checked = true;
|
||||
}
|
||||
updatePickCount();
|
||||
});
|
||||
|
||||
$("#reminderCompanyList")?.addEventListener("change", (event) => {
|
||||
if (event.target.name === "company") updatePickCount();
|
||||
});
|
||||
|
||||
$("#reminderForm")?.addEventListener("submit", async (event) => {
|
||||
@@ -2031,6 +2057,7 @@ function initAdminReminders() {
|
||||
return company?.name || c.value;
|
||||
}).join("、");
|
||||
form.querySelectorAll('input[name="company"]').forEach((c) => { c.checked = false; });
|
||||
updatePickCount();
|
||||
const sendHint = $("#send-hint");
|
||||
sendHint.style.display = "";
|
||||
sendHint.textContent = `已发送给 ${companies},共 ${checked.length} 家公司。`;
|
||||
|
||||
@@ -1083,3 +1083,97 @@ body[data-portal="company"] .side-nav a[data-view="transfers"].active svg {
|
||||
.xfer-split { grid-template-columns: 1fr; }
|
||||
.xfer-split-pane.confirmed { border-right: 0; border-bottom: 1px solid var(--border); }
|
||||
}
|
||||
|
||||
/* ─── 提醒管理:三步发送流(HEL-230) ─────────────────────────── */
|
||||
.send-flow {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr) minmax(0, 1fr);
|
||||
align-items: stretch;
|
||||
gap: 0;
|
||||
}
|
||||
.sf-step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
padding: 0 18px;
|
||||
}
|
||||
.sf-step:first-child { padding-left: 0; }
|
||||
.sf-step:last-child { padding-right: 0; }
|
||||
.sf-step + .sf-step { border-left: 1px solid var(--border); }
|
||||
.sf-step-head {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
}
|
||||
.sf-idx {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
font-weight: 650;
|
||||
color: var(--accent);
|
||||
flex: none;
|
||||
}
|
||||
.sf-step-head > span:nth-child(2) { font-size: 13px; font-weight: 650; }
|
||||
.sf-step-head .meta { margin-left: auto; }
|
||||
.sf-step .hint { font-size: 11.5px; color: var(--muted); }
|
||||
.sf-step .hint.error { color: var(--danger); }
|
||||
.pick-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.pick {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 5px 10px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
background: var(--surface);
|
||||
color: var(--fg);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
|
||||
}
|
||||
.pick:hover { background: var(--fg-soft); }
|
||||
.pick:has(input:checked) {
|
||||
background: var(--accent-soft);
|
||||
border-color: var(--accent);
|
||||
color: var(--accent);
|
||||
}
|
||||
.pick input { width: auto; margin: 0; }
|
||||
.sf-send {
|
||||
margin-top: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
.sf-send .btn-primary { width: 100%; }
|
||||
|
||||
[data-page="reminders"] .history-table {
|
||||
border: 0;
|
||||
}
|
||||
[data-page="reminders"] .history-table .ds-table {
|
||||
min-width: 0;
|
||||
}
|
||||
[data-page="reminders"] .history-table .ds-table th:nth-child(1),
|
||||
[data-page="reminders"] .history-table .ds-table td:nth-child(1) {
|
||||
width: 14%;
|
||||
}
|
||||
[data-page="reminders"] .history-table .ds-table th:nth-child(4),
|
||||
[data-page="reminders"] .history-table .ds-table td:nth-child(4) {
|
||||
width: 28%;
|
||||
white-space: normal;
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.send-flow { grid-template-columns: minmax(0, 1fr); }
|
||||
.sf-step { padding: 14px 0 0; }
|
||||
.sf-step:first-child { padding-top: 0; }
|
||||
.sf-step + .sf-step {
|
||||
border-left: 0;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user