🎨 시연회용 데모 페이지 완성 - DevonThink UI 스타일

핵심 구현사항:
 4개 핵심 기능 페이지 완성
- 📋 프로젝트 정보 등록 (2단계 시스템)
- 🏭 생산회의록 시스템 (4구역 레이아웃)
- 📦 입고 검수 & 보관 관리
- 🔧 생산팀 작업 관리 (그룹장용)

🎨 DevonThink 스타일 디자인
- 회색(#F8F9FA~#202124) + 하늘색(#4A90E2) 컬러 팔레트
- 미니멀하고 전문적인 UI/UX
- 반응형 디자인 (데스크톱/태블릿/모바일)
- CSS Grid + Flexbox 레이아웃

 인터랙티브 기능
- 페이지 전환 애니메이션 (fade-in, slide-in)
- 버튼 클릭 상태 변경 및 알림 시스템
- 진행률 슬라이더, 창고 선반 선택
- 키보드 단축키 (Ctrl+1~4)

📁 파일 구조
- demo/index.html (메인 HTML)
- demo/styles/devonthink.css (DevonThink 스타일)
- demo/styles/main.css (기본 CSS + 유틸리티)
- demo/scripts/main.js (JavaScript 기능)
- demo/README.md (사용법 및 시연 시나리오)

💾 하드코딩 데이터
- TK-2024-015 프로젝트 (ABC 공장 배관공사)
- 공정표, 일정, 자재 현황, 이슈 사항
- 실제 업무 시나리오 반영

🎯 시연 준비 완료
- 브라우저에서 index.html 실행 가능
- 4개 페이지 완전 구현
- 실무진 시연용 데모 완성
This commit is contained in:
Hyungi Ahn
2025-09-15 11:21:12 +09:00
parent 1261468797
commit d1d52e4f50
5 changed files with 3350 additions and 0 deletions

177
demo/README.md Normal file
View File

@@ -0,0 +1,177 @@
# TK Project - 시연회용 데모
## 📋 개요
TK Project 통합 프로젝트 관리 시스템의 시연회용 데모 페이지입니다.
DevonThink UI 스타일을 참조하여 회색과 하늘색 위주의 깔끔한 디자인으로 구현되었습니다.
## 🚀 핵심 기능 4선
### 1. 📋 프로젝트 정보 등록 (2단계 시스템)
- **1단계**: 프로젝트 생성 및 기본 정보 입력
- **2단계**: 세부 사양 입력 (킥오프 미팅, 기술 사양서)
- **특징**: Job No. 자동 생성, 프로젝트 승인 시스템
### 2. 🏭 생산회의록 시스템 (4구역 레이아웃)
- **상단**: 프로젝트 공정표 (Gantt Chart 스타일)
- **중앙 좌측**: 캘린더 기반 일정 관리
- **우측**: 입고 예정 품목 현황
- **하단**: Follow-up 리스트 (미해결 사항)
### 3. 📦 입고 검수 & 보관 관리
- **상단**: 발주 연동 입고 대시보드
- **중앙**: 3단계 검수 프로세스
- **하단**: 창고 구역 관리 및 인수인계 현황
### 4. 🔧 생산팀 작업 관리 (그룹장용)
- **좌측**: 일일 작업 현황 입력 (TK-FB 스타일)
- **우측**: 자재 확인 기능 (확인 전용)
- **특징**: 이슈 등록, 데일리 체크 연동
## 🎨 디자인 특징
### DevonThink 스타일
- **컬러 팔레트**: 회색(#F8F9FA ~ #202124), 하늘색(#4A90E2)
- **최소한의 색상 사용**: 깔끔하고 전문적인 느낌
- **그림자 효과**: 미묘한 그림자로 깊이감 표현
- **타이포그래피**: 시스템 폰트 사용으로 가독성 향상
### 반응형 디자인
- **데스크톱**: 사이드바 + 메인 컨텐츠 레이아웃
- **태블릿/모바일**: 스택형 레이아웃으로 자동 변환
- **그리드 시스템**: CSS Grid를 활용한 유연한 레이아웃
## 🖥️ 사용 방법
### 실행
1. `index.html` 파일을 웹 브라우저에서 열기
2. 또는 로컬 서버 실행:
```bash
# Python 3
python -m http.server 8000
# Node.js (http-server)
npx http-server
```
### 네비게이션
- **사이드바**: 각 기능별 페이지 이동
- **키보드 단축키**: Ctrl + 1~4 (페이지 전환)
- **반응형**: 모바일에서는 상하 스택 레이아웃
### 인터랙티브 기능
- **버튼 클릭**: 상태 변경 및 알림 표시
- **진행률 슬라이더**: 실시간 값 업데이트
- **창고 선반 선택**: 클릭으로 위치 지정
- **애니메이션**: 페이지 전환시 fade-in 효과
## 📁 파일 구조
```
demo/
├── index.html # 메인 HTML 파일
├── styles/
│ ├── devonthink.css # DevonThink 스타일 CSS
│ └── main.css # 기본 CSS 및 유틸리티
├── scripts/
│ └── main.js # JavaScript 기능
└── README.md # 이 파일
```
## 🎯 시연 시나리오
### Scene 1: 프로젝트 등록
1. 새 프로젝트 "ABC 공장 배관공사" 등록
2. Job No. TK-2024-015 자동 생성
3. 기본 정보 입력 → 프로젝트 승인
4. 세부 사양 입력 (킥오프 미팅 결과)
### Scene 2: 생산회의
1. 프로젝트 현황 대시보드 확인
2. 공정표에서 진행률 확인 (설계 100%, 구매 85%)
3. 캘린더 일정 확인 (도장 외주, 압력시험)
4. Follow-up 리스트에서 긴급 사항 확인
### Scene 3: 입고 검수
1. 발주 연동 대시보드에서 입고 예정 품목 확인
2. 검수 프로세스 진행 (입고확인 → 검수진행 → 결과처리)
3. 창고 구역에서 보관 위치 선택
4. 인수인계 현황 확인
### Scene 4: 생산팀 작업
1. 김그룹장 일일 작업 현황 입력
2. 자재 현황 확인 (BOM 조회)
3. 부족한 자재 발견 → 데일리 체크 기록
4. 가용 자재 인수 처리
## 💡 하드코딩 데이터
### 프로젝트 정보
- **Job No**: TK-2024-015
- **프로젝트명**: ABC 공장 배관공사
- **고객사**: ABC 케미칼
- **납기일**: 2024-03-30
### 자재 현황
- 파이프 4인치: 사용가능 (A-3-상단, 45EA)
- 엘보 4인치: 발주중 (입고예정 09/18)
- 플랜지 4인치: 미요청 (설계팀 대기)
- 밸브 2인치: 입고완료 (B-2-중단)
### 이슈 사항
- 14:30 자재부족: 엘보 4인치 10EA 부족
- 10:15 품질이슈: 용접부 기공 발견
## 🔧 기술 스택
### Frontend
- **HTML5**: 시맨틱 마크업
- **CSS3**: Grid, Flexbox, 애니메이션
- **Vanilla JavaScript**: ES6+ 문법 사용
### 스타일링
- **CSS Variables**: 일관된 컬러 시스템
- **CSS Grid**: 복잡한 레이아웃 구현
- **CSS Animations**: 부드러운 전환 효과
### 호환성
- **모던 브라우저**: Chrome, Firefox, Safari, Edge
- **반응형**: 모바일, 태블릿, 데스크톱
- **접근성**: 키보드 네비게이션 지원
## 📝 개발 노트
### 디자인 철학
- **미니멀리즘**: 불필요한 요소 제거
- **일관성**: 통일된 컴포넌트 시스템
- **사용성**: 직관적인 인터페이스
### 성능 최적화
- **CSS 최적화**: 효율적인 선택자 사용
- **JavaScript 최적화**: 이벤트 위임 패턴
- **이미지 최적화**: 아이콘은 이모지 사용
### 확장성
- **모듈화**: 기능별 함수 분리
- **데이터 분리**: 하드코딩 데이터 객체화
- **컴포넌트화**: 재사용 가능한 UI 요소
## 🚀 향후 계획
### Phase 1: 기본 기능 (완료)
- [x] 4개 핵심 페이지 구현
- [x] DevonThink 스타일 적용
- [x] 인터랙티브 기능 추가
### Phase 2: 고도화 (예정)
- [ ] 실제 데이터베이스 연동
- [ ] 사용자 인증 시스템
- [ ] 실시간 데이터 업데이트
### Phase 3: 확장 (예정)
- [ ] 모바일 앱 개발
- [ ] API 연동
- [ ] 고급 분석 기능
---
**TK Project Demo v1.0**
*DevonThink 스타일 기반 프로젝트 관리 시스템*

630
demo/index.html Normal file
View File

@@ -0,0 +1,630 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TK Project - 통합 프로젝트 관리 시스템</title>
<link rel="stylesheet" href="styles/main.css">
<link rel="stylesheet" href="styles/devonthink.css">
</head>
<body>
<!-- 사이드바 -->
<div class="sidebar">
<div class="sidebar-header">
<h1>TK Project</h1>
<p class="version">v1.0 Demo</p>
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<h3>프로젝트 관리</h3>
<ul>
<li><a href="#" onclick="showPage('project-registration')" class="nav-item active">
<span class="nav-icon">📋</span>
프로젝트 등록
</a></li>
<li><a href="#" onclick="showPage('production-meeting')" class="nav-item">
<span class="nav-icon">🏭</span>
생산회의록
</a></li>
</ul>
</div>
<div class="nav-section">
<h3>자재 관리</h3>
<ul>
<li><a href="#" onclick="showPage('incoming-inspection')" class="nav-item">
<span class="nav-icon">📦</span>
입고 검수
</a></li>
</ul>
</div>
<div class="nav-section">
<h3>생산 관리</h3>
<ul>
<li><a href="#" onclick="showPage('production-work')" class="nav-item">
<span class="nav-icon">🔧</span>
생산팀 작업
</a></li>
</ul>
</div>
</nav>
<div class="sidebar-footer">
<div class="user-info">
<div class="user-avatar"></div>
<div class="user-details">
<div class="user-name">김그룹장</div>
<div class="user-role">생산팀</div>
</div>
</div>
</div>
</div>
<!-- 메인 컨텐츠 -->
<div class="main-content">
<!-- 프로젝트 등록 페이지 -->
<div id="project-registration" class="page active">
<div class="page-header">
<h2>프로젝트 정보 등록</h2>
<p class="page-description">새로운 프로젝트를 등록하고 기본 정보를 입력합니다.</p>
</div>
<div class="content-grid">
<!-- 1단계: 기본 정보 -->
<div class="card">
<div class="card-header">
<h3>1단계: 프로젝트 생성</h3>
<span class="status-badge status-active">진행중</span>
</div>
<div class="card-content">
<form class="form-grid">
<div class="form-group">
<label>프로젝트명 *</label>
<input type="text" value="ABC 공장 배관공사" class="form-input">
</div>
<div class="form-group">
<label>고객사 *</label>
<input type="text" value="ABC 케미칼" class="form-input">
</div>
<div class="form-group">
<label>계약금액</label>
<input type="text" value="150,000,000원" class="form-input">
</div>
<div class="form-group">
<label>납기일 *</label>
<input type="date" value="2024-03-30" class="form-input">
</div>
<div class="form-group">
<label>납품방식</label>
<select class="form-select">
<option value="현장납품" selected>현장납품</option>
<option value="공장인도">공장인도</option>
<option value="부분납품">부분납품</option>
</select>
</div>
<div class="form-group">
<label>제작방식</label>
<select class="form-select">
<option value="자체제작" selected>자체제작</option>
<option value="외주제작">외주제작</option>
</select>
</div>
</form>
<div class="auto-generated">
<div class="generated-item">
<span class="label">자동 생성된 Job No.</span>
<span class="value">TK-2024-015</span>
</div>
</div>
<button class="btn btn-primary">✅ 프로젝트 승인 완료</button>
</div>
</div>
<!-- 2단계: 세부 사양 -->
<div class="card">
<div class="card-header">
<h3>2단계: 세부 사양 입력</h3>
<span class="status-badge status-optional">선택적</span>
</div>
<div class="card-content">
<div class="info-section">
<h4>킥오프 미팅 결과</h4>
<div class="info-grid">
<div class="info-item">
<span class="info-label">미팅 일자</span>
<span class="info-value">2024-01-20</span>
</div>
<div class="info-item">
<span class="info-label">참석자</span>
<span class="info-value">김영업, 이PM, 박설계, 최고객</span>
</div>
</div>
<div class="decisions">
<h5>주요 결정사항</h5>
<ul>
<li>압력등급 150LB로 확정</li>
<li>재질 SS316L로 변경</li>
</ul>
</div>
</div>
<div class="info-section">
<h4>기술 사양서</h4>
<div class="spec-grid">
<div class="spec-item">
<span class="spec-label">설계기준</span>
<span class="spec-value">ASME B31.3</span>
</div>
<div class="spec-item">
<span class="spec-label">사용압력</span>
<span class="spec-value">10 bar</span>
</div>
<div class="spec-item">
<span class="spec-label">사용온도</span>
<span class="spec-value">80°C</span>
</div>
<div class="spec-item">
<span class="spec-label">유체</span>
<span class="spec-value">화학용매</span>
</div>
</div>
</div>
<button class="btn btn-secondary">📝 세부사양 업데이트</button>
</div>
</div>
</div>
</div>
<!-- 생산회의록 페이지 -->
<div id="production-meeting" class="page">
<div class="page-header">
<h2>생산회의록 시스템</h2>
<p class="page-description">TK-2024-015 (ABC 공장 배관공사) 프로젝트 현황</p>
</div>
<!-- 공정표 (상단) -->
<div class="process-chart">
<h3>프로젝트 공정표</h3>
<div class="gantt-container">
<div class="process-item completed">
<div class="process-info">
<span class="process-name">설계</span>
<span class="process-responsible">박설계</span>
</div>
<div class="process-bar">
<div class="progress-fill" style="width: 100%"></div>
<span class="progress-text">100%</span>
</div>
<span class="process-date">2024-02-15</span>
</div>
<div class="process-item in-progress">
<div class="process-info">
<span class="process-name">구매</span>
<span class="process-responsible">김구매</span>
</div>
<div class="process-bar">
<div class="progress-fill" style="width: 85%"></div>
<span class="progress-text">85%</span>
</div>
<span class="process-date">2024-02-28</span>
</div>
<div class="process-item in-progress">
<div class="process-info">
<span class="process-name">제작</span>
<span class="process-responsible">이생산</span>
</div>
<div class="process-bar">
<div class="progress-fill" style="width: 60%"></div>
<span class="progress-text">60%</span>
</div>
<span class="process-date">2024-03-20</span>
</div>
<div class="process-item pending">
<div class="process-info">
<span class="process-name">검사</span>
<span class="process-responsible">최품질</span>
</div>
<div class="process-bar">
<div class="progress-fill" style="width: 0%"></div>
<span class="progress-text">대기</span>
</div>
<span class="process-date">2024-03-25</span>
</div>
</div>
</div>
<div class="meeting-layout">
<!-- 캘린더 일정 (중앙 좌측) -->
<div class="schedule-section">
<h3>캘린더 일정</h3>
<div class="schedule-list">
<div class="schedule-item urgent">
<div class="schedule-date">09/20</div>
<div class="schedule-content">
<div class="schedule-title">도장 작업 출고</div>
<div class="schedule-type">외주출고</div>
<div class="schedule-memo">A구역 파이프 20본, B구역 밸브 5개</div>
</div>
</div>
<div class="schedule-item normal">
<div class="schedule-date">09/22</div>
<div class="schedule-content">
<div class="schedule-title">압력시험</div>
<div class="schedule-type">검사일정</div>
<div class="schedule-memo">시험압력: 15bar, 30분간 유지</div>
</div>
</div>
</div>
</div>
<!-- 입고 예정 품목 (우측) -->
<div class="delivery-section">
<h3>입고 예정 품목</h3>
<div class="delivery-list">
<div class="delivery-item completed">
<span class="delivery-status"></span>
<div class="delivery-info">
<div class="delivery-name">파이프 4인치 x 50EA</div>
<div class="delivery-date">입고완료 - 09/10</div>
</div>
</div>
<div class="delivery-item warning">
<span class="delivery-status">🟡</span>
<div class="delivery-info">
<div class="delivery-name">밸브 2인치 x 10EA</div>
<div class="delivery-date">입고예정 - 09/16</div>
</div>
</div>
<div class="delivery-item delayed">
<span class="delivery-status">🔴</span>
<div class="delivery-info">
<div class="delivery-name">엘보 4인치 x 20EA</div>
<div class="delivery-date">지연 - 09/12 → 09/18</div>
</div>
</div>
</div>
</div>
</div>
<!-- Follow-up 리스트 (하단) -->
<div class="followup-section">
<h3>Follow-up 리스트</h3>
<div class="followup-list">
<div class="followup-item priority-high">
<div class="followup-priority">🔴 긴급</div>
<div class="followup-content">
<div class="followup-title">밸브 A 납기 지연 대응</div>
<div class="followup-description">주 공급업체 생산 지연으로 대체 업체 검토 필요</div>
<div class="followup-meta">
<span class="followup-responsible">김구매</span>
<span class="followup-date">등록: 09/10 | 예상해결: 09/17</span>
</div>
</div>
<div class="followup-status">진행중</div>
</div>
<div class="followup-item priority-medium">
<div class="followup-priority">🟡 높음</div>
<div class="followup-content">
<div class="followup-title">용접 검사 일정 조정</div>
<div class="followup-description">고객사 일정 변경으로 검사일 재조정 필요</div>
<div class="followup-meta">
<span class="followup-responsible">최품질</span>
<span class="followup-date">등록: 09/12 | 예상해결: 09/20</span>
</div>
</div>
<div class="followup-status">검토중</div>
</div>
</div>
</div>
</div>
<!-- 입고 검수 페이지 -->
<div id="incoming-inspection" class="page">
<div class="page-header">
<h2>입고 검수 & 보관 관리</h2>
<p class="page-description">발주 정보 연동을 통한 입고 처리 및 보관 관리</p>
</div>
<!-- 발주 연동 대시보드 (상단) -->
<div class="purchase-dashboard">
<h3>발주 연동 입고 대시보드</h3>
<div class="purchase-list">
<div class="purchase-item status-pending">
<div class="purchase-info">
<div class="purchase-header">
<span class="po-number">PO-2024-0156</span>
<span class="purchase-status status-pending">입고대기</span>
</div>
<div class="purchase-details">
<div class="item-name">스테인리스 파이프 4인치 SCH40</div>
<div class="item-meta">수량: 50EA | 공급업체: 대한파이프 | 담당: 김구매</div>
<div class="item-dates">발주일: 2024-09-01 | 예정일: 2024-09-15</div>
</div>
</div>
<button class="btn btn-sm btn-primary">검수 시작</button>
</div>
<div class="purchase-item status-inspecting">
<div class="purchase-info">
<div class="purchase-header">
<span class="po-number">PO-2024-0157</span>
<span class="purchase-status status-inspecting">검수중</span>
</div>
<div class="purchase-details">
<div class="item-name">게이트밸브 2인치 150LB</div>
<div class="item-meta">수량: 10EA | 공급업체: 코리아밸브 | 담당: 김구매</div>
<div class="item-dates">발주일: 2024-09-05 | 예정일: 2024-09-16</div>
</div>
</div>
<button class="btn btn-sm btn-warning">검수 진행중</button>
</div>
</div>
</div>
<div class="inspection-layout">
<!-- 검수 프로세스 (좌측) -->
<div class="inspection-process">
<h3>입고 검수 프로세스</h3>
<div class="process-steps">
<div class="step completed">
<div class="step-number">1</div>
<div class="step-content">
<h4>입고 확인</h4>
<ul class="checklist">
<li class="checked">✅ 납품서 확인</li>
<li class="checked">✅ 검사성적서 확인</li>
<li class="checked">✅ 포장 상태 체크</li>
</ul>
</div>
</div>
<div class="step active">
<div class="step-number">2</div>
<div class="step-content">
<h4>검수 진행</h4>
<ul class="checklist">
<li class="checked">✅ 수량 검수 (10/10 EA)</li>
<li class="checked">✅ 외관 검사</li>
<li class="active">🔄 규격 검사 진행중</li>
<li>📷 사진 촬영 대기</li>
</ul>
</div>
</div>
<div class="step pending">
<div class="step-number">3</div>
<div class="step-content">
<h4>검수 결과</h4>
<div class="result-options">
<button class="btn btn-success btn-sm">✅ 합격</button>
<button class="btn btn-danger btn-sm">❌ 불합격</button>
<button class="btn btn-warning btn-sm">📝 조건부 합격</button>
</div>
</div>
</div>
</div>
</div>
<!-- 보관 위치 관리 (우측) -->
<div class="storage-management">
<h3>보관 위치 관리</h3>
<div class="warehouse-zones">
<div class="zone-item">
<div class="zone-header">
<span class="zone-name">A구역 (파이프류)</span>
<span class="zone-capacity">75/100</span>
</div>
<div class="zone-shelves">
<div class="shelf available">A-1</div>
<div class="shelf occupied">A-2</div>
<div class="shelf available">A-3</div>
<div class="shelf occupied">A-4</div>
<div class="shelf available">A-5</div>
</div>
</div>
<div class="zone-item">
<div class="zone-header">
<span class="zone-name">B구역 (밸브류)</span>
<span class="zone-capacity">45/80</span>
</div>
<div class="zone-shelves">
<div class="shelf available">B-1</div>
<div class="shelf available">B-2</div>
<div class="shelf occupied">B-3</div>
<div class="shelf available">B-4</div>
</div>
</div>
</div>
<div class="storage-actions">
<div class="selected-location">
<span class="label">선택된 위치:</span>
<span class="location">B-2-중단</span>
</div>
<button class="btn btn-primary">📍 위치 지정 완료</button>
<button class="btn btn-secondary">🏷️ QR코드 라벨 출력</button>
</div>
</div>
</div>
<!-- 인수인계 현황 -->
<div class="handover-status">
<h3>인수인계 현황</h3>
<div class="handover-list">
<div class="handover-item">
<div class="item-info">
<span class="item-name">파이프 4인치</span>
<span class="item-location">A-3-상단</span>
</div>
<div class="quantity-info">
<span class="total">전체: 50EA</span>
<span class="handed">인수: 30EA</span>
<span class="remaining">잔량: 20EA</span>
</div>
<span class="handover-status-badge status-partial">부분인수</span>
</div>
</div>
</div>
</div>
<!-- 생산팀 작업 관리 페이지 -->
<div id="production-work" class="page">
<div class="page-header">
<h2>생산팀 작업 관리</h2>
<p class="page-description">김그룹장 - 용접팀 일일 작업 현황</p>
</div>
<div class="work-layout">
<!-- 일일 작업 현황 (좌측) -->
<div class="daily-work">
<h3>일일 작업 현황 입력</h3>
<div class="work-form">
<div class="form-section">
<h4>기본 정보</h4>
<div class="form-grid">
<div class="form-group">
<label>프로젝트</label>
<select class="form-select">
<option selected>TK-2024-015 (ABC 공장 배관공사)</option>
<option>TK-2024-016 (DEF 플랜트)</option>
</select>
</div>
<div class="form-group">
<label>작업 인원</label>
<input type="number" value="5" class="form-input">
</div>
<div class="form-group">
<label>작업 내용</label>
<input type="text" value="메인 라인 파이프 용접" class="form-input">
</div>
<div class="form-group">
<label>진행률</label>
<div class="progress-input">
<input type="range" min="0" max="100" value="75" class="progress-slider">
<span class="progress-value">75%</span>
</div>
</div>
</div>
</div>
<div class="form-section">
<h4>이슈 사항</h4>
<div class="issue-list">
<div class="issue-item">
<div class="issue-header">
<span class="issue-time">14:30</span>
<span class="issue-type type-material">자재부족</span>
<span class="issue-urgency urgency-high">높음</span>
</div>
<div class="issue-content">
<div class="issue-description">엘보 4인치 10EA 부족으로 작업 중단</div>
<div class="issue-solution">구매팀에 긴급 요청, 대체재 검토</div>
</div>
</div>
<div class="issue-item">
<div class="issue-header">
<span class="issue-time">10:15</span>
<span class="issue-type type-quality">품질이슈</span>
<span class="issue-urgency urgency-medium">보통</span>
</div>
<div class="issue-content">
<div class="issue-description">용접부 기공 발견, 재작업 필요</div>
<div class="issue-solution">해당 부위 그라인딩 후 재용접</div>
</div>
</div>
</div>
<button class="btn btn-secondary"> 새 이슈 등록</button>
</div>
</div>
</div>
<!-- 자재 확인 (우측) -->
<div class="material-check">
<h3>자재 확인 기능</h3>
<div class="material-search">
<div class="search-input">
<input type="text" placeholder="Job No. 입력 (TK-2024-015)" class="form-input">
<button class="btn btn-primary">🔍 BOM 조회</button>
</div>
</div>
<div class="material-list">
<div class="material-item status-available">
<div class="material-info">
<div class="material-name">파이프 4인치 SCH40</div>
<div class="material-details">필요: 50EA | 위치: A-3-상단</div>
</div>
<div class="material-status">
<span class="status-badge status-available">🟢 사용가능</span>
<span class="material-qty">45EA</span>
</div>
</div>
<div class="material-item status-ordered">
<div class="material-info">
<div class="material-name">엘보 4인치 150LB</div>
<div class="material-details">필요: 20EA | 구매 진행 중</div>
</div>
<div class="material-status">
<span class="status-badge status-ordered">🟠 발주중</span>
<span class="material-date">입고예정: 09/18</span>
</div>
</div>
<div class="material-item status-not-requested">
<div class="material-info">
<div class="material-name">플랜지 4인치 150LB</div>
<div class="material-details">필요: 15EA | 설계팀 구매 요청 대기</div>
</div>
<div class="material-status">
<span class="status-badge status-not-requested">⚫ 미요청</span>
<span class="material-note">사양 확정 대기</span>
</div>
</div>
<div class="material-item status-ready">
<div class="material-info">
<div class="material-name">밸브 2인치 150LB</div>
<div class="material-details">필요: 5EA | 위치: B-2-중단</div>
</div>
<div class="material-status">
<span class="status-badge status-ready">🟡 입고완료</span>
<button class="btn btn-sm btn-primary">인수 처리</button>
</div>
</div>
</div>
<div class="material-actions">
<div class="action-note">
<p><strong>⚠️ 중요:</strong> 생산팀은 자재 요청을 하지 않습니다.</p>
<p>부족한 자재 발견시 → 데일리 체크에 이슈 등록</p>
</div>
<button class="btn btn-warning">📝 데일리 체크에 기록</button>
</div>
</div>
</div>
</div>
</div>
<script src="scripts/main.js"></script>
</body>
</html>

641
demo/scripts/main.js Normal file
View File

@@ -0,0 +1,641 @@
// TK Project Demo - Main JavaScript
// 전역 변수
let currentPage = 'project-registration';
let currentUser = {
name: '김그룹장',
role: '생산팀',
avatar: '김'
};
// 하드코딩된 데이터
const demoData = {
projects: [
{
jobNo: 'TK-2024-015',
name: 'ABC 공장 배관공사',
client: 'ABC 케미칼',
contractAmount: '150,000,000',
orderDate: '2024-01-15',
deliveryDate: '2024-03-30',
deliveryMethod: '현장납품',
productionType: '자체제작',
status: '승인완료'
}
],
processChart: {
design: { progress: 100, status: '완료', dueDate: '2024-02-15', responsible: '박설계' },
procurement: { progress: 85, status: '진행중', dueDate: '2024-02-28', responsible: '김구매' },
production: { progress: 60, status: '진행중', dueDate: '2024-03-20', responsible: '이생산' },
inspection: { progress: 0, status: '대기', dueDate: '2024-03-25', responsible: '최품질' },
delivery: { progress: 0, status: '대기', dueDate: '2024-03-30', responsible: '박PM' }
},
schedules: [
{
date: '09/20',
type: '외주출고',
title: '도장 작업 출고',
memo: 'A구역 파이프 20본, B구역 밸브 5개\n업체: 대한도장\n연락처: 010-1234-5678',
responsible: '김구매',
urgency: 'urgent'
},
{
date: '09/22',
type: '검사일정',
title: '압력시험',
memo: '시험압력: 15bar, 30분간 유지\n검사자: 최품질, 이생산',
responsible: '최품질',
urgency: 'normal'
}
],
deliveries: [
{
item: '파이프 4인치 x 50EA',
date: '입고완료 - 09/10',
status: 'completed'
},
{
item: '밸브 2인치 x 10EA',
date: '입고예정 - 09/16',
status: 'warning'
},
{
item: '엘보 4인치 x 20EA',
date: '지연 - 09/12 → 09/18',
status: 'delayed'
}
],
followUps: [
{
priority: '긴급',
title: '밸브 A 납기 지연 대응',
description: '주 공급업체 생산 지연으로 대체 업체 검토 필요',
responsible: '김구매',
registeredDate: '09/10',
expectedResolution: '09/17',
status: '진행중',
level: 'high'
},
{
priority: '높음',
title: '용접 검사 일정 조정',
description: '고객사 일정 변경으로 검사일 재조정 필요',
responsible: '최품질',
registeredDate: '09/12',
expectedResolution: '09/20',
status: '검토중',
level: 'medium'
}
],
purchaseOrders: [
{
poNumber: 'PO-2024-0156',
project: 'TK-2024-015',
item: '스테인리스 파이프 4인치 SCH40',
qty: 50,
unit: 'EA',
supplier: '대한파이프',
orderDate: '2024-09-01',
expectedDate: '2024-09-15',
buyer: '김구매',
status: 'pending'
},
{
poNumber: 'PO-2024-0157',
project: 'TK-2024-015',
item: '게이트밸브 2인치 150LB',
qty: 10,
unit: 'EA',
supplier: '코리아밸브',
orderDate: '2024-09-05',
expectedDate: '2024-09-16',
buyer: '김구매',
status: 'inspecting'
}
],
materials: [
{
item: '파이프 4인치 SCH40',
required: 50,
status: 'available',
location: 'A-3-상단',
availableQty: 45,
lastUpdate: '2024-09-14 09:00'
},
{
item: '엘보 4인치 150LB',
required: 20,
status: 'ordered',
currentStage: '구매 진행 중',
expectedDate: '2024-09-18',
availableQty: 0
},
{
item: '플랜지 4인치 150LB',
required: 15,
status: 'not-requested',
currentStage: '설계팀 구매 요청 대기',
availableQty: 0,
note: '설계 변경으로 사양 확정 대기'
},
{
item: '밸브 2인치 150LB',
required: 5,
status: 'ready',
location: 'B-2-중단',
availableQty: 5,
note: '인수 대기 중'
}
],
issues: [
{
time: '14:30',
type: '자재부족',
description: '엘보 4인치 10EA 부족으로 작업 중단',
urgency: '높음',
solution: '구매팀에 긴급 요청, 대체재 검토',
responsible: '김그룹장',
photos: ['issue_001.jpg']
},
{
time: '10:15',
type: '품질이슈',
description: '용접부 기공 발견, 재작업 필요',
urgency: '보통',
solution: '해당 부위 그라인딩 후 재용접',
responsible: '이용접사',
photos: ['quality_001.jpg']
}
]
};
// DOM이 로드되면 초기화
document.addEventListener('DOMContentLoaded', function() {
initializeApp();
setupEventListeners();
showPage(currentPage);
});
// 앱 초기화
function initializeApp() {
console.log('TK Project Demo 초기화 중...');
// 사용자 정보 업데이트
updateUserInfo();
// 데이터 로드
loadDemoData();
console.log('TK Project Demo 초기화 완료');
}
// 이벤트 리스너 설정
function setupEventListeners() {
// 네비게이션 클릭 이벤트
document.querySelectorAll('.nav-item').forEach(item => {
item.addEventListener('click', function(e) {
e.preventDefault();
const pageId = this.getAttribute('onclick').match(/'([^']+)'/)[1];
showPage(pageId);
});
});
// 진행률 슬라이더 이벤트
const progressSlider = document.querySelector('.progress-slider');
if (progressSlider) {
progressSlider.addEventListener('input', function() {
const value = this.value;
const display = document.querySelector('.progress-value');
if (display) {
display.textContent = value + '%';
}
});
}
// 버튼 클릭 이벤트
setupButtonEvents();
// 폼 이벤트
setupFormEvents();
}
// 버튼 이벤트 설정
function setupButtonEvents() {
// 프로젝트 승인 버튼
const approveBtn = document.querySelector('.btn-primary');
if (approveBtn && approveBtn.textContent.includes('프로젝트 승인')) {
approveBtn.addEventListener('click', function() {
showNotification('프로젝트가 성공적으로 승인되었습니다!', 'success');
});
}
// 검수 시작 버튼
document.querySelectorAll('.btn').forEach(btn => {
if (btn.textContent.includes('검수 시작')) {
btn.addEventListener('click', function() {
showNotification('검수 프로세스를 시작합니다.', 'info');
// 버튼 상태 변경
this.textContent = '🔄 검수 진행중';
this.classList.remove('btn-primary');
this.classList.add('btn-warning');
});
}
if (btn.textContent.includes('인수 처리')) {
btn.addEventListener('click', function() {
showNotification('자재 인수가 완료되었습니다.', 'success');
this.textContent = '✅ 인수완료';
this.classList.remove('btn-primary');
this.classList.add('btn-success');
this.disabled = true;
});
}
if (btn.textContent.includes('데일리 체크에 기록')) {
btn.addEventListener('click', function() {
showNotification('데일리 체크에 이슈가 등록되었습니다.', 'warning');
});
}
});
// 선반 클릭 이벤트
document.querySelectorAll('.shelf.available').forEach(shelf => {
shelf.addEventListener('click', function() {
// 기존 선택 해제
document.querySelectorAll('.shelf').forEach(s => s.classList.remove('selected'));
// 현재 선택
this.classList.add('selected');
// 선택된 위치 업데이트
const locationDisplay = document.querySelector('.selected-location .location');
if (locationDisplay) {
locationDisplay.textContent = this.textContent + '-중단';
}
showNotification(`${this.textContent} 선반이 선택되었습니다.`, 'info');
});
});
}
// 폼 이벤트 설정
function setupFormEvents() {
// BOM 조회 버튼
const bomSearchBtn = document.querySelector('.search-input .btn-primary');
if (bomSearchBtn && bomSearchBtn.textContent.includes('BOM 조회')) {
bomSearchBtn.addEventListener('click', function() {
const input = document.querySelector('.search-input .form-input');
const jobNo = input.value || 'TK-2024-015';
showNotification(`${jobNo} 프로젝트의 BOM 정보를 조회했습니다.`, 'success');
// 자재 리스트 업데이트 (이미 하드코딩되어 있음)
animateElements('.material-item');
});
}
}
// 페이지 표시
function showPage(pageId) {
// 모든 페이지 숨기기
document.querySelectorAll('.page').forEach(page => {
page.classList.remove('active');
});
// 모든 네비게이션 아이템 비활성화
document.querySelectorAll('.nav-item').forEach(item => {
item.classList.remove('active');
});
// 선택된 페이지 표시
const targetPage = document.getElementById(pageId);
if (targetPage) {
targetPage.classList.add('active');
targetPage.classList.add('fade-in');
// 네비게이션 아이템 활성화
const navItem = document.querySelector(`[onclick*="${pageId}"]`);
if (navItem) {
navItem.classList.add('active');
}
currentPage = pageId;
// 페이지별 초기화
initializePage(pageId);
}
}
// 페이지별 초기화
function initializePage(pageId) {
switch (pageId) {
case 'project-registration':
initializeProjectRegistration();
break;
case 'production-meeting':
initializeProductionMeeting();
break;
case 'incoming-inspection':
initializeIncomingInspection();
break;
case 'production-work':
initializeProductionWork();
break;
}
}
// 프로젝트 등록 페이지 초기화
function initializeProjectRegistration() {
console.log('프로젝트 등록 페이지 초기화');
// 자동 생성된 Job No. 애니메이션
const jobNoValue = document.querySelector('.generated-item .value');
if (jobNoValue) {
setTimeout(() => {
jobNoValue.style.transform = 'scale(1.1)';
setTimeout(() => {
jobNoValue.style.transform = 'scale(1)';
}, 200);
}, 500);
}
}
// 생산회의록 페이지 초기화
function initializeProductionMeeting() {
console.log('생산회의록 페이지 초기화');
// 공정표 애니메이션
animateElements('.process-item');
// 일정 및 배송 아이템 애니메이션
setTimeout(() => {
animateElements('.schedule-item');
animateElements('.delivery-item');
}, 300);
// Follow-up 아이템 애니메이션
setTimeout(() => {
animateElements('.followup-item');
}, 600);
}
// 입고 검수 페이지 초기화
function initializeIncomingInspection() {
console.log('입고 검수 페이지 초기화');
// 구매 아이템 애니메이션
animateElements('.purchase-item');
// 검수 단계 애니메이션
setTimeout(() => {
animateElements('.step');
}, 300);
// 창고 구역 애니메이션
setTimeout(() => {
animateElements('.zone-item');
}, 600);
}
// 생산팀 작업 페이지 초기화
function initializeProductionWork() {
console.log('생산팀 작업 페이지 초기화');
// 이슈 아이템 애니메이션
animateElements('.issue-item');
// 자재 아이템 애니메이션
setTimeout(() => {
animateElements('.material-item');
}, 300);
}
// 사용자 정보 업데이트
function updateUserInfo() {
const userAvatar = document.querySelector('.user-avatar');
const userName = document.querySelector('.user-name');
const userRole = document.querySelector('.user-role');
if (userAvatar) userAvatar.textContent = currentUser.avatar;
if (userName) userName.textContent = currentUser.name;
if (userRole) userRole.textContent = currentUser.role;
}
// 데모 데이터 로드
function loadDemoData() {
console.log('데모 데이터 로드 중...');
// 여기서 필요한 경우 동적으로 데이터를 DOM에 삽입할 수 있습니다.
// 현재는 HTML에 하드코딩되어 있으므로 추가 작업 불필요
console.log('데모 데이터 로드 완료');
}
// 요소 애니메이션
function animateElements(selector) {
const elements = document.querySelectorAll(selector);
elements.forEach((element, index) => {
setTimeout(() => {
element.classList.add('slide-in');
}, index * 100);
});
}
// 알림 표시
function showNotification(message, type = 'info') {
// 기존 알림 제거
const existingNotification = document.querySelector('.notification');
if (existingNotification) {
existingNotification.remove();
}
// 새 알림 생성
const notification = document.createElement('div');
notification.className = `notification alert alert-${type}`;
notification.textContent = message;
// 스타일 설정
notification.style.position = 'fixed';
notification.style.top = '20px';
notification.style.right = '20px';
notification.style.zIndex = '9999';
notification.style.minWidth = '300px';
notification.style.maxWidth = '500px';
notification.style.boxShadow = 'var(--dt-shadow-lg)';
notification.style.transform = 'translateX(100%)';
notification.style.transition = 'transform 0.3s ease';
// DOM에 추가
document.body.appendChild(notification);
// 애니메이션
setTimeout(() => {
notification.style.transform = 'translateX(0)';
}, 100);
// 자동 제거
setTimeout(() => {
notification.style.transform = 'translateX(100%)';
setTimeout(() => {
if (notification.parentNode) {
notification.remove();
}
}, 300);
}, 3000);
}
// 모달 관련 함수들
function showModal(title, content) {
let modal = document.querySelector('.modal-overlay');
if (!modal) {
modal = createModal();
document.body.appendChild(modal);
}
const modalTitle = modal.querySelector('.modal-title');
const modalBody = modal.querySelector('.modal-body');
modalTitle.textContent = title;
modalBody.innerHTML = content;
modal.classList.add('active');
}
function hideModal() {
const modal = document.querySelector('.modal-overlay');
if (modal) {
modal.classList.remove('active');
}
}
function createModal() {
const modal = document.createElement('div');
modal.className = 'modal-overlay';
modal.innerHTML = `
<div class="modal">
<div class="modal-header">
<h3 class="modal-title"></h3>
</div>
<div class="modal-body"></div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="hideModal()">닫기</button>
</div>
</div>
`;
// 오버레이 클릭시 모달 닫기
modal.addEventListener('click', function(e) {
if (e.target === modal) {
hideModal();
}
});
return modal;
}
// 유틸리티 함수들
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('ko-KR');
}
function formatNumber(number) {
return number.toLocaleString('ko-KR');
}
function formatCurrency(amount) {
return new Intl.NumberFormat('ko-KR', {
style: 'currency',
currency: 'KRW'
}).format(amount);
}
// 검색 기능
function searchItems(query, items, searchFields) {
if (!query) return items;
const lowercaseQuery = query.toLowerCase();
return items.filter(item => {
return searchFields.some(field => {
const value = item[field];
return value && value.toString().toLowerCase().includes(lowercaseQuery);
});
});
}
// 정렬 기능
function sortItems(items, field, direction = 'asc') {
return items.sort((a, b) => {
const aValue = a[field];
const bValue = b[field];
if (direction === 'asc') {
return aValue > bValue ? 1 : -1;
} else {
return aValue < bValue ? 1 : -1;
}
});
}
// 필터 기능
function filterItems(items, filters) {
return items.filter(item => {
return Object.entries(filters).every(([key, value]) => {
if (!value) return true;
return item[key] === value;
});
});
}
// 로컬 스토리지 관련
function saveToLocalStorage(key, data) {
try {
localStorage.setItem(key, JSON.stringify(data));
} catch (error) {
console.error('로컬 스토리지 저장 실패:', error);
}
}
function loadFromLocalStorage(key, defaultValue = null) {
try {
const data = localStorage.getItem(key);
return data ? JSON.parse(data) : defaultValue;
} catch (error) {
console.error('로컬 스토리지 로드 실패:', error);
return defaultValue;
}
}
// 키보드 단축키
document.addEventListener('keydown', function(e) {
// Ctrl + 숫자키로 페이지 전환
if (e.ctrlKey && e.key >= '1' && e.key <= '4') {
e.preventDefault();
const pages = ['project-registration', 'production-meeting', 'incoming-inspection', 'production-work'];
const pageIndex = parseInt(e.key) - 1;
if (pages[pageIndex]) {
showPage(pages[pageIndex]);
}
}
// ESC로 모달 닫기
if (e.key === 'Escape') {
hideModal();
}
});
// 전역 함수로 노출 (HTML onclick에서 사용)
window.showPage = showPage;
window.showModal = showModal;
window.hideModal = hideModal;
window.showNotification = showNotification;
console.log('TK Project Demo JavaScript 로드 완료');

1447
demo/styles/devonthink.css Normal file

File diff suppressed because it is too large Load Diff

455
demo/styles/main.css Normal file
View File

@@ -0,0 +1,455 @@
/* 기본 CSS - DevonThink 스타일과 함께 사용 */
/* 추가 유틸리티 클래스 */
.text-center { text-align: center; }
.text-left { text-align: left; }
.text-right { text-align: right; }
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: 8px; }
.mb-2 { margin-bottom: 16px; }
.mb-3 { margin-bottom: 24px; }
.mb-4 { margin-bottom: 32px; }
.mt-0 { margin-top: 0; }
.mt-1 { margin-top: 8px; }
.mt-2 { margin-top: 16px; }
.mt-3 { margin-top: 24px; }
.mt-4 { margin-top: 32px; }
.p-0 { padding: 0; }
.p-1 { padding: 8px; }
.p-2 { padding: 16px; }
.p-3 { padding: 24px; }
.p-4 { padding: 32px; }
.d-none { display: none; }
.d-block { display: block; }
.d-flex { display: flex; }
.d-grid { display: grid; }
.flex-1 { flex: 1; }
.flex-column { flex-direction: column; }
.align-items-center { align-items: center; }
.justify-content-center { justify-content: center; }
.justify-content-between { justify-content: space-between; }
.w-100 { width: 100%; }
.h-100 { height: 100%; }
/* 애니메이션 */
.fade-in {
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.slide-in {
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from { transform: translateX(-20px); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
/* 호버 효과 */
.hover-lift {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.hover-lift:hover {
transform: translateY(-2px);
box-shadow: var(--dt-shadow-lg);
}
/* 포커스 효과 */
.focus-ring:focus {
outline: none;
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.2);
}
/* 로딩 스피너 */
.spinner {
width: 20px;
height: 20px;
border: 2px solid var(--dt-gray-300);
border-top: 2px solid var(--dt-primary);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 툴팁 */
.tooltip {
position: relative;
cursor: help;
}
.tooltip::after {
content: attr(data-tooltip);
position: absolute;
bottom: 125%;
left: 50%;
transform: translateX(-50%);
background-color: var(--dt-gray-800);
color: white;
padding: 6px 12px;
border-radius: 4px;
font-size: 12px;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity 0.2s ease;
z-index: 1000;
}
.tooltip:hover::after {
opacity: 1;
}
/* 알림 배지 */
.notification-badge {
position: absolute;
top: -6px;
right: -6px;
background-color: var(--dt-danger);
color: white;
border-radius: 50%;
width: 16px;
height: 16px;
font-size: 10px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
}
/* 진행률 바 */
.progress-bar {
width: 100%;
height: 8px;
background-color: var(--dt-gray-200);
border-radius: 4px;
overflow: hidden;
}
.progress-bar-fill {
height: 100%;
background-color: var(--dt-primary);
transition: width 0.3s ease;
}
/* 드래그 앤 드롭 */
.drop-zone {
border: 2px dashed var(--dt-gray-300);
border-radius: 8px;
padding: 40px 20px;
text-align: center;
color: var(--dt-gray-600);
transition: all 0.2s ease;
}
.drop-zone.drag-over {
border-color: var(--dt-primary);
background-color: rgba(74, 144, 226, 0.05);
color: var(--dt-primary);
}
/* 테이블 */
.table {
width: 100%;
border-collapse: collapse;
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: var(--dt-shadow);
}
.table th,
.table td {
padding: 12px 16px;
text-align: left;
border-bottom: 1px solid var(--dt-gray-200);
}
.table th {
background-color: var(--dt-gray-50);
font-weight: 600;
color: var(--dt-gray-700);
font-size: 14px;
}
.table td {
font-size: 14px;
color: var(--dt-gray-800);
}
.table tr:hover {
background-color: var(--dt-gray-50);
}
/* 모달 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
}
.modal-overlay.active {
opacity: 1;
pointer-events: all;
}
.modal {
background-color: white;
border-radius: 8px;
box-shadow: var(--dt-shadow-lg);
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
transform: scale(0.9);
transition: transform 0.3s ease;
}
.modal-overlay.active .modal {
transform: scale(1);
}
.modal-header {
padding: 20px 24px 16px 24px;
border-bottom: 1px solid var(--dt-gray-200);
}
.modal-title {
font-size: 20px;
font-weight: 600;
color: var(--dt-gray-800);
margin: 0;
}
.modal-body {
padding: 24px;
}
.modal-footer {
padding: 16px 24px 20px 24px;
border-top: 1px solid var(--dt-gray-200);
display: flex;
justify-content: flex-end;
gap: 12px;
}
/* 탭 */
.tabs {
border-bottom: 1px solid var(--dt-gray-200);
margin-bottom: 24px;
}
.tab-list {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
.tab-item {
margin-right: 32px;
}
.tab-link {
display: block;
padding: 12px 0;
color: var(--dt-gray-600);
text-decoration: none;
font-weight: 500;
border-bottom: 2px solid transparent;
transition: all 0.2s ease;
}
.tab-link:hover {
color: var(--dt-gray-800);
}
.tab-link.active {
color: var(--dt-primary);
border-bottom-color: var(--dt-primary);
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
/* 아코디언 */
.accordion-item {
border: 1px solid var(--dt-gray-200);
border-radius: 6px;
margin-bottom: 8px;
overflow: hidden;
}
.accordion-header {
padding: 16px 20px;
background-color: var(--dt-gray-50);
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 600;
color: var(--dt-gray-800);
transition: background-color 0.2s ease;
}
.accordion-header:hover {
background-color: var(--dt-gray-100);
}
.accordion-header.active {
background-color: var(--dt-primary);
color: white;
}
.accordion-icon {
transition: transform 0.2s ease;
}
.accordion-header.active .accordion-icon {
transform: rotate(180deg);
}
.accordion-content {
padding: 0 20px;
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease, padding 0.3s ease;
}
.accordion-content.active {
padding: 20px;
max-height: 500px;
}
/* 알림 메시지 */
.alert {
padding: 12px 16px;
border-radius: 6px;
margin-bottom: 16px;
font-size: 14px;
border: 1px solid transparent;
}
.alert-success {
background-color: rgba(52, 168, 83, 0.1);
border-color: var(--dt-success);
color: var(--dt-success);
}
.alert-warning {
background-color: rgba(251, 188, 4, 0.1);
border-color: var(--dt-warning);
color: #B8860B;
}
.alert-danger {
background-color: rgba(234, 67, 53, 0.1);
border-color: var(--dt-danger);
color: var(--dt-danger);
}
.alert-info {
background-color: rgba(74, 144, 226, 0.1);
border-color: var(--dt-primary);
color: var(--dt-primary);
}
/* 빈 상태 */
.empty-state {
text-align: center;
padding: 60px 20px;
color: var(--dt-gray-500);
}
.empty-state-icon {
font-size: 48px;
margin-bottom: 16px;
opacity: 0.5;
}
.empty-state-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 8px;
color: var(--dt-gray-700);
}
.empty-state-description {
font-size: 14px;
margin-bottom: 24px;
}
/* 스크롤바 스타일링 */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--dt-gray-100);
}
::-webkit-scrollbar-thumb {
background: var(--dt-gray-400);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--dt-gray-500);
}
/* 인쇄 스타일 */
@media print {
.sidebar,
.btn,
.modal-overlay {
display: none !important;
}
.main-content {
width: 100% !important;
margin: 0 !important;
padding: 0 !important;
}
.page {
display: block !important;
padding: 0 !important;
}
.card {
box-shadow: none !important;
border: 1px solid #ccc !important;
margin-bottom: 20px !important;
}
}