feat:동기화 페이지에 글로벌/테넌트 필터 추가

- 환경 탭 앞에 글로벌/테넌트 토글 버튼 추가
- 글로벌: tenant_id가 NULL인 코드/카테고리만 표시
- 테넌트: 현재 선택된 테넌트의 데이터만 표시
- Push/Pull API에 type 파라미터 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-29 01:06:33 +09:00
parent 50f9fa2fd3
commit 1e70d2edbf
4 changed files with 198 additions and 119 deletions

View File

@@ -29,12 +29,37 @@ class="px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white font-medium rounded-lg
</div>
</div>
<!-- 환경 선택 -->
<!-- 타입 선택 + 환경 선택 -->
<div class="bg-white rounded-lg shadow-sm mb-4">
<div class="border-b border-gray-200">
<nav class="flex -mb-px" aria-label="Tabs">
<nav class="flex items-center -mb-px" aria-label="Tabs">
<!-- 글로벌/테넌트 토글 -->
<div class="flex items-center border-r border-gray-200 px-4">
<a href="{{ route('common-codes.sync.index', ['env' => $selectedEnv, 'type' => 'global']) }}"
class="px-3 py-2 text-sm font-medium rounded-l-lg border transition-colors
{{ $selectedType === 'global'
? 'bg-purple-600 text-white border-purple-600'
: 'bg-white text-gray-600 border-gray-300 hover:bg-gray-50' }}">
<svg class="w-4 h-4 inline-block mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
글로벌
</a>
<a href="{{ route('common-codes.sync.index', ['env' => $selectedEnv, 'type' => 'tenant']) }}"
class="px-3 py-2 text-sm font-medium rounded-r-lg border border-l-0 transition-colors
{{ $selectedType === 'tenant'
? 'bg-blue-600 text-white border-blue-600'
: 'bg-white text-gray-600 border-gray-300 hover:bg-gray-50' }}">
<svg class="w-4 h-4 inline-block mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
테넌트
</a>
</div>
<!-- 환경 -->
@foreach($environments as $key => $env)
<a href="{{ route('common-codes.sync.index', ['env' => $key]) }}"
<a href="{{ route('common-codes.sync.index', ['env' => $key, 'type' => $selectedType]) }}"
class="px-6 py-3 text-sm font-medium border-b-2 transition-colors
{{ $selectedEnv === $key
? 'border-blue-500 text-blue-600'
@@ -275,6 +300,7 @@ class="w-4 h-4 text-purple-600 border-gray-300 rounded focus:ring-purple-500"
@push('scripts')
<script>
const selectedEnv = '{{ $selectedEnv }}';
const selectedType = '{{ $selectedType }}';
const csrfToken = '{{ csrf_token() }}';
// 전체 선택
@@ -311,8 +337,9 @@ function updateSelectedCount(side) {
}
const codeKeys = Array.from(checkboxes).map(cb => cb.value);
const typeLabel = selectedType === 'global' ? '글로벌' : '테넌트';
if (!confirm(`${codeKeys.length}개 코드를 ${selectedEnv === 'dev' ? '개발' : '운영'} 서버로 Push 하시겠습니까?`)) {
if (!confirm(`${codeKeys.length}개 ${typeLabel} 코드를 ${selectedEnv === 'dev' ? '개발' : '운영'} 서버로 Push 하시겠습니까?`)) {
return;
}
@@ -324,7 +351,7 @@ function updateSelectedCount(side) {
'X-CSRF-TOKEN': csrfToken,
'Accept': 'application/json'
},
body: JSON.stringify({ env: selectedEnv, code_keys: codeKeys })
body: JSON.stringify({ env: selectedEnv, type: selectedType, code_keys: codeKeys })
});
const result = await response.json();
@@ -348,8 +375,9 @@ function updateSelectedCount(side) {
}
const codeKeys = Array.from(checkboxes).map(cb => cb.value);
const typeLabel = selectedType === 'global' ? '글로벌' : '테넌트';
if (!confirm(`${codeKeys.length}개 코드를 로컬로 Pull 하시겠습니까?`)) {
if (!confirm(`${codeKeys.length}개 ${typeLabel} 코드를 로컬로 Pull 하시겠습니까?`)) {
return;
}
@@ -361,7 +389,7 @@ function updateSelectedCount(side) {
'X-CSRF-TOKEN': csrfToken,
'Accept': 'application/json'
},
body: JSON.stringify({ env: selectedEnv, code_keys: codeKeys })
body: JSON.stringify({ env: selectedEnv, type: selectedType, code_keys: codeKeys })
});
const result = await response.json();