improve BA skill

This commit is contained in:
Leonard-ThindPad-P50
2026-09-09 06:34:57 +07:00
parent 119792967c
commit 2c7bcde741
42 changed files with 5429 additions and 54 deletions

View File

@@ -0,0 +1,239 @@
<!--
WF_US002-003_v1.0.html — wireframe low-fi tự chứa, sinh bởi skill ba-3-specification (activity wf).
Nguồn hành vi: SRS_US002-003_v1.0.md · Nguồn bố cục: e-commerce/docs/sections/07-giao-dien.md §7.1.1 SCR-04 (chế độ 🎨)
Nguồn quy ước: UICONV_e-commerce_v1.0.md v1.1
Giữ nguyên <style>/<script> của template gốc (ba-3-specification/templates/wireframe.html) để mọi WF
trong dự án trông và thao tác giống nhau. Chỉ thay nội dung trong <main>.
Thành phần chỉ có ở prototype (SAD §7.1.1) mà SRS chưa có (checkbox chọn dòng, badge "Sản phẩm đã hết
hàng", badge "Giá đã thay đổi") được vẽ trong khối .proto-only, KHÔNG có data-c — xem WF §5 (#1,#2,#3).
-->
<!doctype html>
<html lang="vi">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>WF — US-002, US-003 — Giỏ hàng (SCR-04)</title>
<style>
:root{--ink:#222;--mute:#777;--line:#bbb;--fill:#eee;--fill2:#f7f7f7;--accent:#3b6ea5;--danger:#b23a3a;--bg:#fafafa}
*{box-sizing:border-box}
body{margin:0;font:14px/1.45 system-ui,Segoe UI,Roboto,sans-serif;color:var(--ink);background:var(--bg)}
header.toolbar{position:sticky;top:0;z-index:9;background:#fff;border-bottom:1px solid var(--line);padding:8px 16px;display:flex;gap:16px;align-items:center;flex-wrap:wrap}
header.toolbar h1{font-size:15px;margin:0 16px 0 0}
header.toolbar label{font-size:12px;color:var(--mute)}
header.toolbar select{font:inherit}
.legend{font-size:12px;color:var(--mute);margin-left:auto}
main{padding:24px 16px;display:grid;gap:40px;justify-items:center}
.screen{width:100%;max-width:1200px;background:#fff;border:1px solid var(--line);border-radius:6px;overflow:hidden}
.screen>h2{margin:0;padding:8px 12px;font-size:13px;font-weight:600;background:var(--fill2);border-bottom:1px dashed var(--line);color:var(--mute)}
.screen>h2 small{font-weight:400;margin-left:8px}
.body{padding:12px;display:grid;gap:12px}
.zone{border:1px dashed var(--line);border-radius:4px;padding:10px;position:relative;background:#fff;min-height:44px}
.zone::before{content:attr(data-zone) " · " attr(data-name);position:absolute;top:-8px;left:8px;background:#fff;padding:0 4px;font-size:10px;color:var(--mute);letter-spacing:.02em}
.row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.row.right{justify-content:flex-end}
.row.between{justify-content:space-between}
[data-c]{position:relative}
[data-c]::after{content:attr(data-c);position:absolute;top:-9px;right:-4px;font-size:9px;background:var(--ink);color:#fff;padding:0 4px;border-radius:3px;line-height:14px;opacity:.85}
.label{font-weight:600;font-size:16px}
.btn{display:inline-block;padding:6px 14px;border:1px solid var(--ink);border-radius:4px;background:#fff;cursor:default;white-space:nowrap}
.btn-primary{background:var(--accent);border-color:var(--accent);color:#fff}
.btn-secondary{background:#fff}
.btn-danger{border-color:var(--danger);color:var(--danger)}
.btn[aria-disabled="true"]{opacity:.45;border-style:dashed}
.link{color:var(--accent);text-decoration:underline}
.input,.select{display:inline-block;min-width:160px;padding:6px 8px;border:1px solid var(--line);border-radius:4px;background:#fff;color:var(--mute)}
.select::after{content:" ▾";float:right}
.field{display:grid;gap:4px}
.field>span{font-size:12px}
.field>span .req{color:var(--danger)}
.hint{font-size:12px;color:var(--mute)}
.table{width:100%;border-collapse:collapse}
.table th,.table td{border:1px solid var(--line);padding:6px 8px;text-align:left;font-size:13px}
.table th{background:var(--fill2)}
.card{border:1px solid var(--line);border-radius:4px;padding:10px;background:#fff}
.badge{display:inline-block;padding:0 8px;border:1px solid var(--line);border-radius:10px;font-size:12px;background:var(--fill2)}
.img{background:repeating-linear-gradient(45deg,var(--fill),var(--fill) 6px,var(--fill2) 6px,var(--fill2) 12px);border:1px solid var(--line);min-height:80px;display:grid;place-items:center;color:var(--mute);font-size:12px}
.skeleton{height:14px;background:linear-gradient(90deg,var(--fill),var(--fill2),var(--fill));border-radius:3px;margin:8px 0}
.state{padding:24px;text-align:center;color:var(--mute);border:1px dashed var(--line);border-radius:4px;display:grid;gap:10px;justify-items:center}
.state.error{border-color:var(--danger);color:var(--danger)}
.state .illus{width:80px;height:60px}
.spinner{display:inline-block;width:14px;height:14px;border:2px solid #fff;border-top-color:transparent;border-radius:50%;vertical-align:-2px;margin-right:6px}
.modal{max-width:520px;margin:0 auto;border:1px solid var(--ink);border-radius:6px;background:#fff;box-shadow:0 0 0 2000px rgba(0,0,0,.25)}
.modal .body{padding:16px}
[data-state]{display:none}
[data-state="default"]{display:block}
body[data-view="mobile"] .screen{max-width:390px}
body[data-view="mobile"] .m-hide{display:none!important}
body[data-view="mobile"] .m-stack{flex-direction:column;align-items:stretch}
body[data-view="desktop"] .d-hide{display:none!important}
body[data-view="mobile"] .table{display:none}
body[data-view="mobile"] .cards{display:grid!important;gap:8px}
.cards{display:none}
footer.note{padding:16px;font-size:12px;color:var(--mute);text-align:center}
.proto-only{border:1px dashed var(--danger);border-radius:4px;padding:6px;background:#fdf2f2}
.proto-only .tag{display:inline-block;font-size:10px;color:var(--danger);border:1px solid var(--danger);border-radius:3px;padding:0 4px;margin-bottom:4px}
</style>
</head>
<body data-view="desktop">
<header class="toolbar">
<h1>WF — US-002, US-003 · Giỏ hàng (SCR-04)</h1>
<label>Khung nhìn
<select id="view"><option value="desktop">Desktop ≥1024</option><option value="mobile">Mobile &lt;768</option></select>
</label>
<label>Trạng thái
<select id="state">
<option value="default">Mặc định (có dữ liệu)</option>
<option value="loading">Đang tải</option>
<option value="empty-none">Rỗng — Giỏ hàng trống</option>
<option value="error">Lỗi tải dữ liệu</option>
</select>
</label>
<span class="legend">Nhãn đen = C-id/F-id trong SRS §2.A.3.1 · khung đứt xám = vùng Zn (WF §3.1.1) · khung đứt đỏ = chỉ có ở prototype, chưa có trong SRS (xem WF §5) · hành vi xem SRS, không suy từ hình</span>
</header>
<main>
<section class="screen" id="SCR-04">
<h2>SCR-04 · Giỏ hàng <small>US-002 (xem) + US-003 (sửa/xoá) · vào từ SCR-01 (icon giỏ hàng) · vai trò: ROLE-01 Guest, ROLE-02 Customer</small></h2>
<div class="body">
<div class="zone" data-zone="Z1" data-name="Header màn hình">
<span class="label" data-c="C01" data-key="cart.title">Giỏ hàng của bạn (5 sản phẩm)</span>
</div>
<div class="row m-stack" style="align-items:flex-start">
<!-- Z2 — danh sách nhóm seller -->
<div class="zone" data-zone="Z2" data-name="Danh sách nhóm seller" style="flex:2;min-width:0">
<div data-state="default">
<!-- Nhóm seller "Shop A" -->
<div class="card" style="margin-bottom:10px">
<div class="row between" data-c="C02"><b>Shop A</b></div>
<div class="row between" style="margin-top:8px">
<div class="row">
<div class="img" data-c="C03" style="width:64px;height:64px">ảnh</div>
<div>
<div data-c="C04">Áo thun basic</div>
<div class="hint" data-c="C05">Size M, Đỏ</div>
<div class="hint" data-c="C06" data-key="cart.item.price">100.000 ₫</div>
</div>
</div>
<div class="row">
<div class="proto-only d-hide" style="display:inline-block"><span class="tag">chỉ ở prototype</span><br>☐ chọn dòng</div>
<span class="row" data-c="F01">
<span class="btn btn-secondary">−</span><span class="input" style="min-width:40px;text-align:center;padding:6px">2</span><span class="btn btn-secondary">+</span>
</span>
<span class="btn btn-danger" data-c="C07" data-key="btn.delete">Xoá</span>
</div>
</div>
<div class="row between" style="margin-top:8px">
<div class="row">
<div class="img" style="width:64px;height:64px">ảnh</div>
<div>
<div>Quần jean slimfit</div>
<div class="hint">Size 30</div>
<div class="hint">50.000 ₫</div>
</div>
</div>
<div class="row">
<span class="row"><span class="btn btn-secondary">−</span><span class="input" style="min-width:40px;text-align:center;padding:6px">1</span><span class="btn btn-secondary">+</span></span>
<span class="btn btn-danger" data-key="btn.delete">Xoá</span>
</div>
</div>
<div class="row right" style="margin-top:8px;border-top:1px dashed var(--line);padding-top:6px">
<span data-c="C08">Tạm tính: 250.000 ₫</span>
</div>
</div>
<!-- Nhóm seller "Shop B" -->
<div class="card">
<div class="row between"><b>Shop B</b></div>
<div class="row between" style="margin-top:8px">
<div class="row">
<div class="img" style="width:64px;height:64px">ảnh</div>
<div>
<div>Balo du lịch</div>
<div class="hint">Đen</div>
<div class="hint">200.000 ₫</div>
<div class="proto-only" style="margin-top:4px"><span class="tag">chỉ ở prototype — WF §5 #2</span> badge "Sản phẩm đã hết hàng"</div>
</div>
</div>
<div class="row">
<span class="row"><span class="btn btn-secondary" aria-disabled="true">−</span><span class="input" style="min-width:40px;text-align:center;padding:6px">1</span><span class="btn btn-secondary" aria-disabled="true">+</span></span>
<span class="btn btn-danger" data-key="btn.delete">Xoá</span>
</div>
</div>
<div class="row right" style="margin-top:8px;border-top:1px dashed var(--line);padding-top:6px">
<span>Tạm tính: 200.000 ₫</span>
</div>
</div>
</div>
<div data-state="loading"><div class="skeleton"></div><div class="skeleton" style="width:85%"></div><div class="skeleton" style="width:92%"></div></div>
<div data-state="empty-none">
<div class="state">
<div class="img illus">minh hoạ</div>
<span data-key="cart.empty.title">Giỏ hàng trống</span>
<span class="btn btn-primary" data-c="C10" data-key="cart.empty.cta">Tiếp tục mua sắm</span>
</div>
</div>
<div data-state="error">
<div class="state error" data-c="C12">
<span data-key="cart.error.load">Không tải được dữ liệu, thử lại.</span>
<span class="btn btn-secondary" data-c="C13" data-key="btn.retry">Thử lại</span>
</div>
</div>
</div>
<!-- Z3 — tổng kết -->
<div class="zone m-hide" data-zone="Z3" data-name="Tổng kết (sidebar desktop / footer mobile)" style="flex:1;min-width:220px" data-state-visible-in="default">
<div class="proto-only" style="margin-bottom:8px"><span class="tag">chỉ ở prototype — WF §5 #1</span> "Tổng số lượng đã chọn" (phụ thuộc checkbox chọn dòng, chưa có trong SRS)</div>
<div class="label" data-c="C09">Tổng cộng: 585.000 ₫</div>
<div class="btn btn-primary" data-c="C11" data-key="btn.checkout" style="width:100%;text-align:center;margin-top:10px">Tiến hành Checkout</div>
</div>
</div>
<!-- Footer tổng kết cố định đáy — chỉ hiện ở mobile -->
<div class="zone d-hide" data-zone="Z3" data-name="Tổng kết (footer mobile)" style="position:sticky;bottom:0;background:#fff">
<div class="row between">
<span class="label">Tổng cộng: 585.000 ₫</span>
<span class="btn btn-primary" style="text-align:center">Tiến hành Checkout</span>
</div>
</div>
</div>
</section>
</main>
<footer class="note">Wireframe low-fi · bố cục và trạng thái · không phải thiết kế visual · nguồn hành vi: SRS_US002-003_v1.0.md · nguồn bố cục: e-commerce/docs/sections/07-giao-dien.md §7.1.1 (chế độ 🎨) · nguồn quy ước: UICONV_e-commerce_v1.0.md</footer>
<script>
(function(){
var view=document.getElementById('view'),state=document.getElementById('state');
function apply(){
document.body.setAttribute('data-view',view.value);
var s=state.value;
document.querySelectorAll('[data-state]').forEach(function(el){
var on=el.getAttribute('data-state')===s;
el.style.display=on?(el.tagName==='SPAN'?'inline':'block'):'none';
});
}
view.addEventListener('change',apply);state.addEventListener('change',apply);apply();
})();
</script>
</body>
</html>