"use client";
import React, { useState, useEffect, useCallback } from "react";
import {
DndContext,
closestCenter,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
type DragEndEvent,
} from "@dnd-kit/core";
import {
arrayMove,
SortableContext,
sortableKeyboardCoordinates,
verticalListSortingStrategy,
useSortable,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { Plus, GripVertical, Settings, X, Check, ChevronsUpDown } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils";
import { apiClient } from "@/lib/api/client";
import type { ActionButtonConfig, ModalParamMapping, ColumnConfig } from "./types";
interface ScreenInfo {
screen_id: number;
screen_name: string;
screen_code: string;
}
// 정렬 가능한 버튼 아이템
const SortableButtonItem: React.FC<{
id: string;
button: ActionButtonConfig;
index: number;
onSettingsClick: () => void;
onRemove: () => void;
}> = ({ id, button, index, onSettingsClick, onRemove }) => {
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
const getVariantColor = (variant?: string) => {
switch (variant) {
case "destructive":
return "bg-destructive/10 text-destructive";
case "outline":
return "bg-background border";
case "ghost":
return "bg-muted/50";
case "secondary":
return "bg-secondary text-secondary-foreground";
default:
return "bg-primary/10 text-primary";
}
};
const getActionLabel = (action?: string) => {
switch (action) {
case "add":
return "추가";
case "edit":
return "수정";
case "delete":
return "삭제";
case "bulk-delete":
return "일괄삭제";
case "api":
return "API";
case "custom":
return "커스텀";
default:
return "추가";
}
};
return (
{/* 드래그 핸들 */}
{/* 버튼 정보 */}
{button.label || `버튼 ${index + 1}`}
{getActionLabel(button.action)}
{button.icon && (
{button.icon}
)}
{button.showCondition && button.showCondition !== "always" && (
{button.showCondition === "selected" ? "선택시만" : "미선택시만"}
)}
{/* 액션 버튼들 */}
);
};
interface ActionButtonConfigModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
actionButtons: ActionButtonConfig[];
displayColumns?: ColumnConfig[]; // 모달 파라미터 매핑용
onSave: (buttons: ActionButtonConfig[]) => void;
side: "left" | "right";
}
export const ActionButtonConfigModal: React.FC = ({
open,
onOpenChange,
actionButtons: initialButtons,
displayColumns = [],
onSave,
side,
}) => {
// 로컬 상태
const [buttons, setButtons] = useState([]);
// 버튼 세부설정 모달
const [detailModalOpen, setDetailModalOpen] = useState(false);
const [editingButtonIndex, setEditingButtonIndex] = useState(null);
const [editingButton, setEditingButton] = useState(null);
// 화면 목록
const [screens, setScreens] = useState([]);
const [screensLoading, setScreensLoading] = useState(false);
const [screenSelectOpen, setScreenSelectOpen] = useState(false);
// 드래그 센서
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: {
distance: 8,
},
}),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
})
);
// 초기값 설정
useEffect(() => {
if (open) {
setButtons(initialButtons || []);
}
}, [open, initialButtons]);
// 화면 목록 로드
const loadScreens = useCallback(async () => {
setScreensLoading(true);
try {
const response = await apiClient.get("/screen-management/screens?size=1000");
let screenList: any[] = [];
if (response.data?.success && Array.isArray(response.data?.data)) {
screenList = response.data.data;
} else if (Array.isArray(response.data?.data)) {
screenList = response.data.data;
}
const transformedScreens = screenList.map((s: any) => ({
screen_id: s.screenId ?? s.screen_id ?? s.id,
screen_name: s.screenName ?? s.screen_name ?? s.name ?? `화면 ${s.screenId || s.screen_id || s.id}`,
screen_code: s.screenCode ?? s.screen_code ?? s.code ?? "",
}));
setScreens(transformedScreens);
} catch (error) {
console.error("화면 목록 로드 실패:", error);
setScreens([]);
} finally {
setScreensLoading(false);
}
}, []);
useEffect(() => {
if (open) {
loadScreens();
}
}, [open, loadScreens]);
// 드래그 종료 핸들러
const handleDragEnd = (event: DragEndEvent) => {
const { active, over } = event;
if (over && active.id !== over.id) {
const oldIndex = buttons.findIndex((btn) => btn.id === active.id);
const newIndex = buttons.findIndex((btn) => btn.id === over.id);
if (oldIndex !== -1 && newIndex !== -1) {
setButtons(arrayMove(buttons, oldIndex, newIndex));
}
}
};
// 버튼 추가
const handleAddButton = () => {
const newButton: ActionButtonConfig = {
id: `btn-${Date.now()}`,
label: "새 버튼",
variant: "default",
action: "add",
showCondition: "always",
};
setButtons([...buttons, newButton]);
};
// 버튼 삭제
const handleRemoveButton = (index: number) => {
setButtons(buttons.filter((_, i) => i !== index));
};
// 버튼 업데이트
const handleUpdateButton = (index: number, updates: Partial) => {
const newButtons = [...buttons];
newButtons[index] = { ...newButtons[index], ...updates };
setButtons(newButtons);
};
// 버튼 세부설정 열기
const handleOpenDetailSettings = (index: number) => {
setEditingButtonIndex(index);
setEditingButton({ ...buttons[index] });
setDetailModalOpen(true);
};
// 버튼 세부설정 저장
const handleSaveDetailSettings = () => {
if (editingButtonIndex !== null && editingButton) {
handleUpdateButton(editingButtonIndex, editingButton);
}
setDetailModalOpen(false);
setEditingButtonIndex(null);
setEditingButton(null);
};
// 저장
const handleSave = () => {
onSave(buttons);
onOpenChange(false);
};
// 선택된 화면 정보
const getScreenInfo = (screenId?: number) => {
return screens.find((s) => s.screen_id === screenId);
};
return (
<>
{/* 버튼 세부설정 모달 */}
>
);
};
export default ActionButtonConfigModal;