feat: 순서변경 ▲/▼ 버튼 추가 (터치 지원) + 단가표 테이블 스크롤 수정

- ReorderButtons 공통 컴포넌트 신규 생성 (molecules)
- 패턴B(리스트): RankManagement, TitleManagement, CategoryManagement
- 패턴A(테이블): ProcessDetail, ProcessForm, ChecklistDetail
- 패턴C(컴포넌트): DraggableSection, DraggableField, HierarchyTab
- 모바일: GripVertical 숨김, ▲/▼ 버튼만 표시
- 데스크톱: GripVertical + ▲/▼ 버튼 모두 표시
- 단가표 단가정보 테이블 overflow-hidden → overflow-x-auto + min-w 적용
This commit is contained in:
유병철
2026-02-25 14:28:49 +09:00
parent 4dc0644f8d
commit 0b41b9f813
21 changed files with 619 additions and 363 deletions

View File

@@ -7,6 +7,7 @@ import {
Edit,
Unlink
} from 'lucide-react';
import { ReorderButtons } from '@/components/molecules';
// 입력방식 옵션 (ItemMasterDataManagement에서 사용하는 상수)
const INPUT_TYPE_OPTIONS = [
@@ -25,9 +26,11 @@ interface DraggableFieldProps {
moveField: (dragFieldId: number, hoverFieldId: number) => void;
onDelete: () => void;
onEdit?: () => void;
prevFieldId?: number;
nextFieldId?: number;
}
export function DraggableField({ field, index, moveField, onDelete, onEdit }: DraggableFieldProps) {
export function DraggableField({ field, index, moveField, onDelete, onEdit, prevFieldId, nextFieldId }: DraggableFieldProps) {
const [isDragging, setIsDragging] = useState(false);
const handleDragStart = (e: React.DragEvent) => {
@@ -79,7 +82,14 @@ export function DraggableField({ field, index, moveField, onDelete, onEdit }: Dr
>
<div className="flex-1">
<div className="flex items-center gap-2">
<GripVertical className="h-4 w-4 text-gray-400" />
<GripVertical className="h-4 w-4 text-gray-400 hidden md:block" />
<ReorderButtons
onMoveUp={() => prevFieldId !== undefined && moveField(field.id, prevFieldId)}
onMoveDown={() => nextFieldId !== undefined && moveField(field.id, nextFieldId)}
isFirst={prevFieldId === undefined}
isLast={nextFieldId === undefined}
size="xs"
/>
<span className="text-sm">{field.field_name}</span>
<Badge variant="outline" className="text-xs">
{INPUT_TYPE_OPTIONS.find(t => t.value === field.field_type)?.label || field.field_type}

View File

@@ -10,10 +10,12 @@ import {
X,
Unlink
} from 'lucide-react';
import { ReorderButtons } from '@/components/molecules';
interface DraggableSectionProps {
section: ItemSection;
index: number;
totalSections: number;
moveSection: (dragIndex: number, hoverIndex: number) => void;
onDelete: () => void;
onEditTitle: (id: number, title: string) => void;
@@ -28,6 +30,7 @@ interface DraggableSectionProps {
export function DraggableSection({
section,
index,
totalSections,
moveSection,
onDelete,
onEditTitle,
@@ -87,7 +90,14 @@ export function DraggableSection({
<div className="bg-blue-50 border-b p-3">
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2 flex-1 min-w-0">
<GripVertical className="h-4 w-4 text-gray-400" style={{ cursor: 'move' }} />
<GripVertical className="h-4 w-4 text-gray-400 hidden md:block" style={{ cursor: 'move' }} />
<ReorderButtons
onMoveUp={() => moveSection(index, index - 1)}
onMoveDown={() => moveSection(index, index + 1)}
isFirst={index === 0}
isLast={index === totalSections - 1}
size="xs"
/>
<FileText className="h-4 w-4 text-blue-600" />
{editingSectionId === section.id ? (
<div className="flex items-center gap-2 flex-1">

View File

@@ -380,6 +380,7 @@ export function HierarchyTab({
key={`section-${section.id}-${index}`}
section={section}
index={index}
totalSections={selectedPage.sections.length}
moveSection={(dragIndex, hoverIndex) => {
moveSection(dragIndex, hoverIndex);
}}
@@ -469,7 +470,7 @@ export function HierarchyTab({
) : (
section.fields
.sort((a, b) => (a.order_no ?? 0) - (b.order_no ?? 0))
.map((field, fieldIndex) => (
.map((field, fieldIndex, sortedFields) => (
<DraggableField
key={`${section.id}-${field.id}-${fieldIndex}`}
field={field}
@@ -479,6 +480,8 @@ export function HierarchyTab({
setConfirmAction({ type: 'unlinkField', pageId: String(selectedPage.id), sectionId: String(section.id), fieldId: String(field.id) });
}}
onEdit={() => handleEditField(String(section.id), field)}
prevFieldId={fieldIndex > 0 ? sortedFields[fieldIndex - 1].id : undefined}
nextFieldId={fieldIndex < sortedFields.length - 1 ? sortedFields[fieldIndex + 1].id : undefined}
/>
))
)}