Feat: UI/UX организаций как в назначениях
✅ AJAX добавление/удаление пользователей ✅ AJAX добавление/удаление групп ✅ Modal редактирования организации ✅ Фильтрация GroupSearchController для организаций ✅ Счётчики обновляются без перезагрузки Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
7f19cedeee
commit
de64be24eb
|
|
@ -62,14 +62,124 @@ class OrganizationController extends Controller
|
|||
|
||||
return view('admin.organizations.show', compact('organization'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Добавить пользователя в организацию
|
||||
*/
|
||||
public function addUser(Organization $organization, Request $request)
|
||||
{
|
||||
Gate::authorize('update', $organization);
|
||||
|
||||
$validated = $request->validate([
|
||||
'user_ids' => 'nullable|string',
|
||||
]);
|
||||
|
||||
if (!empty($validated['user_ids'])) {
|
||||
$userIds = array_map('intval', array_filter(explode(',', $validated['user_ids'])));
|
||||
|
||||
foreach ($userIds as $userId) {
|
||||
$user = User::find($userId);
|
||||
if (!$user) continue;
|
||||
|
||||
// Проверка: не состоит ли уже в организации
|
||||
if ($user->organization_id === $organization->id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$user->update(['organization_id' => $organization->id]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->ajax()) {
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
return back()->with('success', 'Пользователи добавлены в организацию.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить пользователя из организации
|
||||
*/
|
||||
public function removeUser(Organization $organization, User $user)
|
||||
{
|
||||
Gate::authorize('update', $organization);
|
||||
|
||||
if ($user->organization_id === $organization->id) {
|
||||
$user->update(['organization_id' => null]);
|
||||
}
|
||||
|
||||
if ($request->ajax()) {
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
return back()->with('success', 'Пользователь удалён из организации.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавить группу в организацию
|
||||
*/
|
||||
public function addGroup(Organization $organization, Request $request)
|
||||
{
|
||||
Gate::authorize('update', $organization);
|
||||
|
||||
$validated = $request->validate([
|
||||
'group_ids' => 'nullable|string',
|
||||
]);
|
||||
|
||||
if (!empty($validated['group_ids'])) {
|
||||
$groupIds = array_map('intval', array_filter(explode(',', $validated['group_ids'])));
|
||||
|
||||
foreach ($groupIds as $groupId) {
|
||||
$group = Group::find($groupId);
|
||||
if (!$group) continue;
|
||||
|
||||
// Проверка: не состоит ли уже в организации
|
||||
if ($group->organization_id === $organization->id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$group->update(['organization_id' => $organization->id]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->ajax()) {
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
return back()->with('success', 'Группы добавлены в организацию.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить группу из организации
|
||||
*/
|
||||
public function removeGroup(Organization $organization, Group $group)
|
||||
{
|
||||
Gate::authorize('update', $organization);
|
||||
|
||||
if ($group->organization_id === $organization->id) {
|
||||
$group->update(['organization_id' => null]);
|
||||
}
|
||||
|
||||
if ($request->ajax()) {
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
return back()->with('success', 'Группа удалена из организации.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Редактирование организации (modal)
|
||||
*/
|
||||
public function edit(Organization $organization)
|
||||
{
|
||||
Gate::authorize('update', $organization);
|
||||
|
||||
return view('admin.organizations.edit', compact('organization'));
|
||||
return redirect()->route('admin.organizations.show', $organization)->with('edit', true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Обновление организации
|
||||
*/
|
||||
public function update(Request $request, Organization $organization)
|
||||
{
|
||||
Gate::authorize('update', $organization);
|
||||
|
|
@ -82,12 +192,16 @@ class OrganizationController extends Controller
|
|||
'phone' => 'nullable|string|max:20',
|
||||
'email' => 'nullable|email|max:255',
|
||||
'description' => 'nullable|string',
|
||||
'is_active' => 'boolean',
|
||||
]);
|
||||
|
||||
$validated['is_active'] = $request->boolean('is_active');
|
||||
|
||||
$organization->update($validated);
|
||||
|
||||
// Для AJAX запросов
|
||||
if ($request->ajax()) {
|
||||
return response()->json(['success' => true, 'organization' => $organization]);
|
||||
}
|
||||
|
||||
return redirect()->route('admin.organizations.show', $organization)
|
||||
->with('success', 'Организация успешно обновлена.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,17 @@ class GroupSearchController extends Controller
|
|||
{
|
||||
$query = $request->get('q', '');
|
||||
$userId = $request->get('user_id', null);
|
||||
|
||||
$organizationId = $request->get('organization_id', null);
|
||||
|
||||
$groupsQuery = Group::query()->with('organization');
|
||||
|
||||
// Если указан organization_id - показываем только общие группы или группы из других организаций
|
||||
if ($organizationId) {
|
||||
$groupsQuery->where(function($q) use ($organizationId) {
|
||||
$q->whereNull('organization_id')
|
||||
->orWhere('organization_id', '!=', $organizationId);
|
||||
});
|
||||
}
|
||||
|
||||
// Если указан user_id - фильтруем по доступным группам
|
||||
if ($userId) {
|
||||
|
|
@ -31,12 +40,12 @@ class GroupSearchController extends Controller
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Если запрос не пустой - фильтруем по названию
|
||||
if (!empty(trim($query))) {
|
||||
$groupsQuery->where('name', 'like', "%{$query}%");
|
||||
}
|
||||
|
||||
|
||||
$groups = $groupsQuery
|
||||
->orderBy('name')
|
||||
->limit(50)
|
||||
|
|
@ -47,7 +56,7 @@ class GroupSearchController extends Controller
|
|||
'text' => $group->name . ($group->organization ? " ({$group->organization->name})" : ' (Общая)'),
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
return response()->json($groups);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,182 +1,118 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('title', $organization->name)
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<nav class="col-md-3 col-lg-2 d-md-block sidebar collapse">
|
||||
<div class="position-sticky pt-3">
|
||||
@include('partials._sidebar')
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<nav class="col-md-3 col-lg-2 d-md-block sidebar"><div class="position-sticky pt-3">@include('partials._sidebar')</div></nav>
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4 main-content">
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<div class="d-flex justify-content-between align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">{{ $organization->name }}</h1>
|
||||
<div class="btn-toolbar">
|
||||
@can('update', $organization)
|
||||
<a href="{{ route('admin.organizations.edit', $organization) }}" class="btn btn-warning btn-sm me-2">
|
||||
<div>
|
||||
<button class="btn btn-warning btn-sm me-2" data-bs-toggle="modal" data-bs-target="#editOrganizationModal">
|
||||
<i class="bi bi-pencil"></i> Редактировать
|
||||
</a>
|
||||
@endcan
|
||||
<a href="{{ route('admin.organizations.index') }}" class="btn btn-secondary btn-sm">
|
||||
<i class="bi bi-arrow-left"></i> Назад
|
||||
</a>
|
||||
</button>
|
||||
<a href="{{ route('admin.organizations.index') }}" class="btn btn-secondary btn-sm">Назад</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">
|
||||
<i class="bi bi-check-circle"></i> {{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0"><i class="bi bi-info-circle"></i> Информация</h5>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-primary text-white"><h5 class="mb-0">Информация</h5></div>
|
||||
<div class="card-body">
|
||||
<table class="table table-sm">
|
||||
<tr>
|
||||
<th width="40%">Название:</th>
|
||||
<td>{{ $organization->name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>ИНН/КПП:</th>
|
||||
<td>{{ $organization->inn ?? '—' }} / {{ $organization->kpp ?? '—' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Адрес:</th>
|
||||
<td>{{ $organization->address ?? '—' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Email:</th>
|
||||
<td>{{ $organization->email ?? '—' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Телефон:</th>
|
||||
<td>{{ $organization->phone ?? '—' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Статус:</th>
|
||||
<td>
|
||||
@if($organization->is_active)
|
||||
<span class="badge bg-success">Активна</span>
|
||||
@else
|
||||
<span class="badge bg-secondary">Не активна</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Описание:</th>
|
||||
<td>{{ $organization->description ?? '—' }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div><strong>ИНН/КПП:</strong> {{ $organization->inn ?? '—' }} / {{ $organization->kpp ?? '—' }}</div>
|
||||
<div><strong>Адрес:</strong> {{ $organization->address ?? '—' }}</div>
|
||||
<div><strong>Телефон:</strong> {{ $organization->phone ?? '—' }}</div>
|
||||
<div><strong>Email:</strong> {{ $organization->email ?? '—' }}</div>
|
||||
<div><strong>Статус:</strong>
|
||||
@if($organization->is_active)
|
||||
<span class="badge bg-success">Активна</span>
|
||||
@else
|
||||
<span class="badge bg-secondary">Не активна</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h5 class="mb-0"><i class="bi bi-graph-up"></i> Статистика</h5>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-success text-white"><h5 class="mb-0">Статистика</h5></div>
|
||||
<div class="card-body">
|
||||
<div class="row text-center">
|
||||
<div class="col-4 mb-3">
|
||||
<div class="display-4 text-primary">{{ $organization->users->count() }}</div>
|
||||
<div class="text-muted">Пользователей</div>
|
||||
<div class="col-6 mb-3">
|
||||
<div class="display-6 text-success" id="users-count">{{ $organization->users->count() }}</div>
|
||||
<div class="text-muted"><i class="bi bi-people"></i> Пользователей</div>
|
||||
</div>
|
||||
<div class="col-4 mb-3">
|
||||
<div class="display-4 text-success">{{ $organization->groups->count() }}</div>
|
||||
<div class="text-muted">Групп</div>
|
||||
</div>
|
||||
<div class="col-4 mb-3">
|
||||
<div class="display-4 text-info">{{ $organization->courseRequests->count() }}</div>
|
||||
<div class="text-muted">Заявок</div>
|
||||
<div class="col-6 mb-3">
|
||||
<div class="display-6 text-info" id="groups-count">{{ $organization->groups->count() }}</div>
|
||||
<div class="text-muted"><i class="bi bi-people-fill"></i> Групп</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header bg-success text-white d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0"><i class="bi bi-people"></i> Пользователи</h5>
|
||||
<a href="{{ route('admin.users.index', ['organization_id' => $organization->id]) }}" class="btn btn-sm btn-primary">
|
||||
Все пользователи <i class="bi bi-arrow-right"></i>
|
||||
</a>
|
||||
<button class="btn btn-sm btn-light" data-bs-toggle="modal" data-bs-target="#addUserModal"><i class="bi bi-plus-lg"></i></button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if($organization->users->count() > 0)
|
||||
<ul class="list-group list-group-flush">
|
||||
@foreach($organization->users->take(5) as $user)
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<a href="{{ route('admin.users.show', $user) }}" class="text-decoration-none">{{ $user->name }}</a>
|
||||
<small class="text-muted">{{ $user->email }}</small>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@if($organization->users->count() > 5)
|
||||
<div class="mt-2 text-muted small">
|
||||
+ ещё {{ $organization->users->count() - 5 }} пользователей
|
||||
<div class="card-body p-0">
|
||||
<div id="users-list">
|
||||
@if($organization->users->count() > 0)
|
||||
<ul class="list-group list-group-flush" id="users-ul">
|
||||
@foreach($organization->users as $user)
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center" id="user-li-{{ $user->id }}">
|
||||
<a href="{{ route('admin.users.show', $user) }}">{{ $user->name }} <small class="text-muted">({{ $user->email }})</small></a>
|
||||
<button onclick="removeUser({{ $user->id }})" class="btn btn-sm btn-outline-danger"><i class="bi bi-x-lg"></i></button>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@else
|
||||
<p class="text-muted text-center py-4" id="users-empty">Нет пользователей</p>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@else
|
||||
<p class="text-muted mb-0">Нет пользователей</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header bg-info text-white d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0"><i class="bi bi-people-fill"></i> Группы</h5>
|
||||
@can('create', App\Models\Group::class)
|
||||
<a href="{{ route('admin.organizations.groups.create', $organization) }}" class="btn btn-sm btn-primary">
|
||||
<i class="bi bi-plus-lg"></i>
|
||||
</a>
|
||||
@endcan
|
||||
<button class="btn btn-sm btn-light" data-bs-toggle="modal" data-bs-target="#addGroupModal"><i class="bi bi-plus-lg"></i></button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if($organization->groups->count() > 0)
|
||||
<ul class="list-group list-group-flush">
|
||||
@foreach($organization->groups->take(5) as $group)
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<strong>{{ $group->name }}</strong>
|
||||
@if($group->description)
|
||||
<br><small class="text-muted">{{ Str::limit($group->description, 50) }}</small>
|
||||
@endif
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<a href="{{ route('admin.organizations.groups.show', [$organization, $group]) }}" class="btn btn-outline-primary" title="Просмотр">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
@can('update', $group)
|
||||
<a href="{{ route('admin.organizations.groups.edit', [$organization, $group]) }}" class="btn btn-outline-warning" title="Редактировать">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
@endcan
|
||||
</div>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@if($organization->groups->count() > 5)
|
||||
<div class="mt-2 text-muted small">
|
||||
+ ещё {{ $organization->groups->count() - 5 }} групп
|
||||
<div class="card-body p-0">
|
||||
<div id="groups-list">
|
||||
@if($organization->groups->count() > 0)
|
||||
<ul class="list-group list-group-flush" id="groups-ul">
|
||||
@foreach($organization->groups as $group)
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center" id="group-li-{{ $group->id }}">
|
||||
<a href="{{ route('admin.groups.show', $group) }}">{{ $group->name }}</a>
|
||||
<button onclick="removeGroup({{ $group->id }})" class="btn btn-sm btn-outline-danger"><i class="bi bi-x-lg"></i></button>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@else
|
||||
<p class="text-muted text-center py-4" id="groups-empty">Нет групп</p>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@else
|
||||
<p class="text-muted mb-0">Нет групп</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header"><h5 class="mb-0">Ссылки</h5></div>
|
||||
<div class="card-body">
|
||||
<a href="{{ route('admin.users.index', ['organization_id' => $organization->id]) }}" class="btn btn-outline-primary">
|
||||
<i class="bi bi-people"></i> Все пользователи организации
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -184,4 +120,257 @@
|
|||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal добавления пользователя -->
|
||||
<div class="modal fade" id="addUserModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form id="addUserForm">
|
||||
@csrf
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Добавить пользователя</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<x-tags-input
|
||||
name="user_ids"
|
||||
url="{{ route('api.users.search') }}"
|
||||
placeholder="Начните вводить имя (пользователи без организации)..."
|
||||
badge_color="success"
|
||||
/>
|
||||
<small class="text-muted">Можно добавить пользователей без организации</small>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||||
<button type="submit" class="btn btn-success">Добавить</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal добавления группы -->
|
||||
<div class="modal fade" id="addGroupModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form id="addGroupForm">
|
||||
@csrf
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Добавить группу</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<x-tags-input
|
||||
name="group_ids"
|
||||
url="{{ route('api.groups.search') }}"
|
||||
placeholder="Начните вводить название (общие группы)..."
|
||||
badge_color="info"
|
||||
/>
|
||||
<small class="text-muted">Можно добавить общие группы (без организации)</small>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||||
<button type="submit" class="btn btn-info">Добавить</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal редактирования организации -->
|
||||
<div class="modal fade" id="editOrganizationModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<form id="editOrganizationForm">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Редактировать организацию</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Название *</label>
|
||||
<input type="text" name="name" class="form-control" value="{{ $organization->name }}" required>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">ИНН</label>
|
||||
<input type="text" name="inn" class="form-control" value="{{ $organization->inn }}">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">КПП</label>
|
||||
<input type="text" name="kpp" class="form-control" value="{{ $organization->kpp }}">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Телефон</label>
|
||||
<input type="text" name="phone" class="form-control" value="{{ $organization->phone }}">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Email</label>
|
||||
<input type="email" name="email" class="form-control" value="{{ $organization->email }}">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Адрес</label>
|
||||
<input type="text" name="address" class="form-control" value="{{ $organization->address }}">
|
||||
</div>
|
||||
<div class="col-md-12 mb-3">
|
||||
<label class="form-label">Описание</label>
|
||||
<textarea name="description" class="form-control" rows="3">{{ $organization->description }}</textarea>
|
||||
</div>
|
||||
<div class="col-md-12 mb-3">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" name="is_active" value="1" class="form-check-input" {{ $organization->is_active ? 'checked' : '' }}>
|
||||
<label class="form-check-label">Активна</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||||
<button type="submit" class="btn btn-warning">Сохранить</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
// AJAX добавление пользователя
|
||||
document.getElementById('addUserForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(this);
|
||||
|
||||
fetch('/admin/organizations/{{ $organization->id }}/users/add', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
}
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
});
|
||||
|
||||
// AJAX удаление пользователя
|
||||
function removeUser(userId) {
|
||||
if (!confirm('Удалить пользователя из организации?')) return;
|
||||
|
||||
fetch('/admin/organizations/{{ $organization->id }}/users/' + userId + '/remove', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const li = document.getElementById('user-li-' + userId);
|
||||
if (li) li.remove();
|
||||
|
||||
const countEl = document.getElementById('users-count');
|
||||
if (countEl) countEl.textContent = parseInt(countEl.textContent) - 1;
|
||||
|
||||
const ul = document.getElementById('users-ul');
|
||||
const empty = document.getElementById('users-empty');
|
||||
if (ul && ul.children.length === 0 && empty) {
|
||||
empty.style.display = 'block';
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
}
|
||||
|
||||
// AJAX добавление группы
|
||||
document.getElementById('addGroupForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(this);
|
||||
|
||||
fetch('/admin/organizations/{{ $organization->id }}/groups/add', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
}
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
});
|
||||
|
||||
// AJAX удаление группы
|
||||
function removeGroup(groupId) {
|
||||
if (!confirm('Удалить группу из организации?')) return;
|
||||
|
||||
fetch('/admin/organizations/{{ $organization->id }}/groups/' + groupId + '/remove', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const li = document.getElementById('group-li-' + groupId);
|
||||
if (li) li.remove();
|
||||
|
||||
const countEl = document.getElementById('groups-count');
|
||||
if (countEl) countEl.textContent = parseInt(countEl.textContent) - 1;
|
||||
|
||||
const ul = document.getElementById('groups-ul');
|
||||
const empty = document.getElementById('groups-empty');
|
||||
if (ul && ul.children.length === 0 && empty) {
|
||||
empty.style.display = 'block';
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
}
|
||||
|
||||
// AJAX редактирование организации
|
||||
document.getElementById('editOrganizationForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(this);
|
||||
|
||||
fetch('/admin/organizations/{{ $organization->id }}', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'X-HTTP-Method-Override': 'PUT'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
}
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
});
|
||||
|
||||
// Автооткрытие modal редактирования
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
@if(session('edit'))
|
||||
const editModal = new bootstrap.Modal(document.getElementById('editOrganizationModal'));
|
||||
editModal.show();
|
||||
@endif
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
|
|
|
|||
|
|
@ -45,8 +45,14 @@ Route::middleware('auth')->group(function () {
|
|||
|
||||
// Администрирование
|
||||
Route::prefix('admin')->name('admin.')->group(function () {
|
||||
Route::resource('organizations', OrganizationController::class);
|
||||
Route::resource('organizations', OrganizationController::class)->except(['edit', 'update']);
|
||||
Route::resource('organizations.groups', GroupController::class);
|
||||
Route::get('/organizations/{organization}/edit', [OrganizationController::class, 'edit'])->name('organizations.edit');
|
||||
Route::put('/organizations/{organization}', [OrganizationController::class, 'update'])->name('organizations.update');
|
||||
Route::post('/organizations/{organization}/users/add', [OrganizationController::class, 'addUser'])->name('organizations.users.add');
|
||||
Route::delete('/organizations/{organization}/users/{user}/remove', [OrganizationController::class, 'removeUser'])->name('organizations.users.remove');
|
||||
Route::post('/organizations/{organization}/groups/add', [OrganizationController::class, 'addGroup'])->name('organizations.groups.add');
|
||||
Route::delete('/organizations/{organization}/groups/{group}/remove', [OrganizationController::class, 'removeGroup'])->name('organizations.groups.remove');
|
||||
Route::resource('users', UserController::class);
|
||||
Route::resource('course-categories', CourseCategoryController::class);
|
||||
Route::resource('courses', CourseController::class);
|
||||
|
|
|
|||
Loading…
Reference in New Issue