Files
sam-manage/resources/views/users/partials/modal-info.blade.php
kent 049fa7ed61 feat: [users] 슈퍼관리자 보호 기능 구현
- 일반관리자가 슈퍼관리자 수정/삭제 불가
- API Controller: update/destroy에서 403 반환
- Web Controller: edit에서 403 abort
- FormRequest: is_super_admin 필드 강제/유지 처리
- View: 테이블, 모달, 생성/수정 폼에서 버튼/체크박스 숨김

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-30 23:10:07 +09:00

210 lines
11 KiB
PHP

{{-- 사용자 모달 - 기본 정보 --}}
<div class="p-6">
{{-- 삭제된 사용자 경고 배너 --}}
@if($user->deleted_at)
<div class="mb-4 p-4 bg-red-50 border border-red-200 rounded-lg">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<svg class="w-6 h-6 text-red-600 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<div>
<p class="text-sm font-semibold text-red-800"> 사용자는 삭제되었습니다.</p>
<p class="text-xs text-red-600 mt-0.5">
삭제일: {{ $user->deleted_at->format('Y-m-d H:i') }}
@if($user->deletedByUser)
· 삭제자: {{ $user->deletedByUser->name }}
@endif
</p>
</div>
</div>
@if(auth()->user()?->is_super_admin)
<button type="button"
onclick="event.stopPropagation(); UserModal.restoreUser()"
class="px-3 py-1.5 text-sm font-medium text-white bg-green-600 rounded-lg hover:bg-green-700 transition-colors">
복원하기
</button>
@endif
</div>
</div>
@endif
{{-- 상단: 기본 정보 카드 --}}
<div class="bg-gray-50 rounded-lg p-4 mb-6">
<div class="flex gap-4">
{{-- 좌측: 프로필 이미지/아이콘 --}}
<div class="flex-shrink-0">
@if($user->profile_photo_path)
<img src="{{ asset('storage/' . $user->profile_photo_path) }}"
alt="{{ $user->name }}"
class="w-20 h-20 rounded-full object-cover">
@else
<div class="w-20 h-20 bg-blue-100 rounded-full flex items-center justify-center">
<span class="text-2xl font-bold text-blue-600">
{{ strtoupper(substr($user->name, 0, 1)) }}
</span>
</div>
@endif
</div>
{{-- 우측: 정보 테이블 --}}
<div class="flex-1">
<div class="mb-2">
<h3 id="user-modal-name" class="text-xl font-bold text-gray-900">{{ $user->name }}</h3>
@if($user->is_super_admin && auth()->user()?->is_super_admin)
<span class="text-xs text-red-600 font-semibold">슈퍼 관리자</span>
@endif
</div>
<table class="w-full text-sm">
<tbody>
<tr>
<td class="py-1 pr-4 text-gray-500 w-24">사용자 ID</td>
<td class="py-1 font-medium text-gray-900">{{ $user->user_id ?? '-' }}</td>
<td class="py-1 pr-4 text-gray-500 w-24">이메일</td>
<td class="py-1 text-gray-900">{{ $user->email }}</td>
</tr>
<tr>
<td class="py-1 pr-4 text-gray-500">연락처</td>
<td class="py-1 text-gray-900">{{ $user->phone ?? '-' }}</td>
<td class="py-1 pr-4 text-gray-500">상태</td>
<td class="py-1">
@if($user->is_active)
<span class="px-2 py-0.5 text-xs font-semibold rounded-full bg-green-100 text-green-800">활성</span>
@else
<span class="px-2 py-0.5 text-xs font-semibold rounded-full bg-gray-100 text-gray-800">비활성</span>
@endif
</td>
</tr>
<tr>
<td class="py-1 pr-4 text-gray-500">가입일</td>
<td class="py-1 text-gray-900">{{ $user->created_at?->format('Y-m-d') ?? '-' }}</td>
<td class="py-1 pr-4 text-gray-500">최근 로그인</td>
<td class="py-1 text-gray-900">{{ $user->last_login_at?->format('Y-m-d H:i') ?? '-' }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
{{-- 2x2 그리드: 테넌트, 역할, 부서, 권한 --}}
<div class="grid grid-cols-2 gap-4 mb-6">
{{-- 소속 테넌트 --}}
<div class="bg-gray-50 rounded-lg p-3">
<h4 class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">소속 테넌트</h4>
@if($user->tenants && $user->tenants->count() > 0)
<div class="flex flex-wrap gap-1.5">
@foreach($user->tenants as $tenant)
<span class="px-2 py-1 text-xs rounded cursor-pointer hover:ring-1 hover:ring-blue-400 {{ $tenant->pivot->is_default ? 'bg-blue-100 text-blue-800 font-medium' : 'bg-white text-gray-700 border border-gray-200' }}"
data-context-menu="tenant"
data-entity-id="{{ $tenant->id }}"
data-entity-name="{{ $tenant->company_name }}"
title="우클릭하여 메뉴 열기">
{{ $tenant->company_name }}
@if($tenant->pivot->is_default)
<span class="text-[10px]">(기본)</span>
@endif
</span>
@endforeach
</div>
@else
<p class="text-xs text-gray-400">소속된 테넌트 없음</p>
@endif
</div>
{{-- 역할 정보 --}}
<div class="bg-gray-50 rounded-lg p-3">
<h4 class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">역할</h4>
@if($user->userRoles && $user->userRoles->count() > 0)
<div class="flex flex-wrap gap-1.5">
@foreach($user->userRoles as $userRole)
@if($userRole->role)
<span class="px-2 py-1 text-xs rounded {{ $userRole->role->guard_name === 'web' ? 'bg-blue-100 text-blue-700' : 'bg-purple-100 text-purple-700' }}"
title="{{ $userRole->role->description }}">
{{ $userRole->role->name }}
</span>
@endif
@endforeach
</div>
@else
<p class="text-xs text-gray-400">할당된 역할 없음</p>
@endif
</div>
{{-- 부서 정보 --}}
<div class="bg-gray-50 rounded-lg p-3">
<h4 class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">부서</h4>
@if($user->departmentUsers && $user->departmentUsers->count() > 0)
<div class="flex flex-wrap gap-1.5">
@foreach($user->departmentUsers as $departmentUser)
@if($departmentUser->department)
<span class="px-2 py-1 text-xs rounded {{ $departmentUser->is_primary ? 'bg-green-100 text-green-800 font-medium' : 'bg-white text-gray-700 border border-gray-200' }}">
{{ $departmentUser->department->name }}
@if($departmentUser->is_primary)
<span class="text-[10px]">()</span>
@endif
</span>
@endif
@endforeach
</div>
@else
<p class="text-xs text-gray-400">소속된 부서 없음</p>
@endif
</div>
{{-- 권한 정보 --}}
<div class="bg-gray-50 rounded-lg p-3">
<h4 class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">권한</h4>
@if(isset($user->web_permission_count) || isset($user->api_permission_count))
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="flex items-center gap-1">
<span class="px-1.5 py-0.5 text-[10px] font-medium bg-blue-100 text-blue-700 rounded">Web</span>
<span class="text-sm font-semibold text-gray-900">{{ $user->web_permission_count ?? 0 }}</span>
</div>
<div class="flex items-center gap-1">
<span class="px-1.5 py-0.5 text-[10px] font-medium bg-green-100 text-green-700 rounded">API</span>
<span class="text-sm font-semibold text-gray-900">{{ $user->api_permission_count ?? 0 }}</span>
</div>
</div>
<a href="{{ route('user-permissions.index') }}?user_id={{ $user->id }}"
class="text-xs text-blue-600 hover:text-blue-800 hover:underline">
관리
</a>
</div>
@else
<p class="text-xs text-gray-400">테넌트 선택 필요</p>
@endif
</div>
</div>
{{-- 하단 버튼 --}}
@php
// 슈퍼관리자 보호: 일반관리자가 슈퍼관리자를 수정/삭제할 수 없음
$canModify = ! $user->is_super_admin || auth()->user()?->is_super_admin;
@endphp
<div class="flex justify-end gap-2 mt-6 pt-4 border-t border-gray-200">
<button type="button"
onclick="UserModal.close()"
class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50">
닫기
</button>
@if($canModify)
@if(!$user->deleted_at)
<button type="button"
onclick="UserModal.deleteUser()"
class="px-4 py-2 text-sm font-medium text-white bg-red-600 rounded-lg hover:bg-red-700">
삭제
</button>
@endif
<button type="button"
onclick="UserModal.goToEdit()"
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700">
수정
</button>
@else
<span class="px-4 py-2 text-sm text-gray-400">슈퍼관리자는 수정할 없습니다</span>
@endif
</div>
</div>