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

@@ -1,13 +1,13 @@
'use client';
import { useState, useCallback, useEffect, useTransition, useRef } from 'react';
import { useState, useCallback, useEffect, useTransition, useRef, useMemo } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { usePermission } from '@/hooks/usePermission';
import { format } from 'date-fns';
import { Trash2, Send, Save, Eye, Loader2 } from 'lucide-react';
import { Trash2, Send, Save, Eye } from 'lucide-react';
import { toast } from 'sonner';
import { DeleteConfirmDialog } from '@/components/ui/confirm-dialog';
import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
import { IntegratedDetailTemplate, type ActionItem } from '@/components/templates/IntegratedDetailTemplate';
import {
documentCreateConfig,
documentEditConfig,
@@ -24,7 +24,6 @@ import {
getEmployees,
} from './actions';
import { useAuthStore } from '@/stores/authStore';
import { Button } from '@/components/ui/button';
import { BasicInfoSection } from './BasicInfoSection';
import { ApprovalLineSection } from './ApprovalLineSection';
import { ReferenceSection } from './ReferenceSection';
@@ -546,54 +545,13 @@ export function DocumentCreate() {
? documentCopyConfig
: documentCreateConfig;
// 헤더 액션 버튼 렌더링
const renderHeaderActions = useCallback(() => {
return (
<div className="flex items-center gap-2">
<Button variant="outline" size="sm" onClick={handlePreview}>
<Eye className="w-4 h-4 mr-1" />
</Button>
{canDelete && (
<Button
variant="outline"
size="sm"
onClick={handleDelete}
disabled={isPending}
>
<Trash2 className="w-4 h-4 mr-1" />
</Button>
)}
<Button
variant="default"
size="sm"
onClick={handleSubmit}
disabled={isPending || !canCreate}
>
{isPending ? (
<Loader2 className="w-4 h-4 mr-1 animate-spin" />
) : (
<Send className="w-4 h-4 mr-1" />
)}
</Button>
<Button
variant="secondary"
size="sm"
onClick={handleSaveDraft}
disabled={isPending}
>
{isPending ? (
<Loader2 className="w-4 h-4 mr-1 animate-spin" />
) : (
<Save className="w-4 h-4 mr-1" />
)}
{isEditMode ? '저장' : '임시저장'}
</Button>
</div>
);
}, [handlePreview, handleDelete, handleSubmit, handleSaveDraft, isPending, isEditMode, canCreate, canDelete]);
// 헤더 액션 버튼 (config 배열 → 모바일 아이콘 패턴 자동 적용)
const headerActionItems = useMemo<ActionItem[]>(() => [
{ icon: Eye, label: '미리보기', onClick: handlePreview, variant: 'outline' },
{ icon: Trash2, label: '삭제', onClick: handleDelete, variant: 'outline', hidden: !canDelete, disabled: isPending },
{ icon: Send, label: '상신', onClick: handleSubmit, variant: 'outline', disabled: isPending || !canCreate, loading: isPending },
{ icon: Save, label: isEditMode ? '저장' : '임시저장', onClick: handleSaveDraft, variant: 'outline', disabled: isPending, loading: isPending },
], [handlePreview, handleDelete, handleSubmit, handleSaveDraft, isPending, isEditMode, canCreate, canDelete]);
// 폼 컨텐츠 렌더링
const renderFormContent = useCallback(() => {
@@ -622,7 +580,7 @@ export function DocumentCreate() {
isLoading={isLoadingDocument}
onBack={handleBack}
renderForm={renderFormContent}
headerActions={renderHeaderActions()}
headerActionItems={headerActionItems}
/>
{/* 미리보기 모달 */}