feat(orders/admin): 발주 리스트 30초 자동 갱신 + 본인 락은 초록 ✏️ "내가 수정 중"
Deploy momo-erp / deploy (push) Successful in 1m56s

- load() 를 30초 setInterval 로 주기적 호출 → 누가 새로 락을 잡았는지 실시간 반영
- 카드/테이블 row 모두:
  • 본인 락 (EDITING_BY === myUserId): 초록 배경 + ✏️ "내가 수정 중"
  • 다른 사람 락: 빨강 배경 + 🔒 보유자명 (기존)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
chpark
2026-05-20 23:22:14 +09:00
parent 9b36ae64a5
commit 585b7d4577
+19 -3
View File
@@ -148,6 +148,12 @@ export default function AdminOrdersPage() {
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => { load(); }, [load]);
// 발주 리스트 30초마다 자동 갱신 — 락 상태(누가 수정 중) 실시간 반영
useEffect(() => {
const id = setInterval(() => { load(); }, 30000);
return () => clearInterval(id);
}, [load]);
const reloadDetail = useCallback(async () => {
if (!activeId) { setDetail(null); return; }
const res = await fetch("/api/m/orders/detail", {
@@ -427,12 +433,13 @@ export default function AdminOrdersPage() {
{orders.map((o) => {
const checked = selected.has(o.OBJID);
const active = o.OBJID === activeId;
const lockedByMeRow = !!o.EDITING_BY && o.EDITING_BY === myUserId;
const lockedByOtherUser = !!o.EDITING_BY && o.EDITING_BY !== myUserId;
return (
<div
key={o.OBJID}
onClick={() => setActiveId(o.OBJID)}
className={`border rounded-lg p-3 cursor-pointer transition ${active ? "border-emerald-500 bg-emerald-50/60 shadow-sm" : lockedByOtherUser ? "border-rose-200 bg-rose-50/40" : "border-slate-200 bg-white hover:bg-slate-50"}`}
className={`border rounded-lg p-3 cursor-pointer transition ${active ? "border-emerald-500 bg-emerald-50/60 shadow-sm" : lockedByMeRow ? "border-emerald-300 bg-emerald-50/30" : lockedByOtherUser ? "border-rose-200 bg-rose-50/40" : "border-slate-200 bg-white hover:bg-slate-50"}`}
>
<div className="flex items-center justify-between gap-2 mb-1.5">
<div className="flex items-center gap-2 min-w-0">
@@ -445,6 +452,9 @@ export default function AdminOrdersPage() {
className="accent-emerald-600 disabled:opacity-30"
/>
<span className="font-bold text-sm text-slate-800 truncate">{o.ORDER_NO}</span>
{lockedByMeRow && (
<span className="text-[10px] font-bold text-emerald-700" title="내가 수정 중"></span>
)}
{lockedByOtherUser && (
<span className="text-[10px] font-bold text-rose-700" title={`${o.EDITING_BY_NAME} 수정 중`}>🔒</span>
)}
@@ -455,6 +465,9 @@ export default function AdminOrdersPage() {
</div>
<div className="text-[11px] text-slate-500">{o.ORDER_DATE}</div>
<div className="text-xs text-slate-700 truncate" title={o.COMPANY_NAME}>{o.COMPANY_NAME}</div>
{lockedByMeRow && (
<div className="text-[10px] font-bold text-emerald-700 mt-0.5"> </div>
)}
{lockedByOtherUser && (
<div className="text-[10px] font-bold text-rose-700 mt-0.5">🔒 {o.EDITING_BY_NAME} </div>
)}
@@ -486,13 +499,14 @@ export default function AdminOrdersPage() {
) : orders.map((o) => {
const checked = selected.has(o.OBJID);
const active = o.OBJID === activeId;
const lockedByMeRow = !!o.EDITING_BY && o.EDITING_BY === myUserId;
const lockedByOtherUser = !!o.EDITING_BY && o.EDITING_BY !== myUserId;
return (
<tr
key={o.OBJID}
onClick={() => setActiveId(o.OBJID)}
className={`border-t border-slate-100 cursor-pointer ${active ? "bg-emerald-50/60" : lockedByOtherUser ? "bg-rose-50/30 hover:bg-rose-50/60" : "hover:bg-slate-50"}`}
title={lockedByOtherUser ? `🔒 ${o.EDITING_BY_NAME} 님이 수정 중` : ""}
className={`border-t border-slate-100 cursor-pointer ${active ? "bg-emerald-50/60" : lockedByMeRow ? "bg-emerald-50/30 hover:bg-emerald-50/50" : lockedByOtherUser ? "bg-rose-50/30 hover:bg-rose-50/60" : "hover:bg-slate-50"}`}
title={lockedByMeRow ? "✏️ 내가 수정 중" : lockedByOtherUser ? `🔒 ${o.EDITING_BY_NAME} 님이 수정 중` : ""}
>
<td className="px-2 py-2 text-center" onClick={(e) => e.stopPropagation()}>
<input
@@ -505,12 +519,14 @@ export default function AdminOrdersPage() {
/>
</td>
<td className="px-1.5 py-1.5 font-semibold text-slate-800 whitespace-nowrap text-[11px]">
{lockedByMeRow && <span className="mr-1 text-emerald-700" title="내가 수정 중"></span>}
{lockedByOtherUser && <span className="mr-1" title={`${o.EDITING_BY_NAME} 수정 중`}>🔒</span>}
{o.ORDER_NO}
</td>
<td className="px-1.5 py-1.5 text-slate-600 whitespace-nowrap text-[11px]">{o.ORDER_DATE}</td>
<td className="px-1.5 py-1.5 truncate max-w-[100px] text-[11px]" title={o.COMPANY_NAME}>
{o.COMPANY_NAME}
{lockedByMeRow && <div className="text-[9px] font-bold text-emerald-700"> </div>}
{lockedByOtherUser && <div className="text-[9px] font-bold text-rose-600">🔒 {o.EDITING_BY_NAME}</div>}
</td>
<td className="px-1.5 py-1.5 text-right tabular-nums font-semibold whitespace-nowrap text-[11px]">{fmt(o.TOTAL_AMOUNT)}</td>