- 테이블 헤더 스타일 통일 (menus, roles, permissions, boards 등) - 권한 매트릭스 체크박스/버튼 크기 20x20으로 표준화 - 스크럼 항목 추가/수정 모달 통합 (코드 중복 제거) - daily-logs API URL 경로 수정 (/pm/ 제거) - 타임존 Asia/Seoul로 변경 - flow-tester 액션 아이콘 크기 조정
207 lines
14 KiB
PHP
207 lines
14 KiB
PHP
<div class="bg-white rounded-lg shadow-sm overflow-hidden">
|
|
<table class="w-full">
|
|
<thead class="bg-gray-50 border-b">
|
|
<tr>
|
|
{{-- 체크박스 (가져오기 모드일 때만 표시) --}}
|
|
@if($importMode ?? false)
|
|
<th class="px-2 py-2 text-center text-sm font-semibold text-gray-700 uppercase tracking-wider w-10">
|
|
<input type="checkbox"
|
|
id="selectAllImport"
|
|
onchange="toggleSelectAllImport(this)"
|
|
class="w-4 h-4 rounded border-gray-300 text-green-600 focus:ring-green-500">
|
|
</th>
|
|
@else
|
|
<th class="px-2 py-2 text-center text-sm font-semibold text-gray-700 uppercase tracking-wider w-10"></th>
|
|
@endif
|
|
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700 uppercase tracking-wider whitespace-nowrap">No.</th>
|
|
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700 uppercase tracking-wider whitespace-nowrap">메뉴명</th>
|
|
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700 uppercase tracking-wider whitespace-nowrap">URL</th>
|
|
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700 uppercase tracking-wider whitespace-nowrap">정렬</th>
|
|
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700 uppercase tracking-wider whitespace-nowrap">활성</th>
|
|
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700 uppercase tracking-wider whitespace-nowrap">숨김</th>
|
|
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700 uppercase tracking-wider whitespace-nowrap">구분</th>
|
|
<th class="px-3 py-2 text-center text-sm font-semibold text-gray-700 uppercase tracking-wider whitespace-nowrap">작업</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="menu-sortable" class="bg-white divide-y divide-gray-200">
|
|
@forelse($menus as $menu)
|
|
<tr class="menu-row {{ $menu->deleted_at ? 'bg-red-50' : '' }} {{ ($importMode ?? false) && ($menu->is_imported ?? false) ? 'bg-gray-50 opacity-50' : 'hover:bg-gray-50' }}"
|
|
data-menu-id="{{ $menu->id }}"
|
|
data-parent-id="{{ $menu->parent_id ?? '' }}"
|
|
data-sort-order="{{ $menu->sort_order ?? 0 }}"
|
|
data-depth="{{ $menu->depth ?? 0 }}">
|
|
{{-- 체크박스 또는 드래그 핸들 --}}
|
|
@if($importMode ?? false)
|
|
<td class="px-2 py-2 whitespace-nowrap text-center">
|
|
@if($menu->is_imported ?? false)
|
|
<input type="checkbox"
|
|
value="{{ $menu->id }}"
|
|
disabled
|
|
class="import-checkbox w-4 h-4 rounded border-gray-300 text-gray-400 cursor-not-allowed">
|
|
@else
|
|
<input type="checkbox"
|
|
value="{{ $menu->id }}"
|
|
onchange="updateImportButtonState()"
|
|
class="import-checkbox w-4 h-4 rounded border-gray-300 text-green-600 focus:ring-green-500">
|
|
@endif
|
|
</td>
|
|
@else
|
|
<td class="px-2 py-2 whitespace-nowrap text-center">
|
|
@if(!$menu->deleted_at)
|
|
<span class="drag-handle cursor-grab active:cursor-grabbing text-gray-400 hover:text-gray-600">
|
|
<svg class="w-4 h-4 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8h16M4 16h16" />
|
|
</svg>
|
|
</span>
|
|
@endif
|
|
</td>
|
|
@endif
|
|
<td class="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
|
|
{{ $loop->iteration }}
|
|
</td>
|
|
<td class="px-3 py-2 whitespace-nowrap">
|
|
<div class="flex items-center gap-1.5" style="padding-left: {{ (($menu->depth ?? 0) * 1.25) }}rem;">
|
|
@if(($menu->depth ?? 0) > 0)
|
|
<span class="text-gray-300 text-xs font-mono flex-shrink-0">└─</span>
|
|
@endif
|
|
|
|
@if($menu->has_children)
|
|
<button type="button"
|
|
onclick="toggleChildren({{ $menu->id }})"
|
|
class="toggle-btn flex items-center text-blue-500 hover:text-blue-700 focus:outline-none"
|
|
data-menu-id="{{ $menu->id }}">
|
|
<svg class="w-4 h-4 transform transition-transform flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
|
|
</svg>
|
|
<svg class="w-3 h-3 ml-0.5 transform transition-transform chevron-icon" fill="currentColor" viewBox="0 0 20 20">
|
|
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/>
|
|
</svg>
|
|
</button>
|
|
@else
|
|
<svg class="w-4 h-4 text-gray-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
</svg>
|
|
@endif
|
|
|
|
<div class="flex items-center gap-1.5">
|
|
<span class="text-sm {{ ($menu->depth ?? 0) === 0 ? 'font-semibold text-gray-900' : 'font-medium text-gray-700' }}">
|
|
{{ $menu->name }}
|
|
</span>
|
|
@if($menu->is_external)
|
|
<span class="text-xs text-blue-600">(외부)</span>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="px-3 py-2 whitespace-nowrap text-sm text-gray-500">
|
|
@if($menu->is_external && $menu->external_url)
|
|
<a href="{{ $menu->external_url }}" target="_blank" class="text-blue-600 hover:underline">
|
|
{{ Str::limit($menu->external_url, 30) }}
|
|
</a>
|
|
@elseif($menu->url)
|
|
{{ $menu->url }}
|
|
@else
|
|
-
|
|
@endif
|
|
</td>
|
|
<td class="px-3 py-2 whitespace-nowrap text-sm text-gray-500 text-center">
|
|
{{ $menu->sort_order ?? 0 }}
|
|
</td>
|
|
<td class="px-3 py-2 whitespace-nowrap text-center">
|
|
@if($importMode ?? false)
|
|
@if($menu->is_active)
|
|
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-700">활성</span>
|
|
@else
|
|
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-500">비활성</span>
|
|
@endif
|
|
@elseif(!$menu->deleted_at)
|
|
<button type="button"
|
|
onclick="toggleActive({{ $menu->id }})"
|
|
class="relative inline-flex h-4 w-7 items-center rounded-full transition-colors focus:outline-none focus:ring-1 focus:ring-blue-500 focus:ring-offset-1 {{ $menu->is_active ? 'bg-blue-500' : 'bg-gray-400' }}">
|
|
<span class="inline-block h-3 w-3 transform rounded-full bg-white shadow-sm transition-transform {{ $menu->is_active ? 'translate-x-3.5' : 'translate-x-0.5' }}"></span>
|
|
</button>
|
|
@else
|
|
<span class="text-gray-400">-</span>
|
|
@endif
|
|
</td>
|
|
<td class="px-3 py-2 whitespace-nowrap text-center">
|
|
@if($importMode ?? false)
|
|
@if($menu->hidden)
|
|
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-amber-100 text-amber-700">숨김</span>
|
|
@else
|
|
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-500">표시</span>
|
|
@endif
|
|
@elseif(!$menu->deleted_at)
|
|
<button type="button"
|
|
onclick="toggleHidden({{ $menu->id }})"
|
|
class="relative inline-flex h-4 w-7 items-center rounded-full transition-colors focus:outline-none focus:ring-1 focus:ring-amber-500 focus:ring-offset-1 {{ $menu->hidden ? 'bg-amber-500' : 'bg-gray-400' }}">
|
|
<span class="inline-block h-3 w-3 transform rounded-full bg-white shadow-sm transition-transform {{ $menu->hidden ? 'translate-x-3.5' : 'translate-x-0.5' }}"></span>
|
|
</button>
|
|
@else
|
|
<span class="text-gray-400">-</span>
|
|
@endif
|
|
</td>
|
|
{{-- 구분 칸 --}}
|
|
<td class="px-3 py-2 whitespace-nowrap text-center">
|
|
@if($importMode ?? false)
|
|
@if($menu->is_imported ?? false)
|
|
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-500">가져옴</span>
|
|
@else
|
|
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-green-100 text-green-700">가능</span>
|
|
@endif
|
|
@elseif($menu->global_menu_id)
|
|
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-purple-100 text-purple-700">기본</span>
|
|
@else
|
|
<span class="text-gray-300">-</span>
|
|
@endif
|
|
</td>
|
|
{{-- 작업 칸 --}}
|
|
<td class="px-3 py-2 whitespace-nowrap text-center">
|
|
@if($importMode ?? false)
|
|
{{-- 가져오기 모드: 작업 없음 --}}
|
|
@elseif($menu->deleted_at)
|
|
<div class="flex items-center justify-center gap-1">
|
|
<button onclick="confirmRestore({{ $menu->id }}, '{{ $menu->name }}')"
|
|
class="inline-flex items-center px-2 py-1 text-xs font-medium rounded bg-green-100 text-green-700 hover:bg-green-200">
|
|
복원
|
|
</button>
|
|
@if(auth()->user()?->is_super_admin)
|
|
<button onclick="confirmForceDelete({{ $menu->id }}, '{{ $menu->name }}')"
|
|
class="inline-flex items-center px-2 py-1 text-xs font-medium rounded bg-red-100 text-red-700 hover:bg-red-200">
|
|
영구삭제
|
|
</button>
|
|
@endif
|
|
</div>
|
|
@else
|
|
<div class="flex items-center justify-center gap-1">
|
|
<a href="{{ route('menus.edit', $menu->id) }}"
|
|
class="inline-flex items-center px-2 py-1 text-xs font-medium rounded bg-blue-100 text-blue-700 hover:bg-blue-200">
|
|
수정
|
|
</a>
|
|
<button onclick="confirmDelete({{ $menu->id }}, '{{ $menu->name }}')"
|
|
class="inline-flex items-center px-2 py-1 text-xs font-medium rounded bg-red-100 text-red-700 hover:bg-red-200">
|
|
삭제
|
|
</button>
|
|
</div>
|
|
@endif
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="9" class="px-4 py-3 text-center text-gray-500">
|
|
메뉴가 없습니다.
|
|
</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- 페이지네이션 (일반 모드에서만 표시) -->
|
|
@if(!($importMode ?? false) && method_exists($menus, 'hasPages'))
|
|
@include('partials.pagination', [
|
|
'paginator' => $menus,
|
|
'target' => '#menu-table',
|
|
'includeForm' => '#filterForm'
|
|
])
|
|
@endif |