feat: 다중 도메인 UI 개선 및 컴포넌트 리팩토링

- 게시판, HR, 설정, 차량관리, 건설, 견적 등 전반적 UI 개선
- FormField, TabChip, Select 등 공통 컴포넌트 개선
- 가격배분 edit 페이지 제거 및 상세 페이지 통합
- 체크리스트, 근태, 급여, 권한 관리 등 폼 개선

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-02-25 22:30:06 +09:00
parent 1675bcbedf
commit 8f9507a665
86 changed files with 856 additions and 685 deletions

View File

@@ -10,7 +10,7 @@
import { useState, useEffect, useCallback } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { ArrowLeft, FileText, CheckCircle2, Edit3, Save, X } from 'lucide-react';
import { ArrowLeft, FileText, CheckCircle2, Edit, Save, X } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { useMenuStore } from '@/stores/menuStore';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
@@ -135,7 +135,7 @@ export function PriceDistributionDetail({ id, mode: propMode }: Props) {
const result = await updatePriceDistribution(id, formData);
if (result.success) {
toast.success('저장되었습니다.');
router.push(`/master-data/price-distribution/${id}`);
router.push(`/master-data/price-distribution/${id}?mode=view`);
} else {
toast.error(result.error || '저장에 실패했습니다.');
}
@@ -164,12 +164,12 @@ export function PriceDistributionDetail({ id, mode: propMode }: Props) {
// 수정 모드 전환
const handleEditMode = () => {
router.push(`/master-data/price-distribution/${id}/edit`);
router.push(`/master-data/price-distribution/${id}?mode=edit`);
};
// 취소
const handleCancel = () => {
router.push(`/master-data/price-distribution/${id}`);
router.push(`/master-data/price-distribution/${id}?mode=view`);
};
// 목록으로
@@ -352,25 +352,27 @@ export function PriceDistributionDetail({ id, mode: propMode }: Props) {
{/* 단가 목록 테이블 */}
<Card className="mt-4">
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle className="text-base"> </CardTitle>
<div className="flex items-center gap-3">
<Select value={gradeFilter} onValueChange={setGradeFilter}>
<SelectTrigger className="h-8 min-w-[120px] w-auto text-sm">
<SelectValue placeholder="등급" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all"></SelectItem>
{TRADE_GRADE_OPTIONS.map((opt) => (
<SelectItem key={opt.value} value={opt.value}>
{opt.label}
</SelectItem>
))}
</SelectContent>
</Select>
<span className="text-sm text-muted-foreground">
{detail.items.length}
</span>
<CardHeader>
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
<CardTitle className="text-base"> </CardTitle>
<div className="flex items-center gap-3">
<Select value={gradeFilter} onValueChange={setGradeFilter}>
<SelectTrigger className="h-8 min-w-[120px] w-auto text-sm">
<SelectValue placeholder="등급" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all"></SelectItem>
{TRADE_GRADE_OPTIONS.map((opt) => (
<SelectItem key={opt.value} value={opt.value}>
{opt.label}
</SelectItem>
))}
</SelectContent>
</Select>
<span className="text-sm text-muted-foreground whitespace-nowrap">
{detail.items.length}
</span>
</div>
</div>
</CardHeader>
<CardContent className="p-0">
@@ -492,7 +494,7 @@ export function PriceDistributionDetail({ id, mode: propMode }: Props) {
className="md:size-default"
disabled={detail.status === 'finalized'}
>
<Edit3 className="w-4 h-4 md:mr-2" />
<Edit className="w-4 h-4 md:mr-2" />
<span className="hidden md:inline"></span>
</Button>
)}

View File

@@ -110,7 +110,7 @@ export function PriceDistributionList() {
if (result.success && result.data) {
toast.success('단가배포가 등록되었습니다.');
setShowRegisterDialog(false);
router.push(`/master-data/price-distribution/${result.data.id}`);
router.push(`/master-data/price-distribution/${result.data.id}?mode=view`);
} else {
toast.error(result.error || '등록에 실패했습니다.');
}
@@ -123,7 +123,7 @@ export function PriceDistributionList() {
// 행 클릭 → 상세
const handleRowClick = (item: PriceDistributionListItem) => {
router.push(`/master-data/price-distribution/${item.id}`);
router.push(`/master-data/price-distribution/${item.id}?mode=view`);
};
// 상태 필터 설정