2d5b94a026
Deploy momo-erp / deploy (push) Successful in 2m36s
- 사용자 수정 폼에 '비밀번호 변경' 입력란 추가 (빈 칸이면 기존 유지) - /api/admin/users/save update 분기: password 값이 있으면 AES 암호화 후 user_password 갱신
163 lines
9.6 KiB
TypeScript
163 lines
9.6 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect, Suspense } from "react";
|
|
import { useSearchParams } from "next/navigation";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import Swal from "sweetalert2";
|
|
|
|
function UserForm() {
|
|
const searchParams = useSearchParams();
|
|
const userId = searchParams.get("userId");
|
|
const isNew = !userId;
|
|
const [form, setForm] = useState<Record<string, string>>({});
|
|
const [depts, setDepts] = useState<{ DEPT_CODE: string; DEPT_NAME: string }[]>([]);
|
|
const [whs, setWhs] = useState<{ OBJID: string; WH_NAME: string; WH_CODE: string }[]>([]);
|
|
const [branches, setBranches] = useState<{ CODE: string; NAME: string }[]>([]);
|
|
const [loading, setLoading] = useState(false);
|
|
const set = (k: string, v: string) => setForm((p) => ({ ...p, [k]: v }));
|
|
|
|
useEffect(() => {
|
|
fetch("/api/admin/dept", { method: "POST", headers: { "Content-Type": "application/json" }, body: "{}" })
|
|
.then((r) => r.json()).then((d) => setDepts(d.RESULTLIST || [])).catch(() => {});
|
|
fetch("/api/m/warehouses/list", { method: "POST" })
|
|
.then((r) => r.json()).then((d) => setWhs(d.RESULTLIST || [])).catch(() => {});
|
|
fetch("/api/m/admin/statement-branches/list", { method: "POST" })
|
|
.then((r) => r.json()).then((d) => setBranches(d.RESULTLIST || [])).catch(() => {});
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (userId) {
|
|
fetch("/api/admin/users/detail", {
|
|
method: "POST", headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ userId }),
|
|
}).then((r) => r.json()).then((d) => { if (d.success && d.data) setForm(d.data); }).catch(() => {});
|
|
}
|
|
}, [userId]);
|
|
|
|
const handleSave = async () => {
|
|
if (!form.user_id) { Swal.fire("알림", "사용자 ID를 입력하세요.", "warning"); return; }
|
|
if (!form.user_name) { Swal.fire("알림", "이름을 입력하세요.", "warning"); return; }
|
|
setLoading(true);
|
|
try {
|
|
const res = await fetch("/api/admin/users/save", {
|
|
method: "POST", headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ ...form, actionType: isNew ? "regist" : "update" }),
|
|
});
|
|
const data = await res.json();
|
|
if (data.success) {
|
|
await Swal.fire({ icon: "success", title: data.message || "저장되었습니다.", timer: 1500, showConfirmButton: false });
|
|
if (window.opener) try { window.opener.location.reload(); } catch {}
|
|
if (isNew) window.close();
|
|
} else { Swal.fire("오류", data.message || "저장 실패", "error"); }
|
|
} catch { Swal.fire("오류", "서버 오류", "error"); }
|
|
finally { setLoading(false); }
|
|
};
|
|
|
|
return (
|
|
<div className="p-3 bg-white min-h-screen text-[12px]">
|
|
<h2 className="text-sm font-bold text-gray-800 mb-2 border-b pb-1.5">
|
|
{isNew ? "사용자 등록" : `사용자 수정 (${userId})`}
|
|
</h2>
|
|
<div className="grid grid-cols-2 gap-x-3 gap-y-1.5">
|
|
<div><label className="block text-[11px] font-medium text-gray-500 mb-0.5">사용자 ID <span className="text-red-400">*</span></label>
|
|
<Input className="h-8" value={form.user_id || ""} onChange={(e) => set("user_id", e.target.value)} readOnly={!isNew} /></div>
|
|
<div><label className="block text-[11px] font-medium text-gray-500 mb-0.5">이름/업체명 <span className="text-red-400">*</span></label>
|
|
<Input className="h-8" value={form.user_name || ""} onChange={(e) => set("user_name", e.target.value)} /></div>
|
|
<div><label className="block text-[11px] font-medium text-gray-500 mb-0.5">부서</label>
|
|
<select value={form.dept_code || ""}
|
|
onChange={(e) => { set("dept_code", e.target.value); const d = depts.find((x) => x.DEPT_CODE === e.target.value); if (d) set("dept_name", d.DEPT_NAME); }}
|
|
className="h-8 w-full rounded border border-gray-300 bg-white px-2 text-[12px]">
|
|
<option value="">선택</option>
|
|
{depts.map((d) => <option key={d.DEPT_CODE} value={d.DEPT_CODE}>{d.DEPT_NAME}</option>)}
|
|
</select></div>
|
|
<div><label className="block text-[11px] font-medium text-gray-500 mb-0.5">유형</label>
|
|
<select value={form.user_type || ""} onChange={(e) => set("user_type", e.target.value)}
|
|
className="h-8 w-full rounded border border-gray-300 bg-white px-2 text-[12px]">
|
|
<option value="">선택</option>
|
|
<option value="A">관리자</option>
|
|
<option value="U">사용자</option>
|
|
</select></div>
|
|
<div><label className="block text-[11px] font-medium text-gray-500 mb-0.5">직급/직책</label>
|
|
<Input className="h-8" value={form.position_name || ""} onChange={(e) => set("position_name", e.target.value)} /></div>
|
|
<div><label className="block text-[11px] font-medium text-gray-500 mb-0.5">이메일</label>
|
|
<Input className="h-8" type="email" value={form.email || ""} onChange={(e) => set("email", e.target.value)} /></div>
|
|
<div><label className="block text-[11px] font-medium text-gray-500 mb-0.5">휴대폰</label>
|
|
<Input className="h-8" value={form.cell_phone || ""} onChange={(e) => set("cell_phone", e.target.value)} /></div>
|
|
<div><label className="block text-[11px] font-medium text-gray-500 mb-0.5">전화번호</label>
|
|
<Input className="h-8" value={form.tel || ""} onChange={(e) => set("tel", e.target.value)} /></div>
|
|
<div><label className="block text-[11px] font-medium text-gray-500 mb-0.5">대표자</label>
|
|
<Input className="h-8" value={form.ceo_name || ""} onChange={(e) => set("ceo_name", e.target.value)} /></div>
|
|
<div><label className="block text-[11px] font-medium text-gray-500 mb-0.5">사업자등록번호</label>
|
|
<Input className="h-8" value={form.biz_no || ""} onChange={(e) => set("biz_no", e.target.value)} /></div>
|
|
<div className="col-span-2"><label className="block text-[11px] font-medium text-gray-500 mb-0.5">주소</label>
|
|
<Input className="h-8" value={form.address || ""} onChange={(e) => set("address", e.target.value)} /></div>
|
|
|
|
{isNew ? (
|
|
<div className="col-span-2"><label className="block text-[11px] font-medium text-gray-500 mb-0.5">초기 비밀번호</label>
|
|
<Input className="h-8" type="password" value={form.password || ""} onChange={(e) => set("password", e.target.value)} placeholder="비밀번호 입력 (미입력 시 1)" /></div>
|
|
) : (
|
|
<div className="col-span-2"><label className="block text-[11px] font-medium text-gray-500 mb-0.5">
|
|
비밀번호 변경 <span className="text-gray-400 font-normal">(빈 칸이면 안 바꿈)</span>
|
|
</label>
|
|
<Input className="h-8" type="password" value={form.password || ""} onChange={(e) => set("password", e.target.value)} placeholder="새 비밀번호 입력" /></div>
|
|
)}
|
|
</div>
|
|
|
|
{!isNew && (
|
|
<div className="mt-2 pt-2 border-t grid grid-cols-2 gap-x-3 gap-y-1.5">
|
|
<div>
|
|
<label className="block text-[11px] font-medium text-gray-500 mb-0.5">출고 기준 창고 <span className="text-gray-400 font-normal">(미지정 = 기본)</span></label>
|
|
<select
|
|
value={form.default_wh_objid || ""}
|
|
onChange={(e) => set("default_wh_objid", e.target.value)}
|
|
className="h-8 w-full rounded border border-gray-300 bg-white px-2 text-[12px]"
|
|
>
|
|
<option value="">미지정 (기본 창고)</option>
|
|
{whs.map((w) => (
|
|
<option key={w.OBJID} value={w.OBJID}>{w.WH_NAME} ({w.WH_CODE})</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label className="block text-[11px] font-medium text-gray-500 mb-0.5">기준 거래명세서 <span className="text-gray-400 font-normal">(공급자 정보)</span></label>
|
|
<select
|
|
value={form.statement_branch || "HQ"}
|
|
onChange={(e) => set("statement_branch", e.target.value)}
|
|
className="h-8 w-full rounded border border-gray-300 bg-white px-2 text-[12px]"
|
|
>
|
|
{branches.length === 0 ? (
|
|
<option value="HQ">본사</option>
|
|
) : branches.map((b) => (
|
|
<option key={b.CODE} value={b.CODE}>{b.NAME} ({b.CODE})</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label className="block text-[11px] font-medium text-gray-500 mb-0.5">특수 권한</label>
|
|
<div className="flex gap-2 h-8 items-center">
|
|
<label className="inline-flex items-center gap-1 px-2 h-8 border rounded text-[11px] cursor-pointer hover:bg-gray-50">
|
|
<input type="checkbox" checked={form.unlimited_qty === "Y"} onChange={(e) => set("unlimited_qty", e.target.checked ? "Y" : "N")} className="w-3.5 h-3.5" />
|
|
발주한도 무시
|
|
</label>
|
|
<label className="inline-flex items-center gap-1 px-2 h-8 border rounded text-[11px] cursor-pointer hover:bg-gray-50">
|
|
<input type="checkbox" checked={form.view_hidden === "Y"} onChange={(e) => set("view_hidden", e.target.checked ? "Y" : "N")} className="w-3.5 h-3.5" />
|
|
숨김품목 보기
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex justify-end gap-2 mt-3 pt-2 border-t">
|
|
<Button onClick={handleSave} disabled={loading}>{loading ? "저장 중..." : "저장"}</Button>
|
|
<Button variant="secondary" onClick={() => window.close()}>닫기</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default function Page() {
|
|
return <Suspense fallback={<div className="p-8 text-center text-gray-400">로딩 중...</div>}><UserForm /></Suspense>;
|
|
}
|