feat:공통코드 글로벌→테넌트 체크박스 선택 및 일괄 복사 기능
This commit is contained in:
@@ -318,6 +318,100 @@ public function copy(Request $request, int $id): RedirectResponse|JsonResponse
|
||||
->with('success', '글로벌 코드가 테넌트용으로 복사되었습니다.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 글로벌 코드를 테넌트용으로 일괄 복사
|
||||
*/
|
||||
public function bulkCopy(Request $request): RedirectResponse|JsonResponse
|
||||
{
|
||||
$tenantId = session('selected_tenant_id');
|
||||
|
||||
if (! $tenantId) {
|
||||
if ($request->ajax()) {
|
||||
return response()->json(['error' => '테넌트를 먼저 선택해주세요.'], 400);
|
||||
}
|
||||
return redirect()->back()->with('error', '테넌트를 먼저 선택해주세요.');
|
||||
}
|
||||
|
||||
// JSON 문자열로 받은 경우 처리
|
||||
$idsJson = $request->input('ids_json');
|
||||
if ($idsJson) {
|
||||
$ids = json_decode($idsJson, true);
|
||||
if (! is_array($ids) || empty($ids)) {
|
||||
if ($request->ajax()) {
|
||||
return response()->json(['error' => '복사할 코드를 선택해주세요.'], 400);
|
||||
}
|
||||
return redirect()->back()->with('error', '복사할 코드를 선택해주세요.');
|
||||
}
|
||||
} else {
|
||||
$validated = $request->validate([
|
||||
'ids' => 'required|array|min:1',
|
||||
'ids.*' => 'integer',
|
||||
]);
|
||||
$ids = $validated['ids'];
|
||||
}
|
||||
$codeGroup = null;
|
||||
$copiedCount = 0;
|
||||
$skippedCount = 0;
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
foreach ($ids as $id) {
|
||||
$globalCode = CommonCode::whereNull('tenant_id')->find($id);
|
||||
if (! $globalCode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$codeGroup = $globalCode->code_group;
|
||||
|
||||
// 이미 복사된 코드가 있는지 확인
|
||||
$exists = CommonCode::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('code_group', $globalCode->code_group)
|
||||
->where('code', $globalCode->code)
|
||||
->exists();
|
||||
|
||||
if ($exists) {
|
||||
$skippedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 복사
|
||||
CommonCode::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'code_group' => $globalCode->code_group,
|
||||
'code' => $globalCode->code,
|
||||
'name' => $globalCode->name,
|
||||
'sort_order' => $globalCode->sort_order,
|
||||
'attributes' => $globalCode->attributes,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$copiedCount++;
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
if ($request->ajax()) {
|
||||
return response()->json(['error' => '복사 중 오류가 발생했습니다.'], 500);
|
||||
}
|
||||
return redirect()->back()->with('error', '복사 중 오류가 발생했습니다.');
|
||||
}
|
||||
|
||||
$message = "{$copiedCount}개 코드가 복사되었습니다.";
|
||||
if ($skippedCount > 0) {
|
||||
$message .= " ({$skippedCount}개는 이미 존재하여 건너뜀)";
|
||||
}
|
||||
|
||||
if ($request->ajax()) {
|
||||
return response()->json(['success' => true, 'message' => $message, 'copied' => $copiedCount, 'skipped' => $skippedCount]);
|
||||
}
|
||||
|
||||
return redirect()
|
||||
->route('common-codes.index', ['group' => $codeGroup ?? 'item_type'])
|
||||
->with('success', $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 코드 삭제 (테넌트 코드만)
|
||||
*/
|
||||
|
||||
@@ -92,14 +92,32 @@ class="px-4 py-3 text-sm font-medium border-b-2 whitespace-nowrap transition-col
|
||||
<h3 class="font-semibold text-gray-800">글로벌 코드</h3>
|
||||
<span class="text-xs text-gray-500">({{ $globalCodes->count() }})</span>
|
||||
</div>
|
||||
@if(!$isHQ)
|
||||
<span class="text-xs text-gray-400">본사만 편집 가능</span>
|
||||
@endif
|
||||
<div class="flex items-center gap-2">
|
||||
@if($globalCodes->count() > 0)
|
||||
<button type="button"
|
||||
id="bulkCopyBtn"
|
||||
onclick="bulkCopy()"
|
||||
class="hidden px-3 py-1.5 bg-green-600 hover:bg-green-700 text-white text-xs font-medium rounded-lg transition-colors flex items-center gap-1">
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<span id="bulkCopyCount">0</span>개 선택 복사
|
||||
</button>
|
||||
@endif
|
||||
@if(!$isHQ)
|
||||
<span class="text-xs text-gray-400">본사만 편집 가능</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-3 py-2 text-center w-10">
|
||||
<input type="checkbox" id="selectAllGlobal"
|
||||
onchange="toggleSelectAll(this)"
|
||||
class="w-4 h-4 text-green-600 border-gray-300 rounded focus:ring-green-500">
|
||||
</th>
|
||||
<th class="px-3 py-2 text-left text-xs font-medium text-gray-500">코드</th>
|
||||
<th class="px-3 py-2 text-left text-xs font-medium text-gray-500">이름</th>
|
||||
<th class="px-3 py-2 text-center text-xs font-medium text-gray-500 w-16">순서</th>
|
||||
@@ -110,6 +128,11 @@ class="px-4 py-3 text-sm font-medium border-b-2 whitespace-nowrap transition-col
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
@forelse($globalCodes as $code)
|
||||
<tr class="hover:bg-gray-50 {{ !$code->is_active ? 'opacity-50' : '' }}">
|
||||
<td class="px-3 py-2 text-center">
|
||||
<input type="checkbox" name="global_code_ids[]" value="{{ $code->id }}"
|
||||
onchange="updateBulkCopyButton()"
|
||||
class="global-code-checkbox w-4 h-4 text-green-600 border-gray-300 rounded focus:ring-green-500">
|
||||
</td>
|
||||
<td class="px-3 py-2">
|
||||
<span class="font-mono text-xs bg-gray-100 px-1.5 py-0.5 rounded">{{ $code->code }}</span>
|
||||
</td>
|
||||
@@ -163,7 +186,7 @@ class="p-1 text-gray-400 hover:text-red-600 transition-colors"
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="px-3 py-8 text-center text-gray-400">
|
||||
<td colspan="6" class="px-3 py-8 text-center text-gray-400">
|
||||
글로벌 코드가 없습니다.
|
||||
</td>
|
||||
</tr>
|
||||
@@ -365,6 +388,12 @@ class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm fon
|
||||
<form id="copyForm" method="POST" class="hidden">
|
||||
@csrf
|
||||
</form>
|
||||
|
||||
<!-- 일괄 복사 폼 (hidden) -->
|
||||
<form id="bulkCopyForm" action="{{ route('common-codes.bulk-copy') }}" method="POST" class="hidden">
|
||||
@csrf
|
||||
<input type="hidden" name="ids_json" id="bulkCopyIds">
|
||||
</form>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
@@ -426,6 +455,59 @@ function copyCode(id) {
|
||||
form.submit();
|
||||
}
|
||||
|
||||
// 전체 선택 토글
|
||||
function toggleSelectAll(checkbox) {
|
||||
const checkboxes = document.querySelectorAll('.global-code-checkbox');
|
||||
checkboxes.forEach(cb => cb.checked = checkbox.checked);
|
||||
updateBulkCopyButton();
|
||||
}
|
||||
|
||||
// 선택된 항목 수 업데이트 및 버튼 표시/숨김
|
||||
function updateBulkCopyButton() {
|
||||
const checkboxes = document.querySelectorAll('.global-code-checkbox:checked');
|
||||
const count = checkboxes.length;
|
||||
const btn = document.getElementById('bulkCopyBtn');
|
||||
const countSpan = document.getElementById('bulkCopyCount');
|
||||
|
||||
if (btn) {
|
||||
if (count > 0) {
|
||||
btn.classList.remove('hidden');
|
||||
btn.classList.add('flex');
|
||||
countSpan.textContent = count;
|
||||
} else {
|
||||
btn.classList.add('hidden');
|
||||
btn.classList.remove('flex');
|
||||
}
|
||||
}
|
||||
|
||||
// 전체 선택 체크박스 상태 업데이트
|
||||
const allCheckboxes = document.querySelectorAll('.global-code-checkbox');
|
||||
const selectAllCheckbox = document.getElementById('selectAllGlobal');
|
||||
if (selectAllCheckbox) {
|
||||
selectAllCheckbox.checked = allCheckboxes.length > 0 && checkboxes.length === allCheckboxes.length;
|
||||
selectAllCheckbox.indeterminate = checkboxes.length > 0 && checkboxes.length < allCheckboxes.length;
|
||||
}
|
||||
}
|
||||
|
||||
// 일괄 복사
|
||||
function bulkCopy() {
|
||||
const checkboxes = document.querySelectorAll('.global-code-checkbox:checked');
|
||||
const ids = Array.from(checkboxes).map(cb => cb.value);
|
||||
|
||||
if (ids.length === 0) {
|
||||
alert('복사할 코드를 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm(`선택한 ${ids.length}개 글로벌 코드를 테넌트용으로 복사하시겠습니까?`)) return;
|
||||
|
||||
// hidden form으로 POST
|
||||
const form = document.getElementById('bulkCopyForm');
|
||||
const idsInput = document.getElementById('bulkCopyIds');
|
||||
idsInput.value = JSON.stringify(ids);
|
||||
form.submit();
|
||||
}
|
||||
|
||||
// 코드 삭제
|
||||
function deleteCode(id, code) {
|
||||
if (!confirm(`'${code}' 코드를 삭제하시겠습니까?`)) return;
|
||||
|
||||
@@ -301,6 +301,7 @@
|
||||
Route::prefix('common-codes')->name('common-codes.')->group(function () {
|
||||
Route::get('/', [CommonCodeController::class, 'index'])->name('index');
|
||||
Route::post('/', [CommonCodeController::class, 'store'])->name('store');
|
||||
Route::post('/bulk-copy', [CommonCodeController::class, 'bulkCopy'])->name('bulk-copy');
|
||||
Route::put('/{id}', [CommonCodeController::class, 'update'])->name('update');
|
||||
Route::post('/{id}/toggle', [CommonCodeController::class, 'toggle'])->name('toggle');
|
||||
Route::post('/{id}/copy', [CommonCodeController::class, 'copy'])->name('copy');
|
||||
|
||||
Reference in New Issue
Block a user