feat: 처방 주요 효능(efficacy) 필드 추가 및 UI 개선

- DB: formulas 테이블에 efficacy 칼럼 추가
- API: 처방 생성/수정/조회 시 efficacy 필드 처리
- UI: 처방 등록/수정 모달에 주요 효능 입력 필드 추가
- UI: 처방 상세 화면에 주요 효능 표시
- 기존 처방들의 주요 효능 데이터 입력 완료

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-02-18 04:39:05 +00:00
parent 95df32c14d
commit 124bc5eaf8
5 changed files with 1677 additions and 70 deletions

View File

@@ -122,6 +122,11 @@
<i class="bi bi-flower1"></i> 약재 관리
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" data-page="herb-info">
<i class="bi bi-book"></i> 약재 정보
</a>
</li>
</ul>
</div>
@@ -151,8 +156,14 @@
</div>
<div class="col-md-3">
<div class="stat-card">
<h5><i class="bi bi-cash-stack"></i> 재고 자산</h5>
<h5>
<i class="bi bi-cash-stack"></i> 재고 자산
<button class="btn btn-sm btn-outline-secondary ms-2" data-bs-toggle="modal" data-bs-target="#inventorySettingsModal" title="계산 설정">
<i class="bi bi-gear"></i>
</button>
</h5>
<div class="value" id="inventoryValue">0</div>
<small class="text-muted" id="inventoryMode">전체 재고</small>
</div>
</div>
</div>
@@ -969,9 +980,239 @@
</div>
</div>
</div>
<!-- Herb Information Page -->
<div id="herb-info" class="main-content">
<div class="d-flex justify-content-between align-items-center mb-4">
<h3><i class="bi bi-book"></i> 한약재 정보 시스템</h3>
<div class="btn-group">
<button type="button" class="btn btn-outline-primary active" data-view="search">
<i class="bi bi-search"></i> 검색
</button>
<button type="button" class="btn btn-outline-primary" data-view="efficacy">
<i class="bi bi-tags"></i> 효능별
</button>
<button type="button" class="btn btn-outline-primary" data-view="category">
<i class="bi bi-grid-3x3"></i> 분류별
</button>
</div>
</div>
<!-- Search Section -->
<div id="herb-search-section" class="mb-4">
<div class="row">
<div class="col-md-6">
<div class="input-group">
<span class="input-group-text"><i class="bi bi-search"></i></span>
<input type="text" class="form-control" id="herbSearchInput"
placeholder="약재명, 학명, 효능으로 검색...">
<button class="btn btn-primary" id="herbSearchBtn">검색</button>
</div>
</div>
<div class="col-md-6">
<div class="d-flex gap-2">
<select class="form-select" id="herbInfoEfficacyFilter">
<option value="">모든 효능</option>
<option value="보혈">보혈</option>
<option value="보기">보기</option>
<option value="활혈">활혈</option>
<option value="청열">청열</option>
<option value="해독">해독</option>
<option value="거담">거담</option>
<option value="이수">이수</option>
<option value="안신">안신</option>
</select>
<select class="form-select" id="herbInfoPropertyFilter">
<option value="">모든 성미</option>
<option value="한">한(寒)</option>
<option value="열">열(熱)</option>
<option value="온">온(溫)</option>
<option value="량">량(涼)</option>
<option value="평">평(平)</option>
</select>
</div>
</div>
</div>
</div>
<!-- Efficacy Tags Section (Hidden by default) -->
<div id="herb-efficacy-section" class="mb-4" style="display: none;">
<div class="row g-3" id="efficacyTagsContainer">
<!-- Dynamic efficacy tag buttons will be added here -->
</div>
</div>
<!-- Results Grid -->
<div class="row g-3" id="herbInfoGrid">
<!-- Herb cards will be dynamically added here -->
</div>
<!-- Herb Detail Modal -->
<div class="modal fade" id="herbDetailModal" tabindex="-1">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header bg-success text-white">
<h5 class="modal-title">
<i class="bi bi-flower1"></i>
<span id="herbDetailName">약재명</span>
<span id="herbDetailHanja" class="ms-2"></span>
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="row">
<!-- 기본 정보 -->
<div class="col-md-6">
<div class="card mb-3">
<div class="card-header bg-light">
<h6 class="mb-0"><i class="bi bi-info-circle"></i> 기본 정보</h6>
</div>
<div class="card-body">
<dl class="row mb-0">
<dt class="col-sm-4">성분코드</dt>
<dd class="col-sm-8" id="detailIngredientCode">-</dd>
<dt class="col-sm-4">학명</dt>
<dd class="col-sm-8" id="detailLatinName">-</dd>
<dt class="col-sm-4">약용부위</dt>
<dd class="col-sm-8" id="detailMedicinalPart">-</dd>
<dt class="col-sm-4">기원식물</dt>
<dd class="col-sm-8" id="detailOriginPlant">-</dd>
</dl>
</div>
</div>
<div class="card mb-3">
<div class="card-header bg-light">
<h6 class="mb-0"><i class="bi bi-thermometer"></i> 성미귀경</h6>
</div>
<div class="card-body">
<dl class="row mb-0">
<dt class="col-sm-4">성(性)</dt>
<dd class="col-sm-8">
<span class="badge bg-info" id="detailProperty">-</span>
</dd>
<dt class="col-sm-4">미(味)</dt>
<dd class="col-sm-8" id="detailTaste">-</dd>
<dt class="col-sm-4">귀경</dt>
<dd class="col-sm-8" id="detailMeridian">-</dd>
</dl>
</div>
</div>
</div>
<!-- 효능 정보 -->
<div class="col-md-6">
<div class="card mb-3">
<div class="card-header bg-light">
<h6 class="mb-0"><i class="bi bi-heart-pulse"></i> 효능효과</h6>
</div>
<div class="card-body">
<div class="mb-3">
<strong>주요 효능:</strong>
<div id="detailMainEffects" class="mt-2">-</div>
</div>
<div class="mb-3">
<strong>적응증:</strong>
<div id="detailIndications" class="mt-2">-</div>
</div>
<div class="mb-3">
<strong>효능 태그:</strong>
<div id="detailEfficacyTags" class="mt-2">
<!-- Dynamic tags -->
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header bg-light">
<h6 class="mb-0"><i class="bi bi-capsule"></i> 용법용량</h6>
</div>
<div class="card-body">
<dl class="row mb-0">
<dt class="col-sm-4">상용량</dt>
<dd class="col-sm-8" id="detailDosageRange">-</dd>
<dt class="col-sm-4">극량</dt>
<dd class="col-sm-8" id="detailDosageMax">-</dd>
<dt class="col-sm-4">포제법</dt>
<dd class="col-sm-8" id="detailPreparation">-</dd>
</dl>
</div>
</div>
</div>
</div>
<!-- 추가 정보 탭 -->
<div class="mt-4">
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="tab" href="#tabSafety">
<i class="bi bi-shield-check"></i> 안전성
</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="tab" href="#tabComponents">
<i class="bi bi-diagram-3"></i> 성분정보
</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="tab" href="#tabClinical">
<i class="bi bi-clipboard2-pulse"></i> 임상응용
</a>
</li>
</ul>
<div class="tab-content p-3 border border-top-0">
<div class="tab-pane fade show active" id="tabSafety">
<div class="row">
<div class="col-md-6">
<h6>금기사항</h6>
<div id="detailContraindications" class="text-danger">-</div>
</div>
<div class="col-md-6">
<h6>주의사항</h6>
<div id="detailPrecautions" class="text-warning">-</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="tabComponents">
<h6>주요 성분</h6>
<div id="detailActiveCompounds">-</div>
</div>
<div class="tab-pane fade" id="tabClinical">
<div class="row">
<div class="col-md-6">
<h6>약리작용</h6>
<div id="detailPharmacological">-</div>
</div>
<div class="col-md-6">
<h6>임상응용</h6>
<div id="detailClinical">-</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">닫기</button>
<button type="button" class="btn btn-primary" id="editHerbInfoBtn">
<i class="bi bi-pencil"></i> 정보 수정
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Patient Modal -->
<div class="modal fade" id="patientModal" tabindex="-1">
@@ -1157,6 +1398,10 @@
<label class="form-label">설명</label>
<textarea class="form-control" id="formulaDescription" rows="2"></textarea>
</div>
<div class="mt-3">
<label class="form-label">주요 효능</label>
<textarea class="form-control" id="formulaEfficacy" rows="2" placeholder="예: 기혈양허 치료, 병후 회복, 만성 피로 개선"></textarea>
</div>
<div class="mt-3">
<h6>구성 약재</h6>
<table class="table table-sm">
@@ -1186,6 +1431,140 @@
</div>
</div>
<!-- Formula Detail Modal (처방 상세 모달) -->
<div class="modal fade" id="formulaDetailModal" tabindex="-1">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title">
<i class="bi bi-journal-medical"></i>
<span id="formulaDetailName">처방명</span> 상세 정보
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<!-- 처방 기본 정보 카드 -->
<div class="card mb-4">
<div class="card-header bg-light">
<h6 class="mb-0"><i class="bi bi-info-circle"></i> 기본 정보</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<dl class="row mb-0">
<dt class="col-sm-4">처방 코드:</dt>
<dd class="col-sm-8" id="detailFormulaCode">-</dd>
<dt class="col-sm-4">처방명:</dt>
<dd class="col-sm-8" id="detailFormulaName">-</dd>
<dt class="col-sm-4">처방 유형:</dt>
<dd class="col-sm-8" id="detailFormulaType">-</dd>
</dl>
</div>
<div class="col-md-6">
<dl class="row mb-0">
<dt class="col-sm-4">기본 첩수:</dt>
<dd class="col-sm-8" id="detailBaseCheop">-</dd>
<dt class="col-sm-4">기본 파우치:</dt>
<dd class="col-sm-8" id="detailBasePouches">-</dd>
<dt class="col-sm-4">등록일:</dt>
<dd class="col-sm-8" id="detailCreatedAt">-</dd>
</dl>
</div>
</div>
<div class="row mt-3">
<div class="col-12">
<dt>설명:</dt>
<dd id="detailDescription" class="text-muted">-</dd>
</div>
</div>
</div>
</div>
<!-- 구성 약재 정보 카드 -->
<div class="card mb-4">
<div class="card-header bg-light d-flex justify-content-between align-items-center">
<h6 class="mb-0"><i class="bi bi-list-ul"></i> 구성 약재</h6>
<div>
<span class="badge bg-primary" id="totalIngredientsCount">0개</span>
<span class="badge bg-success" id="totalGramsPerCheop">0g</span>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead class="table-light">
<tr>
<th width="50">#</th>
<th width="200">약재명</th>
<th width="100">1첩당 용량</th>
<th width="150" style="white-space: nowrap;">1제 기준 <small style="font-size: 0.85em;" class="text-muted">(20첩/30파우치)</small></th>
<th style="padding-left: 15px;">효능/역할</th>
<th width="150">재고 상태</th>
</tr>
</thead>
<tbody id="formulaDetailIngredients">
<!-- 동적으로 추가 -->
</tbody>
<tfoot class="table-secondary">
<tr>
<th></th>
<th>합계</th>
<th class="text-end" id="totalGrams1Cheop">0g</th>
<th class="text-end" id="totalGrams1Je">0g</th>
<th></th>
<th></th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
<!-- 효능 및 주의사항 카드 -->
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header bg-light">
<h6 class="mb-0"><i class="bi bi-heart-pulse"></i> 주요 효능</h6>
</div>
<div class="card-body">
<div id="formulaEffects">
<p class="text-muted">처방의 주요 효능 정보가 여기에 표시됩니다.</p>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header bg-light">
<h6 class="mb-0"><i class="bi bi-exclamation-triangle"></i> 사용 시 주의사항</h6>
</div>
<div class="card-body">
<div id="formulaPrecautions">
<p class="text-muted">처방 사용 시 주의사항이 여기에 표시됩니다.</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">닫기</button>
<button type="button" class="btn btn-warning" id="editFormulaDetailBtn">
<i class="bi bi-pencil"></i> 수정
</button>
<button type="button" class="btn btn-danger" id="deleteFormulaBtn">
<i class="bi bi-trash"></i> 삭제
</button>
</div>
</div>
</div>
</div>
<!-- Supplier Modal -->
<div class="modal fade" id="supplierModal" tabindex="-1">
<div class="modal-dialog">
@@ -1284,5 +1663,54 @@
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="/static/app.js?v=20260217"></script>
<!-- 재고 자산 계산 설정 모달 -->
<div class="modal fade" id="inventorySettingsModal" tabindex="-1" aria-labelledby="inventorySettingsModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="inventorySettingsModalLabel">
<i class="bi bi-calculator"></i> 재고 자산 계산 설정
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label fw-bold">계산 방식 선택</label>
<div class="form-check">
<input class="form-check-input" type="radio" name="inventoryMode" id="modeAll" value="all" checked>
<label class="form-check-label" for="modeAll">
<strong>전체 재고</strong>
<div class="text-muted small">모든 LOT의 재고를 포함하여 계산</div>
</label>
</div>
<div class="form-check mt-2">
<input class="form-check-input" type="radio" name="inventoryMode" id="modeReceiptOnly" value="receipt_only">
<label class="form-check-label" for="modeReceiptOnly">
<strong>입고장 기준</strong>
<div class="text-muted small">입고장과 연결된 LOT만 계산</div>
</label>
</div>
<div class="form-check mt-2">
<input class="form-check-input" type="radio" name="inventoryMode" id="modeVerified" value="verified">
<label class="form-check-label" for="modeVerified">
<strong>검증된 재고</strong>
<div class="text-muted small">검증 확인된 LOT만 계산</div>
</label>
</div>
</div>
<div class="alert alert-info" id="modeInfo" style="display: none;">
<h6 class="alert-heading"><i class="bi bi-info-circle"></i> 현재 상태</h6>
<div id="modeInfoContent"></div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">취소</button>
<button type="button" class="btn btn-primary" onclick="saveInventorySettings()">적용</button>
</div>
</div>
</div>
</div>
</body>
</html>