Files
sam-manage/resources/views/tenants/index.blade.php
hskwon 5f50716d7f feat: Phase 4-2 역할 관리 시스템 구현 (HTMX + API 패턴)
- RoleService, RoleController (Blade/API) 생성
- 역할 CRUD 기능 완성 (목록/생성/수정/삭제)
- FormRequest 검증 (StoreRoleRequest, UpdateRoleRequest)
- HTMX 패턴 적용 (index, create, edit)
- 권한 선택 UI (체크박스, 전체 선택/해제)
- Tenant Selector 통합
- 레이아웃 패턴 문서화 (LAYOUT_PATTERN.md)
- Sidebar 메뉴에 역할 관리 추가
- Pagination partial 컴포넌트 추가
- Tenants 레이아웃 100% 폭으로 통일

주요 수정:
- UpdateRoleRequest 라우트 파라미터 수정 (role → id)
- RoleController permissions 조회 시 description 제거
- Conditional tenant filtering 적용
2025-11-24 16:36:02 +09:00

131 lines
4.8 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@extends('layouts.app')
@section('title', '테넌트 관리')
@section('content')
<!-- TENANT INDEX PAGE MARKER - 이것이 보이면 tenants/index.blade.php가 로드된 것입니다 -->
<!-- 페이지 헤더 -->
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold text-gray-800">🏢 테넌트 관리</h1>
<a href="{{ route('tenants.create') }}" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition">
+ 테넌트
</a>
</div>
<!-- 필터 영역 -->
<div class="bg-white rounded-lg shadow-sm p-4 mb-6">
<form id="filterForm" class="flex gap-4">
<!-- 검색 -->
<div class="flex-1">
<input type="text"
name="search"
placeholder="회사명, 코드, 이메일로 검색..."
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<!-- 상태 필터 -->
<div class="w-48">
<select name="tenant_st_code" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="">전체 상태</option>
<option value="trial">트라이얼</option>
<option value="active">활성</option>
<option value="suspended">정지</option>
<option value="expired">만료</option>
</select>
</div>
<!-- 삭제된 항목 포함 -->
<div class="w-48">
<select name="trashed" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="">활성만</option>
<option value="with">삭제 포함</option>
<option value="only">삭제만</option>
</select>
</div>
<!-- 검색 버튼 -->
<button type="submit" class="bg-gray-600 hover:bg-gray-700 text-white px-6 py-2 rounded-lg transition">
검색
</button>
</form>
</div>
<!-- 테이블 영역 (HTMX로 로드) -->
<div id="tenant-table"
hx-get="/api/admin/tenants"
hx-trigger="load, filterSubmit from:body"
hx-include="#filterForm"
hx-headers='{"X-CSRF-TOKEN": "{{ csrf_token() }}"}'
class="bg-white rounded-lg shadow-sm overflow-hidden">
<!-- 로딩 스피너 -->
<div class="flex justify-center items-center p-12">
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
</div>
</div>
@endsection
@push('scripts')
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
<script>
// 폼 제출 시 HTMX 이벤트 트리거
document.getElementById('filterForm').addEventListener('submit', function(e) {
e.preventDefault();
htmx.trigger('#tenant-table', 'filterSubmit');
});
// HTMX 응답 처리
document.body.addEventListener('htmx:afterSwap', function(event) {
if (event.detail.target.id === 'tenant-table') {
const response = JSON.parse(event.detail.xhr.response);
if (response.html) {
event.detail.target.innerHTML = response.html;
}
}
});
// 삭제 확인
window.confirmDelete = function(id, name) {
if (confirm(`"${name}" 테넌트를 삭제하시겠습니까?`)) {
htmx.ajax('DELETE', `/api/admin/tenants/${id}`, {
target: '#tenant-table',
swap: 'none',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
}
}).then(() => {
htmx.trigger('#tenant-table', 'filterSubmit');
});
}
};
// 복원 확인
window.confirmRestore = function(id, name) {
if (confirm(`"${name}" 테넌트를 복원하시겠습니까?`)) {
htmx.ajax('POST', `/api/admin/tenants/${id}/restore`, {
target: '#tenant-table',
swap: 'none',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
}
}).then(() => {
htmx.trigger('#tenant-table', 'filterSubmit');
});
}
};
// 영구삭제 확인
window.confirmForceDelete = function(id, name) {
if (confirm(`"${name}" 테넌트를 영구 삭제하시겠습니까?\n\n⚠ 이 작업은 되돌릴 수 없습니다!`)) {
htmx.ajax('DELETE', `/api/admin/tenants/${id}/force`, {
target: '#tenant-table',
swap: 'none',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
}
}).then(() => {
htmx.trigger('#tenant-table', 'filterSubmit');
});
}
};
</script>
@endpush