580 lines
17 KiB
Markdown
580 lines
17 KiB
Markdown
|
|
# 공통 컴포넌트 가이드
|
||
|
|
|
||
|
|
이 문서는 SAM MES 시스템의 통일된 레이아웃과 반응형 디자인을 위한 공통 컴포넌트 사용 가이드입니다.
|
||
|
|
|
||
|
|
> 📌 **중요**: 페이지 타이틀 구조 통일에 대한 상세 가이드는 [TITLE_STRUCTURE_STANDARDIZATION.md](/TITLE_STRUCTURE_STANDARDIZATION.md)를 참고하세요.
|
||
|
|
|
||
|
|
## 📋 목차
|
||
|
|
|
||
|
|
1. [개요](#개요)
|
||
|
|
2. [공통 컴포넌트 목록](#공통-컴포넌트-목록)
|
||
|
|
3. [사용 예시](#사용-예시)
|
||
|
|
4. [디자인 원칙](#디자인-원칙)
|
||
|
|
|
||
|
|
## 개요
|
||
|
|
|
||
|
|
모든 페이지는 통일된 레이아웃과 디자인 스타일을 사용하여 일관된 사용자 경험을 제공합니다.
|
||
|
|
- ✅ 완전한 반응형 디자인 (데스크톱/태블릿/모바일)
|
||
|
|
- ✅ 데스크톱: 테이블 형식
|
||
|
|
- ✅ 모바일: 카드 형식
|
||
|
|
- ✅ 통일된 헤더, 검색, 통계 패턴
|
||
|
|
|
||
|
|
## 공통 컴포넌트 목록
|
||
|
|
|
||
|
|
### 1. PageLayout
|
||
|
|
|
||
|
|
페이지의 기본 레이아웃을 제공합니다.
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { PageLayout } from "./common/PageLayout";
|
||
|
|
|
||
|
|
function MyPage() {
|
||
|
|
return (
|
||
|
|
<PageLayout maxWidth="2xl">
|
||
|
|
{/* 페이지 내용 */}
|
||
|
|
</PageLayout>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Props:**
|
||
|
|
- `maxWidth`: `"sm" | "md" | "lg" | "xl" | "2xl" | "full"` (기본값: "full")
|
||
|
|
- `sm`: max-w-3xl
|
||
|
|
- `md`: max-w-5xl
|
||
|
|
- `lg`: max-w-6xl
|
||
|
|
- `xl`: max-w-7xl
|
||
|
|
- `2xl`: max-w-[1600px]
|
||
|
|
- `full`: w-full (전체 너비, 기본값)
|
||
|
|
- 자동으로 padding과 spacing 적용 (p-4 md:p-6, space-y-4 md:space-y-6)
|
||
|
|
|
||
|
|
### 2. PageHeader
|
||
|
|
|
||
|
|
페이지 헤더 (제목, 설명, 액션 버튼)
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { PageHeader } from "./common/PageHeader";
|
||
|
|
import { FileText, Plus } from "lucide-react";
|
||
|
|
|
||
|
|
<PageHeader
|
||
|
|
title="견적 관리"
|
||
|
|
description="작성된 견적서 목록을 확인하고 관리합니다"
|
||
|
|
icon={FileText}
|
||
|
|
actions={
|
||
|
|
<Button onClick={handleNew}>
|
||
|
|
<Plus className="w-4 h-4 mr-2" />
|
||
|
|
신규 작성
|
||
|
|
</Button>
|
||
|
|
}
|
||
|
|
/>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Props:**
|
||
|
|
- `title`: 페이지 제목 (필수)
|
||
|
|
- `description`: 페이지 설명 (선택)
|
||
|
|
- `icon`: Lucide 아이콘 컴포넌트 (선택)
|
||
|
|
- `actions`: 액션 버튼들 (선택)
|
||
|
|
|
||
|
|
### 3. StatCards
|
||
|
|
|
||
|
|
통계 카드를 그리드로 표시
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { StatCards } from "./common/StatCards";
|
||
|
|
import { FileText } from "lucide-react";
|
||
|
|
|
||
|
|
const stats = [
|
||
|
|
{
|
||
|
|
label: "전체 견적",
|
||
|
|
value: 150,
|
||
|
|
icon: FileText,
|
||
|
|
iconColor: "text-blue-600"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
label: "금일 작성",
|
||
|
|
value: 12,
|
||
|
|
icon: FileText,
|
||
|
|
iconColor: "text-green-600",
|
||
|
|
trend: {
|
||
|
|
value: "+15%",
|
||
|
|
isPositive: true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
];
|
||
|
|
|
||
|
|
<StatCards stats={stats} />
|
||
|
|
```
|
||
|
|
|
||
|
|
**Props:**
|
||
|
|
- `stats`: 통계 데이터 배열
|
||
|
|
- `label`: 라벨
|
||
|
|
- `value`: 값 (숫자 또는 문자열)
|
||
|
|
- `icon`: Lucide 아이콘 (선택)
|
||
|
|
- `iconColor`: 아이콘 색상 클래스 (선택)
|
||
|
|
- `trend`: 추세 정보 (선택)
|
||
|
|
|
||
|
|
### 4. SearchFilter
|
||
|
|
|
||
|
|
검색 및 필터 바
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { SearchFilter } from "./common/SearchFilter";
|
||
|
|
|
||
|
|
<SearchFilter
|
||
|
|
searchValue={searchTerm}
|
||
|
|
onSearchChange={setSearchTerm}
|
||
|
|
searchPlaceholder="거래처, 작성자로 검색..."
|
||
|
|
filterButton={true}
|
||
|
|
onFilterClick={handleFilter}
|
||
|
|
extraActions={
|
||
|
|
<Button>
|
||
|
|
<Download className="w-4 h-4 mr-2" />
|
||
|
|
내보내기
|
||
|
|
</Button>
|
||
|
|
}
|
||
|
|
/>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Props:**
|
||
|
|
- `searchValue`: 검색어 (필수)
|
||
|
|
- `onSearchChange`: 검색어 변경 핸들러 (필수)
|
||
|
|
- `searchPlaceholder`: 검색 placeholder (선택)
|
||
|
|
- `filterButton`: 필터 버튼 표시 여부 (기본값: true)
|
||
|
|
- `onFilterClick`: 필터 클릭 핸들러 (선택)
|
||
|
|
- `extraActions`: 추가 액션 버튼들 (선택)
|
||
|
|
|
||
|
|
### 5. EmptyState
|
||
|
|
|
||
|
|
빈 상태 표시
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { EmptyState } from "./common/EmptyState";
|
||
|
|
import { FileText } from "lucide-react";
|
||
|
|
|
||
|
|
<EmptyState
|
||
|
|
icon={FileText}
|
||
|
|
title="작성된 견적서가 없습니다"
|
||
|
|
description="첫 견적서를 작성하여 시작하세요"
|
||
|
|
action={{
|
||
|
|
label: "첫 견적서 작성하기",
|
||
|
|
onClick: handleNew
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Props:**
|
||
|
|
- `icon`: Lucide 아이콘 컴포넌트 (필수)
|
||
|
|
- `title`: 제목 (필수)
|
||
|
|
- `description`: 설명 (선택)
|
||
|
|
- `action`: 액션 버튼 정보 (선택)
|
||
|
|
- `label`: 버튼 텍스트
|
||
|
|
- `onClick`: 클릭 핸들러
|
||
|
|
|
||
|
|
### 6. MobileCard
|
||
|
|
|
||
|
|
모바일용 카드 컴포넌트
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { MobileCard } from "./common/MobileCard";
|
||
|
|
import { FileText, Eye, Edit, Trash2 } from "lucide-react";
|
||
|
|
|
||
|
|
<MobileCard
|
||
|
|
title="샘플 거래처"
|
||
|
|
subtitle="작성자: 김철수"
|
||
|
|
icon={<FileText className="w-4 h-4 text-blue-600" />}
|
||
|
|
badge={{
|
||
|
|
label: "진행중",
|
||
|
|
variant: "outline"
|
||
|
|
}}
|
||
|
|
fields={[
|
||
|
|
{ label: "접수일", value: "2025-01-15" },
|
||
|
|
{ label: "금액", value: "1,500,000원" },
|
||
|
|
{ label: "상태", value: "완료", badge: true, badgeVariant: "secondary" }
|
||
|
|
]}
|
||
|
|
actions={[
|
||
|
|
{
|
||
|
|
label: "상세",
|
||
|
|
icon: <Eye className="w-3 h-3 mr-1" />,
|
||
|
|
variant: "outline",
|
||
|
|
onClick: handleView
|
||
|
|
},
|
||
|
|
{
|
||
|
|
label: "수정",
|
||
|
|
icon: <Edit className="w-3 h-3 mr-1" />,
|
||
|
|
variant: "outline",
|
||
|
|
onClick: handleEdit
|
||
|
|
}
|
||
|
|
]}
|
||
|
|
/>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Props:**
|
||
|
|
- `title`: 제목 (필수)
|
||
|
|
- `subtitle`: 부제목 (선택)
|
||
|
|
- `icon`: 아이콘 ReactNode (선택)
|
||
|
|
- `badge`: 배지 정보 (선택)
|
||
|
|
- `fields`: 필드 배열 (필수)
|
||
|
|
- `actions`: 액션 버튼 배열 (선택)
|
||
|
|
|
||
|
|
## 사용 예시
|
||
|
|
|
||
|
|
### List 페이지 전체 구조
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { useState } from "react";
|
||
|
|
import { PageLayout } from "./common/PageLayout";
|
||
|
|
import { PageHeader } from "./common/PageHeader";
|
||
|
|
import { StatCards } from "./common/StatCards";
|
||
|
|
import { SearchFilter } from "./common/SearchFilter";
|
||
|
|
import { EmptyState } from "./common/EmptyState";
|
||
|
|
import { MobileCard } from "./common/MobileCard";
|
||
|
|
import { Card, CardContent } from "./ui/card";
|
||
|
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./ui/table";
|
||
|
|
import { Button } from "./ui/button";
|
||
|
|
import { Badge } from "./ui/badge";
|
||
|
|
import { Plus, FileText, Eye, Edit, Trash2 } from "lucide-react";
|
||
|
|
|
||
|
|
export function MyListPage() {
|
||
|
|
const [searchTerm, setSearchTerm] = useState("");
|
||
|
|
const [items, setItems] = useState([]);
|
||
|
|
|
||
|
|
const stats = [
|
||
|
|
{ label: "전체", value: items.length, icon: FileText, iconColor: "text-blue-600" },
|
||
|
|
{ label: "금일", value: 12, icon: FileText, iconColor: "text-green-600" }
|
||
|
|
];
|
||
|
|
|
||
|
|
const filteredItems = items.filter(item =>
|
||
|
|
item.name.toLowerCase().includes(searchTerm.toLowerCase())
|
||
|
|
);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<PageLayout>
|
||
|
|
<PageHeader
|
||
|
|
title="항목 관리"
|
||
|
|
description="항목 목록을 확인하고 관리합니다"
|
||
|
|
icon={FileText}
|
||
|
|
actions={
|
||
|
|
<Button onClick={handleNew}>
|
||
|
|
<Plus className="w-4 h-4 mr-2" />
|
||
|
|
신규 작성
|
||
|
|
</Button>
|
||
|
|
}
|
||
|
|
/>
|
||
|
|
|
||
|
|
<SearchFilter
|
||
|
|
searchValue={searchTerm}
|
||
|
|
onSearchChange={setSearchTerm}
|
||
|
|
searchPlaceholder="검색..."
|
||
|
|
/>
|
||
|
|
|
||
|
|
<StatCards stats={stats} />
|
||
|
|
|
||
|
|
{filteredItems.length === 0 ? (
|
||
|
|
<EmptyState
|
||
|
|
icon={FileText}
|
||
|
|
title="항목이 없습니다"
|
||
|
|
action={{ label: "첫 항목 작성", onClick: handleNew }}
|
||
|
|
/>
|
||
|
|
) : (
|
||
|
|
<>
|
||
|
|
{/* 데스크톱 테이블 */}
|
||
|
|
<Card className="hidden md:block">
|
||
|
|
<CardContent className="p-0">
|
||
|
|
<Table>
|
||
|
|
<TableHeader>
|
||
|
|
<TableRow>
|
||
|
|
<TableHead>이름</TableHead>
|
||
|
|
<TableHead>상태</TableHead>
|
||
|
|
<TableHead className="text-right">관리</TableHead>
|
||
|
|
</TableRow>
|
||
|
|
</TableHeader>
|
||
|
|
<TableBody>
|
||
|
|
{filteredItems.map(item => (
|
||
|
|
<TableRow key={item.id}>
|
||
|
|
<TableCell>{item.name}</TableCell>
|
||
|
|
<TableCell>
|
||
|
|
<Badge>{item.status}</Badge>
|
||
|
|
</TableCell>
|
||
|
|
<TableCell className="text-right">
|
||
|
|
<Button variant="ghost" size="sm">
|
||
|
|
<Edit className="w-4 h-4" />
|
||
|
|
</Button>
|
||
|
|
</TableCell>
|
||
|
|
</TableRow>
|
||
|
|
))}
|
||
|
|
</TableBody>
|
||
|
|
</Table>
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
|
||
|
|
{/* 모바일 카드 */}
|
||
|
|
<div className="md:hidden">
|
||
|
|
{filteredItems.map(item => (
|
||
|
|
<MobileCard
|
||
|
|
key={item.id}
|
||
|
|
title={item.name}
|
||
|
|
badge={{ label: item.status }}
|
||
|
|
fields={[
|
||
|
|
{ label: "생성일", value: item.date }
|
||
|
|
]}
|
||
|
|
actions={[
|
||
|
|
{
|
||
|
|
label: "수정",
|
||
|
|
icon: <Edit className="w-3 h-3 mr-1" />,
|
||
|
|
onClick: () => handleEdit(item)
|
||
|
|
}
|
||
|
|
]}
|
||
|
|
/>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</>
|
||
|
|
)}
|
||
|
|
</PageLayout>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Write 페이지 전체 구조
|
||
|
|
|
||
|
|
```tsx
|
||
|
|
import { PageLayout } from "./common/PageLayout";
|
||
|
|
import { PageHeader } from "./common/PageHeader";
|
||
|
|
import { Card, CardContent, CardHeader, CardTitle } from "./ui/card";
|
||
|
|
import { Button } from "./ui/button";
|
||
|
|
import { Input } from "./ui/input";
|
||
|
|
import { Label } from "./ui/label";
|
||
|
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./ui/collapsible";
|
||
|
|
import { ChevronDown, ChevronUp, Save, FileEdit } from "lucide-react";
|
||
|
|
|
||
|
|
export function MyWritePage({ onSave, onCancel }) {
|
||
|
|
const [isBasicInfoOpen, setIsBasicInfoOpen] = useState(true);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<PageLayout>
|
||
|
|
<PageHeader
|
||
|
|
title="항목 작성"
|
||
|
|
description="항목 정보를 입력하세요"
|
||
|
|
icon={FileEdit}
|
||
|
|
actions={
|
||
|
|
<>
|
||
|
|
<Button variant="outline" onClick={onCancel}>
|
||
|
|
취소
|
||
|
|
</Button>
|
||
|
|
<Button onClick={handleSave}>
|
||
|
|
<Save className="w-4 h-4 mr-2" />
|
||
|
|
저장하기
|
||
|
|
</Button>
|
||
|
|
</>
|
||
|
|
}
|
||
|
|
/>
|
||
|
|
|
||
|
|
{/* 접이식 섹션 */}
|
||
|
|
<Collapsible open={isBasicInfoOpen} onOpenChange={setIsBasicInfoOpen}>
|
||
|
|
<Card>
|
||
|
|
<CollapsibleTrigger className="w-full">
|
||
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 cursor-pointer hover:bg-muted/50">
|
||
|
|
<CardTitle>기본 정보</CardTitle>
|
||
|
|
{isBasicInfoOpen ? (
|
||
|
|
<ChevronUp className="w-5 h-5 text-muted-foreground" />
|
||
|
|
) : (
|
||
|
|
<ChevronDown className="w-5 h-5 text-muted-foreground" />
|
||
|
|
)}
|
||
|
|
</CardHeader>
|
||
|
|
</CollapsibleTrigger>
|
||
|
|
<CollapsibleContent>
|
||
|
|
<CardContent className="space-y-4">
|
||
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
|
|
<div>
|
||
|
|
<Label>이름</Label>
|
||
|
|
<Input />
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<Label>상태</Label>
|
||
|
|
<Input />
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</CardContent>
|
||
|
|
</CollapsibleContent>
|
||
|
|
</Card>
|
||
|
|
</Collapsible>
|
||
|
|
</PageLayout>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 디자인 원칙
|
||
|
|
|
||
|
|
### 반응형 브레이크포인트
|
||
|
|
|
||
|
|
- **모바일**: `< 768px` - 카드 뷰, 단일 컬럼
|
||
|
|
- **태블릿**: `768px - 1024px` - 2-3 컬럼 그리드
|
||
|
|
- **데스크톱**: `> 1024px` - 테이블 뷰, 4 컬럼 그리드
|
||
|
|
|
||
|
|
### 간격 (Spacing)
|
||
|
|
|
||
|
|
**필수 규칙:**
|
||
|
|
- 모든 페이지는 `PageLayout`으로 감싸기 (자동으로 통일된 padding/spacing 적용)
|
||
|
|
- 페이지 padding: `p-4 md:p-6` (PageLayout 자동 적용)
|
||
|
|
- 섹션 간격: `space-y-4 md:space-y-6` (PageLayout 자동 적용)
|
||
|
|
- 그리드 간격: `gap-4`
|
||
|
|
- Card 내부 padding: `p-4 md:p-6`
|
||
|
|
- Table이 포함된 Card: `CardContent`에 `p-0` 사용 (테이블이 꽉 차도록)
|
||
|
|
|
||
|
|
### 색상 패턴
|
||
|
|
|
||
|
|
통계 카드 아이콘 색상:
|
||
|
|
- 파랑: `text-blue-600` - 전체/기본
|
||
|
|
- 초록: `text-green-600` - 성공/금일
|
||
|
|
- 주황: `text-orange-600` - 경고/이번주
|
||
|
|
- 보라: `text-purple-600` - 정보/평균
|
||
|
|
|
||
|
|
### 타이포그래피
|
||
|
|
|
||
|
|
**페이지 타이틀 구조 (필수 규칙):**
|
||
|
|
- ⛔ **절대 금지**: 직접 `<h1>` 태그나 커스텀 제목 작성
|
||
|
|
- ✅ **필수 사용**: `PageHeader` 컴포넌트 사용
|
||
|
|
- PageHeader는 자동으로 올바른 타이포그래피 적용 (`text-xl md:text-2xl`)
|
||
|
|
|
||
|
|
**잘못된 예시 (사용 금지):**
|
||
|
|
```tsx
|
||
|
|
<h1 className="text-3xl font-bold">주문 관리</h1>
|
||
|
|
<h1 className="text-2xl md:text-3xl font-bold">차량관리</h1>
|
||
|
|
```
|
||
|
|
|
||
|
|
**올바른 예시 (필수):**
|
||
|
|
```tsx
|
||
|
|
<PageHeader
|
||
|
|
title="주문 관리"
|
||
|
|
description="고객 주문 접수 및 진행 상황 관리"
|
||
|
|
icon={ShoppingCart}
|
||
|
|
actions={<Button>등록</Button>}
|
||
|
|
/>
|
||
|
|
```
|
||
|
|
|
||
|
|
**타이포그래피 규칙:**
|
||
|
|
- 페이지 제목: `text-xl md:text-2xl` (PageHeader가 자동 적용)
|
||
|
|
- 카드 제목: `text-base`
|
||
|
|
- 본문: `text-sm`
|
||
|
|
- 설명: `text-sm text-muted-foreground`
|
||
|
|
|
||
|
|
### 아이콘 크기
|
||
|
|
|
||
|
|
- 헤더 아이콘: `w-6 h-6`
|
||
|
|
- 버튼 아이콘: `w-4 h-4`
|
||
|
|
- 작은 아이콘: `w-3 h-3`
|
||
|
|
- 통계 아이콘: `w-8 h-8 md:w-10 md:h-10`
|
||
|
|
|
||
|
|
## 체크리스트
|
||
|
|
|
||
|
|
새 페이지를 만들 때 다음을 확인하세요:
|
||
|
|
|
||
|
|
### 필수 사항
|
||
|
|
- [ ] `PageLayout`으로 감싸기 (maxWidth는 특별한 경우가 아니면 기본값 "full" 사용)
|
||
|
|
- [ ] `PageHeader` 사용 (title, description, icon, actions) - **커스텀 h1 태그 절대 금지**
|
||
|
|
- [ ] 페이지 타이틀은 반드시 `PageHeader` 컴포넌트 사용 - `text-3xl`, `text-2xl md:text-3xl` 직접 사용 금지
|
||
|
|
- [ ] 통계가 필요하면 `StatCards` 사용
|
||
|
|
- [ ] 검색이 필요하면 `SearchFilter` 사용
|
||
|
|
- [ ] 빈 상태에 `EmptyState` 사용
|
||
|
|
|
||
|
|
### 반응형 디자인
|
||
|
|
- [ ] 데스크톱: `Card` + `Table` (테이블을 감싸는 Card만 표시)
|
||
|
|
- [ ] 모바일: `MobileCard` (md:hidden)
|
||
|
|
- [ ] 반응형 그리드: `grid-cols-1 md:grid-cols-2 lg:grid-cols-4`
|
||
|
|
|
||
|
|
### 레이아웃 규칙
|
||
|
|
- [ ] Table이 포함된 Card의 CardContent는 `p-0` 사용
|
||
|
|
- [ ] Dialog width: `max-w-[95vw] sm:max-w-lg md:max-w-2xl` 또는 `max-w-[95vw] sm:max-w-xl md:max-w-4xl`
|
||
|
|
- [ ] Dialog에 `DialogDescription` 포함
|
||
|
|
- [ ] 모든 액션 버튼에 아이콘 추가
|
||
|
|
- [ ] Card 내부 padding: `p-4 md:p-6` (Table이 있는 경우 제외)
|
||
|
|
|
||
|
|
## 디자인 시스템 통일 작업
|
||
|
|
|
||
|
|
### ✅ 완료된 페이지 (PageLayout + PageHeader 적용)
|
||
|
|
|
||
|
|
- `/components/VehicleManagement.tsx` - 차량관리
|
||
|
|
- `/components/EquipmentManagement.tsx` - 설비관리
|
||
|
|
- `/components/ItemManagement.tsx` - 품목관리
|
||
|
|
- `/components/OrderManagement.tsx` - 주문관리 ⭐ **NEW**
|
||
|
|
- `/components/QuoteManagement3List.tsx` - 견적관리 목록
|
||
|
|
- `/components/QuoteManagement3Write.tsx` - 견적관리 작성
|
||
|
|
|
||
|
|
### ⚠️ 업데이트 필요 페이지 (커스텀 h1 사용 중)
|
||
|
|
|
||
|
|
다음 페이지들은 아직 커스텀 `<h1 className="text-3xl font-bold">` 또는 `<h1 className="text-2xl md:text-3xl font-bold">`를 사용하고 있어 `PageHeader` 컴포넌트로 변경이 필요합니다:
|
||
|
|
|
||
|
|
**품질관리:**
|
||
|
|
- (대부분 완료)
|
||
|
|
|
||
|
|
**전자결재:**
|
||
|
|
- `/components/ApprovalManagement.tsx` - 전자결재
|
||
|
|
|
||
|
|
**회계관리:**
|
||
|
|
- `/components/AccountingManagement.tsx` - 회계 관리
|
||
|
|
- `/components/ShippingManagement.tsx` - 출고관리
|
||
|
|
|
||
|
|
**인사관리:**
|
||
|
|
- `/components/HRManagement.tsx` - 인사관리
|
||
|
|
|
||
|
|
**시스템관리:**
|
||
|
|
- `/components/SystemManagement.tsx` - 시스템 관리
|
||
|
|
|
||
|
|
**자재관리:**
|
||
|
|
- `/components/MaterialManagement.tsx` - 자재 관리
|
||
|
|
|
||
|
|
**대시보드:**
|
||
|
|
- `/components/Dashboard.tsx` - CEO/생산관리자/작업자 대시보드
|
||
|
|
|
||
|
|
### 업데이트 방법
|
||
|
|
|
||
|
|
기존 커스텀 헤더 코드:
|
||
|
|
```tsx
|
||
|
|
<div className="flex justify-between items-center">
|
||
|
|
<div>
|
||
|
|
<h1 className="text-3xl font-bold text-foreground">주문 관리</h1>
|
||
|
|
<p className="text-muted-foreground mt-1">고객 주문 접수 및 진행 상황 관리</p>
|
||
|
|
</div>
|
||
|
|
<div className="flex space-x-3">
|
||
|
|
<Button>등록</Button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
```
|
||
|
|
|
||
|
|
변경 후 PageHeader 사용:
|
||
|
|
```tsx
|
||
|
|
import { PageHeader } from "./common/PageHeader";
|
||
|
|
import { ShoppingCart } from "lucide-react";
|
||
|
|
|
||
|
|
<PageHeader
|
||
|
|
title="주문 관리"
|
||
|
|
description="고객 주문 접수 및 진행 상황 관리"
|
||
|
|
icon={ShoppingCart}
|
||
|
|
actions={
|
||
|
|
<Button>
|
||
|
|
<Plus className="w-4 h-4 mr-2" />
|
||
|
|
등록
|
||
|
|
</Button>
|
||
|
|
}
|
||
|
|
/>
|
||
|
|
```
|
||
|
|
|
||
|
|
### 업데이트 체크리스트
|
||
|
|
|
||
|
|
각 페이지 업데이트 시 확인:
|
||
|
|
1. [ ] `PageLayout`으로 전체 페이지 감싸기
|
||
|
|
2. [ ] 커스텀 `<h1>` 태그 제거
|
||
|
|
3. [ ] `PageHeader` 컴포넌트 import 및 사용
|
||
|
|
4. [ ] 적절한 lucide-react 아이콘 추가
|
||
|
|
5. [ ] actions prop에 버튼들 이동
|
||
|
|
6. [ ] description prop에 설명 추가
|
||
|
|
7. [ ] `text-3xl` 또는 `text-2xl md:text-3xl` 클래스 완전 제거
|
||
|
|
|
||
|
|
## 참고 파일
|
||
|
|
|
||
|
|
완벽하게 구현된 예시:
|
||
|
|
- `/components/VehicleManagement.tsx` - 차량관리 (PageLayout + PageHeader 완벽 구현)
|
||
|
|
- `/components/EquipmentManagement.tsx` - 설비관리 (PageLayout + PageHeader 완벽 구현)
|
||
|
|
- `/components/ItemManagement.tsx` - 품목관리 (PageLayout + PageHeader 완벽 구현)
|
||
|
|
- `/components/QuoteManagement3List.tsx` - 견적관리 목록
|
||
|
|
- `/components/QuoteManagement3Write.tsx` - 견적관리 작성
|
||
|
|
- `/components/common/` - 모든 공통 컴포넌트
|