Feat: Заявки — Blade шаблоны (create, show, edit)
✅ create.blade.php — создание с элементами ✅ show.blade.php — просмотр с статистикой ✅ edit.blade.php — редактирование элементов ✅ Кнопки approve/reject на show Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
87f20ef702
commit
7ad06e85fa
|
|
@ -0,0 +1,170 @@
|
|||
@extends('layouts.app')
|
||||
@section('title', 'Создать заявку')
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<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 align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">Создать заявку</h1>
|
||||
<a href="{{ route('admin.course-requests.index') }}" class="btn btn-secondary btn-sm">Назад</a>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('admin.course-requests.store') }}" method="POST" id="createRequestForm">
|
||||
@csrf
|
||||
<div class="row">
|
||||
<div class="col-md-8 mb-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-primary text-white"><h5 class="mb-0">Курсы и получатели</h5></div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Организация *</label>
|
||||
<x-searchable-select
|
||||
name="organization_id"
|
||||
url="{{ route('api.organizations.search') }}"
|
||||
placeholder="Начните вводить название организации..."
|
||||
:required="true"
|
||||
/>
|
||||
@error('organization_id')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<h6 class="mb-3">Элементы заявки</h6>
|
||||
<div id="items-container">
|
||||
<div class="item-row border rounded p-3 mb-3 bg-light">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Курс *</label>
|
||||
<x-searchable-select
|
||||
name="items[0][course_id]"
|
||||
url="{{ route('api.courses.search') }}"
|
||||
placeholder="Выберите курс..."
|
||||
:required="true"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Пользователь</label>
|
||||
<x-tags-input
|
||||
name="items[0][user_ids]"
|
||||
url="{{ route('api.users.search') }}"
|
||||
placeholder="Или выберите пользователей..."
|
||||
badge_color="success"
|
||||
/>
|
||||
<small class="text-muted">Или</small>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Группа</label>
|
||||
<x-tags-input
|
||||
name="items[0][group_ids]"
|
||||
url="{{ route('api.groups.search') }}"
|
||||
placeholder="Или выберите группы..."
|
||||
badge_color="info"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Дата начала *</label>
|
||||
<input type="date" name="items[0][start_date]" class="form-control form-control-sm" required>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Дата окончания</label>
|
||||
<input type="date" name="items[0][end_date]" class="form-control form-control-sm">
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger mt-2 remove-item" style="display:none;"><i class="bi bi-trash"></i> Удалить</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" id="add-item-btn">
|
||||
<i class="bi bi-plus-lg"></i> Добавить элемент
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 mb-4">
|
||||
<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="mb-3">
|
||||
<label class="form-label">Статус</label>
|
||||
<select name="status" class="form-select">
|
||||
<option value="pending">Ожидает подтверждения</option>
|
||||
@if(auth()->user()->hasRole(['Administrator', 'Manager', 'Curator']))
|
||||
<option value="approved" selected>Одобрена (создать назначения сразу)</option>
|
||||
@endif
|
||||
</select>
|
||||
<small class="text-muted">Admin/Manager/Curator могут создать сразу одобренную заявку</small>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Заметка</label>
|
||||
<textarea name="note" class="form-control" rows="4">{{ old('note') }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Создать заявку</button>
|
||||
<a href="{{ route('admin.course-requests.index') }}" class="btn btn-secondary">Отмена</a>
|
||||
</form>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
let itemCount = 1;
|
||||
|
||||
document.getElementById('add-item-btn').addEventListener('click', function() {
|
||||
const container = document.getElementById('items-container');
|
||||
const newItem = document.createElement('div');
|
||||
newItem.className = 'item-row border rounded p-3 mb-3 bg-light';
|
||||
newItem.innerHTML = `
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Курс *</label>
|
||||
<select name="items[${itemCount}][course_id]" class="form-select form-select-sm" required>
|
||||
<option value="">Выберите курс</option>
|
||||
@foreach($courses as $id => $title)
|
||||
<option value="{{ $id }}">{{ $title }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Пользователь</label>
|
||||
<input type="text" class="form-control form-control-sm" placeholder="ID пользователей (через запятую)" name="items[${itemCount}][user_ids]">
|
||||
<small class="text-muted">Или</small>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Группа</label>
|
||||
<input type="text" class="form-control form-control-sm" placeholder="ID групп (через запятую)" name="items[${itemCount}][group_ids]">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Дата начала *</label>
|
||||
<input type="date" name="items[${itemCount}][start_date]" class="form-control form-control-sm" required>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Дата окончания</label>
|
||||
<input type="date" name="items[${itemCount}][end_date]" class="form-control form-control-sm">
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger mt-2 remove-item"><i class="bi bi-trash"></i> Удалить</button>
|
||||
`;
|
||||
container.appendChild(newItem);
|
||||
itemCount++;
|
||||
|
||||
// Показываем кнопки удаления
|
||||
document.querySelectorAll('.remove-item').forEach(btn => btn.style.display = 'inline-block');
|
||||
});
|
||||
|
||||
// Удаление элемента
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.classList.contains('remove-item') || e.target.closest('.remove-item')) {
|
||||
e.target.closest('.item-row').remove();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
@extends('layouts.app')
|
||||
@section('title', 'Редактировать заявку #' . $courseRequest->id)
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<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 align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">Редактировать заявку #{{ $courseRequest->id }}</h1>
|
||||
<a href="{{ route('admin.course-requests.show', $courseRequest) }}" class="btn btn-secondary btn-sm">Назад</a>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('admin.course-requests.update', $courseRequest) }}" method="POST" id="editRequestForm">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="row">
|
||||
<div class="col-md-8 mb-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-primary text-white"><h5 class="mb-0">Элементы заявки</h5></div>
|
||||
<div class="card-body">
|
||||
<div id="items-container">
|
||||
@foreach($courseRequest->items as $index => $item)
|
||||
<div class="item-row border rounded p-3 mb-3 bg-light">
|
||||
<input type="hidden" name="items[{{ $index }}][id]" value="{{ $item->id }}">
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Курс *</label>
|
||||
<select name="items[{{ $index }}][course_id]" class="form-select form-select-sm" required>
|
||||
@foreach($courses as $id => $title)
|
||||
<option value="{{ $id }}" {{ $item->course_id == $id ? 'selected' : '' }}>{{ $title }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Пользователь</label>
|
||||
<input type="text" class="form-control form-control-sm" value="{{ $item->user_id }}" placeholder="ID пользователей" name="items[{{ $index }}][user_id]">
|
||||
<small class="text-muted">Или</small>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Группа</label>
|
||||
<input type="text" class="form-control form-control-sm" value="{{ $item->group_id }}" placeholder="ID групп" name="items[{{ $index }}][group_id]">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Дата начала *</label>
|
||||
<input type="date" name="items[{{ $index }}][start_date]" class="form-control form-control-sm" value="{{ $item->start_date->format('Y-m-d') }}" required>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Дата окончания</label>
|
||||
<input type="date" name="items[{{ $index }}][end_date]" class="form-control form-control-sm" value="{{ $item->end_date?->format('Y-m-d') }}">
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger mt-2 remove-item"><i class="bi bi-trash"></i> Удалить</button>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" id="add-item-btn">
|
||||
<i class="bi bi-plus-lg"></i> Добавить элемент
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 mb-4">
|
||||
<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="mb-3">
|
||||
<label class="form-label">Заметка</label>
|
||||
<textarea name="note" class="form-control" rows="4">{{ old('note', $courseRequest->note) }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Сохранить изменения</button>
|
||||
<a href="{{ route('admin.course-requests.show', $courseRequest) }}" class="btn btn-secondary">Отмена</a>
|
||||
</form>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
let itemCount = {{ $courseRequest->items->count() }};
|
||||
|
||||
document.getElementById('add-item-btn').addEventListener('click', function() {
|
||||
const container = document.getElementById('items-container');
|
||||
const newItem = document.createElement('div');
|
||||
newItem.className = 'item-row border rounded p-3 mb-3 bg-light';
|
||||
newItem.innerHTML = `
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Курс *</label>
|
||||
<select name="items[${itemCount}][course_id]" class="form-select form-select-sm" required>
|
||||
<option value="">Выберите курс</option>
|
||||
@foreach($courses as $id => $title)
|
||||
<option value="{{ $id }}">{{ $title }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Пользователь</label>
|
||||
<input type="text" class="form-control form-control-sm" placeholder="ID пользователей" name="items[${itemCount}][user_id]">
|
||||
<small class="text-muted">Или</small>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Группа</label>
|
||||
<input type="text" class="form-control form-control-sm" placeholder="ID групп" name="items[${itemCount}][group_id]">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Дата начала *</label>
|
||||
<input type="date" name="items[${itemCount}][start_date]" class="form-control form-control-sm" required>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small">Дата окончания</label>
|
||||
<input type="date" name="items[${itemCount}][end_date]" class="form-control form-control-sm">
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger mt-2 remove-item"><i class="bi bi-trash"></i> Удалить</button>
|
||||
`;
|
||||
container.appendChild(newItem);
|
||||
itemCount++;
|
||||
});
|
||||
|
||||
// Удаление элемента
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.classList.contains('remove-item') || e.target.closest('.remove-item')) {
|
||||
e.target.closest('.item-row').remove();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
@extends('layouts.app')
|
||||
@section('title', 'Заявка #' . $courseRequest->id)
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<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 align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">Заявка #{{ $courseRequest->id }}</h1>
|
||||
<div>
|
||||
@if($courseRequest->isPending())
|
||||
<form action="{{ route('admin.course-requests.approve', $courseRequest) }}" method="POST" class="d-inline">
|
||||
@csrf
|
||||
<button class="btn btn-success btn-sm"><i class="bi bi-check-lg"></i> Одобрить</button>
|
||||
</form>
|
||||
<form action="{{ route('admin.course-requests.reject', $courseRequest) }}" method="POST" class="d-inline">
|
||||
@csrf
|
||||
<button class="btn btn-danger btn-sm"><i class="bi bi-x-lg"></i> Отклонить</button>
|
||||
</form>
|
||||
<a href="{{ route('admin.course-requests.edit', $courseRequest) }}" class="btn btn-warning btn-sm"><i class="bi bi-pencil"></i> Редактировать</a>
|
||||
@endif
|
||||
<a href="{{ route('admin.course-requests.index') }}" class="btn btn-secondary btn-sm">Назад</a>
|
||||
</div>
|
||||
</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">
|
||||
<div><strong>Организация:</strong> {{ $courseRequest->organization->name }}</div>
|
||||
<div><strong>Статус:</strong>
|
||||
@if($courseRequest->isPending())
|
||||
<span class="badge bg-warning">Ожидает</span>
|
||||
@elseif($courseRequest->isApproved())
|
||||
<span class="badge bg-success">Одобрена</span>
|
||||
@else
|
||||
<span class="badge bg-danger">Отклонена</span>
|
||||
@endif
|
||||
</div>
|
||||
<div><strong>Создана:</strong> {{ $courseRequest->created_at->format('d.m.Y H:i') }}</div>
|
||||
<div><strong>Кем:</strong> {{ $courseRequest->requestedBy->name }}</div>
|
||||
@if($courseRequest->approved_by)
|
||||
<div><strong>{{ $courseRequest->isApproved() ? 'Одобрена' : 'Отклонена' }}:</strong> {{ $courseRequest->approvedBy?->name }} {{ $courseRequest->approved_at->format('d.m.Y H:i') }}</div>
|
||||
@endif
|
||||
@if($courseRequest->note)
|
||||
<div class="mt-3"><strong>Заметка:</strong><br>{{ $courseRequest->note }}</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</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">
|
||||
<div class="display-6 text-primary">{{ $courseRequest->items->count() }}</div>
|
||||
<div class="text-muted">Курсов</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<div class="display-6 text-success">{{ $courseRequest->items->where('user_id')->count() }}</div>
|
||||
<div class="text-muted">Пользователей</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<div class="display-6 text-info">{{ $courseRequest->items->where('group_id')->count() }}</div>
|
||||
<div class="text-muted">Групп</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header"><h5 class="mb-0">Элементы заявки</h5></div>
|
||||
<div class="card-body">
|
||||
@if($courseRequest->items->count() > 0)
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Курс</th>
|
||||
<th>Тип</th>
|
||||
<th>Получатель</th>
|
||||
<th>Период</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($courseRequest->items as $item)
|
||||
<tr>
|
||||
<td><strong>{{ $item->course->title }}</strong></td>
|
||||
<td>
|
||||
@if($item->user_id)
|
||||
<span class="badge bg-success"><i class="bi bi-person"></i> Индивидуально</span>
|
||||
@elseif($item->group_id)
|
||||
<span class="badge bg-info"><i class="bi bi-people"></i> Группе</span>
|
||||
@else
|
||||
<span class="badge bg-primary"><i class="bi bi-building"></i> Организации</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if($item->user_id)
|
||||
{{ $item->user?->name ?? '—' }}
|
||||
@elseif($item->group_id)
|
||||
{{ $item->group?->name ?? '—' }}
|
||||
@else
|
||||
{{ $courseRequest->organization->name }}
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<small>{{ $item->start_date->format('d.m.Y') }}</small>
|
||||
@if($item->end_date)
|
||||
<br><small>→ {{ $item->end_date->format('d.m.Y') }}</small>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@else
|
||||
<p class="text-muted text-center py-4">В заявке нет элементов</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($courseRequest->isApproved())
|
||||
<div class="card shadow-sm mt-4">
|
||||
<div class="card-header bg-success text-white"><h5 class="mb-0"><i class="bi bi-check-circle"></i> Назначения созданы</h5></div>
|
||||
<div class="card-body">
|
||||
<p class="mb-0">По этой заявке были созданы назначения. Проверить их можно в разделе <a href="{{ route('admin.course-assignments.index') }}">Назначения курсов</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
Loading…
Reference in New Issue