feat: 재고 보정 및 보정 내역 조회 기능 추가
- 재고 보정 모달 UI 추가 (약재, 로트, 수량 관리) - 재고 보정 내역 조회 모달 추가 - 재고 보정 상세 조회 모달 추가 - 자동 델타 계산 기능 (보정 전/후 비교) - 로트별 재고 선택 및 원산지별 그룹화 - 보정 유형: 감모/손실, 발견, 재고조사, 파손, 유통기한 경과 - API 데이터 형식 수정 (items → details) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b58e46f8fd
commit
724af5000a
360
static/app.js
360
static/app.js
@ -1467,6 +1467,366 @@ $(document).ready(function() {
|
||||
applyLedgerFilters();
|
||||
});
|
||||
|
||||
// ==================== 재고 보정 ====================
|
||||
|
||||
// 재고 보정 모달 열기
|
||||
$('#showStockAdjustmentBtn').on('click', function() {
|
||||
// 현재 날짜 설정
|
||||
$('#adjustmentDate').val(new Date().toISOString().split('T')[0]);
|
||||
$('#adjustmentItemsList').empty();
|
||||
$('#stockAdjustmentForm')[0].reset();
|
||||
$('#stockAdjustmentModal').modal('show');
|
||||
});
|
||||
|
||||
// 재고 보정 내역 모달 열기
|
||||
$('#showAdjustmentHistoryBtn').on('click', function() {
|
||||
loadAdjustmentHistory();
|
||||
});
|
||||
|
||||
// 재고 보정 내역 로드
|
||||
function loadAdjustmentHistory() {
|
||||
$.get('/api/stock-adjustments', function(response) {
|
||||
if (response.success) {
|
||||
const tbody = $('#adjustmentHistoryList');
|
||||
tbody.empty();
|
||||
|
||||
if (response.data.length === 0) {
|
||||
tbody.append(`
|
||||
<tr>
|
||||
<td colspan="7" class="text-center text-muted">보정 내역이 없습니다.</td>
|
||||
</tr>
|
||||
`);
|
||||
} else {
|
||||
response.data.forEach(adj => {
|
||||
// 보정 유형 한글 변환
|
||||
let typeLabel = '';
|
||||
switch(adj.adjustment_type) {
|
||||
case 'LOSS': typeLabel = '감모/손실'; break;
|
||||
case 'FOUND': typeLabel = '발견'; break;
|
||||
case 'RECOUNT': typeLabel = '재고조사'; break;
|
||||
case 'DAMAGE': typeLabel = '파손'; break;
|
||||
case 'EXPIRE': typeLabel = '유통기한 경과'; break;
|
||||
default: typeLabel = adj.adjustment_type;
|
||||
}
|
||||
|
||||
tbody.append(`
|
||||
<tr>
|
||||
<td>${adj.adjustment_date}</td>
|
||||
<td><code>${adj.adjustment_no}</code></td>
|
||||
<td><span class="badge bg-warning">${typeLabel}</span></td>
|
||||
<td>${adj.detail_count || 0}개</td>
|
||||
<td>${adj.created_by || '-'}</td>
|
||||
<td>${adj.notes || '-'}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-info view-adjustment-detail"
|
||||
data-id="${adj.adjustment_id}">
|
||||
<i class="bi bi-eye"></i> 상세
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
`);
|
||||
});
|
||||
|
||||
// 상세보기 버튼 이벤트
|
||||
$('.view-adjustment-detail').on('click', function() {
|
||||
const adjustmentId = $(this).data('id');
|
||||
viewAdjustmentDetail(adjustmentId);
|
||||
});
|
||||
}
|
||||
|
||||
$('#adjustmentHistoryModal').modal('show');
|
||||
}
|
||||
}).fail(function() {
|
||||
alert('보정 내역을 불러오는데 실패했습니다.');
|
||||
});
|
||||
}
|
||||
|
||||
// 재고 보정 상세 조회
|
||||
function viewAdjustmentDetail(adjustmentId) {
|
||||
$.get(`/api/stock-adjustments/${adjustmentId}`, function(response) {
|
||||
if (response.success) {
|
||||
const data = response.data;
|
||||
|
||||
// 보정 정보 표시
|
||||
$('#detailAdjustmentNo').text(data.adjustment_no);
|
||||
$('#detailAdjustmentDate').text(data.adjustment_date);
|
||||
|
||||
// 보정 유형 한글 변환
|
||||
let typeLabel = '';
|
||||
switch(data.adjustment_type) {
|
||||
case 'LOSS': typeLabel = '감모/손실'; break;
|
||||
case 'FOUND': typeLabel = '발견'; break;
|
||||
case 'RECOUNT': typeLabel = '재고조사'; break;
|
||||
case 'DAMAGE': typeLabel = '파손'; break;
|
||||
case 'EXPIRE': typeLabel = '유통기한 경과'; break;
|
||||
default: typeLabel = data.adjustment_type;
|
||||
}
|
||||
$('#detailAdjustmentType').html(`<span class="badge bg-warning">${typeLabel}</span>`);
|
||||
$('#detailAdjustmentCreatedBy').text(data.created_by || '-');
|
||||
$('#detailAdjustmentNotes').text(data.notes || '-');
|
||||
|
||||
// 보정 상세 항목 표시
|
||||
const itemsBody = $('#detailAdjustmentItems');
|
||||
itemsBody.empty();
|
||||
|
||||
if (data.details && data.details.length > 0) {
|
||||
data.details.forEach(item => {
|
||||
const delta = item.quantity_delta;
|
||||
let deltaHtml = '';
|
||||
if (delta > 0) {
|
||||
deltaHtml = `<span class="text-success">+${delta.toFixed(1)}g</span>`;
|
||||
} else if (delta < 0) {
|
||||
deltaHtml = `<span class="text-danger">${delta.toFixed(1)}g</span>`;
|
||||
} else {
|
||||
deltaHtml = '<span class="text-muted">0g</span>';
|
||||
}
|
||||
|
||||
itemsBody.append(`
|
||||
<tr>
|
||||
<td>${item.herb_name}</td>
|
||||
<td>${item.insurance_code || '-'}</td>
|
||||
<td>${item.origin_country || '-'}</td>
|
||||
<td>#${item.lot_id}</td>
|
||||
<td>${item.quantity_before.toFixed(1)}g</td>
|
||||
<td>${item.quantity_after.toFixed(1)}g</td>
|
||||
<td>${deltaHtml}</td>
|
||||
<td>${item.reason || '-'}</td>
|
||||
</tr>
|
||||
`);
|
||||
});
|
||||
}
|
||||
|
||||
// 보정 상세 모달 표시
|
||||
$('#adjustmentDetailModal').modal('show');
|
||||
}
|
||||
}).fail(function() {
|
||||
alert('보정 상세 정보를 불러오는데 실패했습니다.');
|
||||
});
|
||||
}
|
||||
|
||||
// 보정 대상 약재 추가
|
||||
let adjustmentItemCount = 0;
|
||||
$('#addAdjustmentItemBtn').on('click', function() {
|
||||
addAdjustmentItemRow();
|
||||
});
|
||||
|
||||
function addAdjustmentItemRow() {
|
||||
adjustmentItemCount++;
|
||||
const rowId = `adj-item-${adjustmentItemCount}`;
|
||||
|
||||
const newRow = $(`
|
||||
<tr data-row-id="${rowId}">
|
||||
<td>
|
||||
<select class="form-select form-select-sm adj-herb-select" required>
|
||||
<option value="">약재 선택</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select class="form-select form-select-sm adj-lot-select" disabled required>
|
||||
<option value="">약재 먼저 선택</option>
|
||||
</select>
|
||||
</td>
|
||||
<td class="before-qty text-end">-</td>
|
||||
<td>
|
||||
<input type="number" class="form-control form-control-sm after-qty-input"
|
||||
min="0" step="0.1" placeholder="0.0" required>
|
||||
</td>
|
||||
<td class="delta-qty text-end">-</td>
|
||||
<td>
|
||||
<input type="text" class="form-control form-control-sm reason-input"
|
||||
placeholder="사유">
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger remove-adj-item">
|
||||
<i class="bi bi-x"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
`);
|
||||
|
||||
$('#adjustmentItemsList').append(newRow);
|
||||
|
||||
// 약재 목록 로드
|
||||
loadHerbsForSelect(newRow.find('.adj-herb-select'));
|
||||
|
||||
// 약재 선택 이벤트
|
||||
newRow.find('.adj-herb-select').on('change', function() {
|
||||
const herbId = $(this).val();
|
||||
const row = $(this).closest('tr');
|
||||
|
||||
if (herbId) {
|
||||
loadLotsForAdjustment(herbId, row);
|
||||
} else {
|
||||
row.find('.adj-lot-select').empty().append('<option value="">약재 먼저 선택</option>').prop('disabled', true);
|
||||
row.find('.before-qty').text('-');
|
||||
row.find('.after-qty-input').val('');
|
||||
row.find('.delta-qty').text('-');
|
||||
}
|
||||
});
|
||||
|
||||
// 로트 선택 이벤트
|
||||
newRow.find('.adj-lot-select').on('change', function() {
|
||||
const selectedOption = $(this).find('option:selected');
|
||||
const row = $(this).closest('tr');
|
||||
|
||||
if (selectedOption.val()) {
|
||||
const beforeQty = parseFloat(selectedOption.data('qty')) || 0;
|
||||
row.find('.before-qty').text(beforeQty.toFixed(1) + 'g');
|
||||
row.data('before-qty', beforeQty);
|
||||
|
||||
// 기존 변경후 값이 있으면 델타 재계산
|
||||
const afterQty = parseFloat(row.find('.after-qty-input').val());
|
||||
if (!isNaN(afterQty)) {
|
||||
updateDelta(row, beforeQty, afterQty);
|
||||
}
|
||||
} else {
|
||||
row.find('.before-qty').text('-');
|
||||
row.find('.after-qty-input').val('');
|
||||
row.find('.delta-qty').text('-');
|
||||
}
|
||||
});
|
||||
|
||||
// 변경후 수량 입력 이벤트
|
||||
newRow.find('.after-qty-input').on('input', function() {
|
||||
const row = $(this).closest('tr');
|
||||
const beforeQty = row.data('before-qty') || 0;
|
||||
const afterQty = parseFloat($(this).val()) || 0;
|
||||
|
||||
updateDelta(row, beforeQty, afterQty);
|
||||
});
|
||||
|
||||
// 삭제 버튼
|
||||
newRow.find('.remove-adj-item').on('click', function() {
|
||||
$(this).closest('tr').remove();
|
||||
});
|
||||
}
|
||||
|
||||
// 약재별 로트 목록 로드
|
||||
function loadLotsForAdjustment(herbId, row) {
|
||||
$.get(`/api/inventory/detail/${herbId}`, function(response) {
|
||||
if (response.success) {
|
||||
const lotSelect = row.find('.adj-lot-select');
|
||||
lotSelect.empty();
|
||||
lotSelect.append('<option value="">로트/원산지 선택</option>');
|
||||
|
||||
const data = response.data;
|
||||
|
||||
// 원산지별로 로트 표시
|
||||
data.origins.forEach(origin => {
|
||||
const optgroup = $(`<optgroup label="${origin.origin_country}">`);
|
||||
|
||||
origin.lots.forEach(lot => {
|
||||
optgroup.append(`
|
||||
<option value="${lot.lot_id}"
|
||||
data-qty="${lot.quantity_onhand}"
|
||||
data-origin="${origin.origin_country}">
|
||||
로트#${lot.lot_id} - ${lot.quantity_onhand.toFixed(1)}g (${lot.received_date})
|
||||
</option>
|
||||
`);
|
||||
});
|
||||
|
||||
lotSelect.append(optgroup);
|
||||
});
|
||||
|
||||
lotSelect.prop('disabled', false);
|
||||
}
|
||||
}).fail(function() {
|
||||
alert('재고 정보를 불러오는데 실패했습니다.');
|
||||
});
|
||||
}
|
||||
|
||||
// 델타 계산 및 표시
|
||||
function updateDelta(row, beforeQty, afterQty) {
|
||||
const delta = afterQty - beforeQty;
|
||||
const deltaElement = row.find('.delta-qty');
|
||||
|
||||
if (delta > 0) {
|
||||
deltaElement.html(`<span class="text-success">+${delta.toFixed(1)}g</span>`);
|
||||
} else if (delta < 0) {
|
||||
deltaElement.html(`<span class="text-danger">${delta.toFixed(1)}g</span>`);
|
||||
} else {
|
||||
deltaElement.html('<span class="text-muted">0g</span>');
|
||||
}
|
||||
|
||||
row.data('delta', delta);
|
||||
}
|
||||
|
||||
// 재고 보정 저장 버튼
|
||||
$('#saveAdjustmentBtn').on('click', function() {
|
||||
saveStockAdjustment();
|
||||
});
|
||||
|
||||
// 재고 보정 저장
|
||||
$('#stockAdjustmentForm').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
saveStockAdjustment();
|
||||
});
|
||||
|
||||
function saveStockAdjustment() {
|
||||
const items = [];
|
||||
let hasError = false;
|
||||
|
||||
$('#adjustmentItemsList tr').each(function() {
|
||||
const herbId = $(this).find('.adj-herb-select').val();
|
||||
const lotId = $(this).find('.adj-lot-select').val();
|
||||
const beforeQty = $(this).data('before-qty');
|
||||
const afterQty = parseFloat($(this).find('.after-qty-input').val());
|
||||
const delta = $(this).data('delta');
|
||||
const reason = $(this).find('.reason-input').val();
|
||||
|
||||
if (!herbId || !lotId) {
|
||||
hasError = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
items.push({
|
||||
herb_item_id: parseInt(herbId),
|
||||
lot_id: parseInt(lotId),
|
||||
quantity_before: beforeQty,
|
||||
quantity_after: afterQty,
|
||||
quantity_delta: delta,
|
||||
reason: reason
|
||||
});
|
||||
});
|
||||
|
||||
if (hasError) {
|
||||
alert('모든 항목의 약재와 로트를 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (items.length === 0) {
|
||||
alert('보정할 항목을 추가해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
const adjustmentData = {
|
||||
adjustment_date: $('#adjustmentDate').val(),
|
||||
adjustment_type: $('#adjustmentType').val(),
|
||||
created_by: $('#adjustmentCreatedBy').val() || 'SYSTEM',
|
||||
notes: $('#adjustmentNotes').val(),
|
||||
details: items // API expects 'details', not 'items'
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: '/api/stock-adjustments',
|
||||
method: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(adjustmentData),
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
alert(`재고 보정이 완료되었습니다.\n보정번호: ${response.adjustment_no}\n항목 수: ${items.length}개`);
|
||||
$('#stockAdjustmentModal').modal('hide');
|
||||
|
||||
// 재고 목록 새로고침
|
||||
loadInventory();
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
alert('오류: ' + (xhr.responseJSON?.error || '재고 보정 실패'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function formatCurrency(amount) {
|
||||
if (amount === null || amount === undefined) return '0원';
|
||||
return new Intl.NumberFormat('ko-KR', {
|
||||
|
||||
@ -589,6 +589,9 @@
|
||||
<button class="btn btn-warning me-2" id="showStockAdjustmentBtn">
|
||||
<i class="bi bi-sliders"></i> 재고 보정
|
||||
</button>
|
||||
<button class="btn btn-outline-warning me-2" id="showAdjustmentHistoryBtn">
|
||||
<i class="bi bi-clock-history"></i> 보정 내역
|
||||
</button>
|
||||
<button class="btn btn-outline-info" id="showStockLedgerBtn">
|
||||
<i class="bi bi-journal-text"></i> 입출고 원장
|
||||
</button>
|
||||
@ -698,6 +701,200 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 재고 보정 모달 -->
|
||||
<div class="modal fade" id="stockAdjustmentModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-warning text-dark">
|
||||
<h5 class="modal-title"><i class="bi bi-sliders"></i> 재고 보정</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="stockAdjustmentForm">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">보정일자 *</label>
|
||||
<input type="date" class="form-control" id="adjustmentDate" required>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">보정 유형 *</label>
|
||||
<select class="form-control" id="adjustmentType" required>
|
||||
<option value="">선택하세요</option>
|
||||
<option value="LOSS">감모/손실</option>
|
||||
<option value="FOUND">발견</option>
|
||||
<option value="RECOUNT">재고조사</option>
|
||||
<option value="DAMAGE">파손</option>
|
||||
<option value="EXPIRE">유통기한 경과</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">담당자</label>
|
||||
<input type="text" class="form-control" id="adjustmentCreatedBy" placeholder="담당자 이름">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-12">
|
||||
<label class="form-label">비고</label>
|
||||
<textarea class="form-control" id="adjustmentNotes" rows="2" placeholder="보정 사유를 입력하세요"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6><i class="bi bi-list-check"></i> 보정 대상 약재</h6>
|
||||
<button type="button" class="btn btn-sm btn-primary" id="addAdjustmentItemBtn">
|
||||
<i class="bi bi-plus-circle"></i> 약재 추가
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th width="200">약재명</th>
|
||||
<th width="250">로트/원산지</th>
|
||||
<th width="100">보정 전 재고</th>
|
||||
<th width="100">보정 후 재고</th>
|
||||
<th width="80">증감량</th>
|
||||
<th>보정 사유</th>
|
||||
<th width="60">작업</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="adjustmentItemsList">
|
||||
<!-- Dynamic content -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info mt-3">
|
||||
<i class="bi bi-info-circle"></i>
|
||||
<strong>안내:</strong> 보정 전 재고는 현재 시스템 재고가 자동으로 표시됩니다.
|
||||
실사 재고를 "보정 후 재고"에 입력하면 증감량이 자동 계산됩니다.
|
||||
</div>
|
||||
</form>
|
||||
</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="saveAdjustmentBtn">
|
||||
<i class="bi bi-check-circle"></i> 보정 실행
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 재고 보정 내역 모달 -->
|
||||
<div class="modal fade" id="adjustmentHistoryModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-warning text-dark">
|
||||
<h5 class="modal-title"><i class="bi bi-clock-history"></i> 재고 보정 내역</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th width="120">보정일자</th>
|
||||
<th width="150">보정번호</th>
|
||||
<th width="100">보정 유형</th>
|
||||
<th width="80">항목 수</th>
|
||||
<th width="100">담당자</th>
|
||||
<th>비고</th>
|
||||
<th width="120">작업</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="adjustmentHistoryList">
|
||||
<!-- Dynamic content -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">닫기</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 재고 보정 상세 모달 -->
|
||||
<div class="modal fade" id="adjustmentDetailModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-info text-white">
|
||||
<h5 class="modal-title"><i class="bi bi-file-text"></i> 재고 보정 상세</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- 보정 헤더 정보 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">보정 정보</h6>
|
||||
<table class="table table-sm table-borderless">
|
||||
<tr>
|
||||
<th width="100">보정번호:</th>
|
||||
<td id="detailAdjustmentNo"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>보정일자:</th>
|
||||
<td id="detailAdjustmentDate"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>보정 유형:</th>
|
||||
<td id="detailAdjustmentType"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>담당자:</th>
|
||||
<td id="detailAdjustmentCreatedBy"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">비고</h6>
|
||||
<p id="detailAdjustmentNotes" class="mb-0"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 보정 상세 항목 -->
|
||||
<h6><i class="bi bi-list-check"></i> 보정 상세 내역</h6>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>약재명</th>
|
||||
<th>보험코드</th>
|
||||
<th>원산지</th>
|
||||
<th>로트ID</th>
|
||||
<th>보정 전</th>
|
||||
<th>보정 후</th>
|
||||
<th>증감량</th>
|
||||
<th>사유</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="detailAdjustmentItems">
|
||||
<!-- Dynamic content -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">닫기</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Herbs Page -->
|
||||
|
||||
Loading…
Reference in New Issue
Block a user