Merge branch 'jskim-node' of http://39.117.244.52:3000/kjs/ERP-node into ycshin-node

This commit is contained in:
2026-03-05 09:03:24 +09:00
79 changed files with 11688 additions and 666 deletions

View File

@@ -135,6 +135,7 @@ export function ScreenGroupTreeView({
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const [deletingGroup, setDeletingGroup] = useState<ScreenGroup | null>(null);
const [deleteScreensWithGroup, setDeleteScreensWithGroup] = useState(false); // 화면도 함께 삭제 체크박스
const [deleteNumberingRules, setDeleteNumberingRules] = useState(false); // 채번 규칙도 함께 삭제 체크박스
const [isDeleting, setIsDeleting] = useState(false); // 삭제 진행 중 상태
const [deleteProgress, setDeleteProgress] = useState({ current: 0, total: 0, message: "" }); // 삭제 진행 상태
@@ -439,7 +440,8 @@ export function ScreenGroupTreeView({
const handleDeleteGroup = (group: ScreenGroup, e?: React.MouseEvent) => {
e?.stopPropagation();
setDeletingGroup(group);
setDeleteScreensWithGroup(false); // 기본값: 화면 삭제 안함
setDeleteScreensWithGroup(false);
setDeleteNumberingRules(false);
setIsDeleteDialogOpen(true);
};
@@ -572,11 +574,17 @@ export function ScreenGroupTreeView({
// 최종적으로 대상 그룹 삭제
currentStep++;
setDeleteProgress({ current: currentStep, total: totalSteps, message: "그룹 삭제 완료 중..." });
const response = await deleteScreenGroup(deletingGroup.id);
const isRootGroup = !deletingGroup.parent_group_id;
const response = await deleteScreenGroup(deletingGroup.id, {
deleteNumberingRules: isRootGroup && deleteNumberingRules,
});
if (response.success) {
const messages = [];
if (deleteScreensWithGroup) messages.push(`화면 ${totalScreensToDelete}`);
if (isRootGroup && deleteNumberingRules) messages.push("채번 규칙");
toast.success(
deleteScreensWithGroup
? `그룹과 화면 ${totalScreensToDelete}개가 삭제되었습니다`
messages.length > 0
? `그룹과 ${messages.join(", ")}이(가) 삭제되었습니다`
: "그룹이 삭제되었습니다"
);
await loadGroupsData();
@@ -593,6 +601,7 @@ export function ScreenGroupTreeView({
setIsDeleteDialogOpen(false);
setDeletingGroup(null);
setDeleteScreensWithGroup(false);
setDeleteNumberingRules(false);
}
};
@@ -1012,7 +1021,7 @@ export function ScreenGroupTreeView({
const loadGroupsData = async () => {
try {
setLoading(true);
const response = await getScreenGroups({ size: 1000 }); // 모든 그룹 가져오기
const response = await getScreenGroups({ size: 1000, excludePop: true });
if (response.success && response.data) {
setGroups(response.data);
@@ -1479,7 +1488,7 @@ export function ScreenGroupTreeView({
</p>
<p className="mt-2 text-destructive/80">
{deleteScreensWithGroup
? "⚠️ 그룹에 속한 모든 화면, 플로우, 관련 데이터가 영구적으로 삭제됩니다. 이 작업은 되돌릴 수 없습니다."
? "그룹에 속한 모든 화면, 플로우, 관련 데이터가 영구적으로 삭제됩니다. 이 작업은 되돌릴 수 없습니다."
: "그룹에 속한 화면들은 미분류로 이동됩니다."
}
</p>
@@ -1520,6 +1529,43 @@ export function ScreenGroupTreeView({
</label>
</div>
)}
{/* 최상위 그룹일 때 채번 삭제 경고 */}
{deletingGroup && !deletingGroup.parent_group_id && (
<div className="space-y-3">
<div className="rounded-md border-2 border-destructive bg-destructive/5 p-4">
<div className="flex items-start gap-3">
<AlertTriangle className="mt-0.5 h-6 w-6 shrink-0 text-destructive" />
<div className="space-y-2">
<p className="text-sm font-bold text-destructive">
-
</p>
<p className="text-xs text-destructive/90 leading-relaxed">
.
<span className="font-bold underline"> </span>.
, .
</p>
</div>
</div>
</div>
<div className="flex items-center space-x-2 rounded-md border border-destructive/30 bg-destructive/5 p-3">
<input
type="checkbox"
id="deleteNumberingRules"
checked={deleteNumberingRules}
onChange={(e) => setDeleteNumberingRules(e.target.checked)}
className="h-4 w-4 rounded border-destructive text-destructive focus:ring-destructive"
/>
<label
htmlFor="deleteNumberingRules"
className="cursor-pointer text-sm font-semibold text-destructive"
>
()
</label>
</div>
</div>
)}
{/* 로딩 오버레이 */}
{isDeleting && (
@@ -1551,7 +1597,7 @@ export function ScreenGroupTreeView({
</AlertDialogCancel>
<AlertDialogAction
onClick={(e) => {
e.preventDefault(); // 자동 닫힘 방지
e.preventDefault();
confirmDeleteGroup();
}}
disabled={isDeleting}

View File

@@ -224,7 +224,7 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
const loadGroups = async () => {
try {
setLoadingGroups(true);
const response = await getScreenGroups();
const response = await getScreenGroups({ excludePop: true });
if (response.success && response.data) {
setGroups(response.data);
}