You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
113 lines
4.3 KiB
JavaScript
113 lines
4.3 KiB
JavaScript
/* 아리 — 결재함 (승인 큐) 위젯
|
|
아리가 미리 해둔 일 = 되돌리기만, 위험한 일 = 승인 대기.
|
|
dash-v2.jsx에서 <ApprovalCard I={I} autonomy={...} showLog={...} /> 로 사용. */
|
|
(function () {
|
|
const { useState, useEffect } = React;
|
|
const A = window.AriApprovals;
|
|
|
|
/* autonomy: "approve"(승인 우선) | "mixed"(혼합) | "auto"(완전 자율) */
|
|
function initStatus(autonomy) {
|
|
const m = {};
|
|
A.items.forEach((it) => {
|
|
if (autonomy === "auto") m[it.id] = "done";
|
|
else if (autonomy === "mixed") m[it.id] = it.risk === "low" ? "done" : "pending";
|
|
else m[it.id] = "pending";
|
|
});
|
|
return m;
|
|
}
|
|
|
|
function ApprovalCard({ I, autonomy, showLog }) {
|
|
const [st, setSt] = useState(() => initStatus(autonomy));
|
|
useEffect(() => { setSt(initStatus(autonomy)); }, [autonomy]);
|
|
|
|
const pending = A.items.filter((it) => st[it.id] === "pending");
|
|
const done = A.items.filter((it) => st[it.id] === "done");
|
|
|
|
const approve = (id) => setSt((s) => ({ ...s, [id]: "done" }));
|
|
const undo = (id) => setSt((s) => ({ ...s, [id]: "pending" }));
|
|
const approveAll = () => setSt((s) => {
|
|
const n = { ...s };
|
|
pending.forEach((it) => { n[it.id] = "done"; });
|
|
return n;
|
|
});
|
|
|
|
return (
|
|
<section className="card appr sp2">
|
|
<div className="ch">
|
|
<div className="ico lime"><I d="spark" /></div>
|
|
<div className="htext">
|
|
<h3>아리 결재함</h3>
|
|
<div className="sub">확인만 하면 끝 · 오늘 {A.savedToday} 아껴드렸어요</div>
|
|
</div>
|
|
{pending.length > 0
|
|
? <span className="count warm">대기 {pending.length}건</span>
|
|
: <span className="count">모두 처리됨</span>}
|
|
</div>
|
|
|
|
{/* ----- 승인 대기 ----- */}
|
|
{pending.length === 0 ? (
|
|
<div className="appr-empty">
|
|
<span className="ae-tick"><I d="tick" w="3" /></span>
|
|
결재할 게 없어요 — 아리가 알아서 하고 있어요. 아래에서 언제든 되돌릴 수 있어요.
|
|
</div>
|
|
) : (
|
|
<div className="appr-list">
|
|
{pending.map((it) => (
|
|
<div className="ap-row" key={it.id}>
|
|
<div className="ap-ic" style={{ "--tone": `var(--${it.tone})` }}><I d={it.icon} /></div>
|
|
<div className="ap-body">
|
|
<div className="ap-title">{it.title}</div>
|
|
<div className="ap-detail">{it.detail}</div>
|
|
</div>
|
|
<div className="ap-acts">
|
|
<button className="ap-ok" onClick={() => approve(it.id)}>
|
|
<I d="tick" w="3" />{it.cta || "승인"}
|
|
</button>
|
|
<button className="ap-alt">{it.alt || "나중에"}</button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
{pending.length >= 2 && (
|
|
<button className="ap-all" onClick={approveAll}>
|
|
<I d="tick" w="3" />모두 승인 ({pending.length}건)
|
|
</button>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{/* ----- 자동 처리됨 (되돌리기) ----- */}
|
|
{done.length > 0 && (
|
|
<div className="appr-done">
|
|
<div className="ad-label">처리됨 — 탭 한 번으로 되돌릴 수 있어요</div>
|
|
{done.map((it) => (
|
|
<div className="ad-row" key={it.id}>
|
|
<span className="ad-tick"><I d="tick" w="3" /></span>
|
|
<span className="ad-title">{it.title}</span>
|
|
<span className="ad-time">{it.time}</span>
|
|
<button className="ad-undo" onClick={() => undo(it.id)}>
|
|
<I d="swap" />{it.undoLabel || "되돌리기"}
|
|
</button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* ----- 활동 로그 ----- */}
|
|
{showLog && (
|
|
<div className="appr-log">
|
|
<div className="ad-label">밤사이 조용히 한 일 {A.autoCountNight}건</div>
|
|
{A.log.map((l, i) => (
|
|
<div className="al-row" key={i}>
|
|
<span className="al-time">{l.time}</span>
|
|
<span className="al-text">{l.text}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</section>
|
|
);
|
|
}
|
|
|
|
window.ApprovalCard = ApprovalCard;
|
|
})();
|