This commit is contained in:
2025-05-30 12:42:34 +04:00
parent 353452458c
commit 18b4d060a2
6 changed files with 259 additions and 50 deletions

View File

@@ -3,6 +3,7 @@
namespace App\Http\Controllers;
use App\Services\ApiService;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
@@ -62,6 +63,7 @@ class StatementController extends Controller
$typesResponse = $this->api->get('/employee/types');
$teachersResponse = $this->api->get('/employee/teachers');
// Если employee API успешно ответил
if ($statementResponse->successful() &&
$groupsResponse->successful() &&
$disciplinesResponse->successful() &&
@@ -78,7 +80,29 @@ class StatementController extends Controller
]);
}
abort($statementResponse->status());
$statementResponse2 = $this->api->get("/teacher/statements/{$id}");
$groupsResponse2 = $this->api->get('/teacher/groups');
$disciplinesResponse2 = $this->api->get('/teacher/disciplines');
$typesResponse2 = $this->api->get('/teacher/types');
$teachersResponse2 = $this->api->get('/teacher/teachers');
if ($statementResponse2->successful() &&
$groupsResponse2->successful() &&
$disciplinesResponse2->successful() &&
$typesResponse2->successful() &&
$teachersResponse2->successful()) {
return view('statements.form', [
'statement' => $statementResponse2->json(),
'groups' => $groupsResponse2->json(),
'disciplines' => $disciplinesResponse2->json(),
'types' => $typesResponse2->json(),
'teachers' => $teachersResponse2->json(),
'isEdit' => true
]);
}
abort($statementResponse->status() ?? $statementResponse2->status() ?? 500);
}
public function update(Request $request, $id)
@@ -118,4 +142,82 @@ class StatementController extends Controller
'disciplines' => $disciplines,
]);
}
/**
* Генерация PDF ведомости
*/
public function generatePdf(int $statementId, Request $request)
{
try {
$withGrades = $request->input('with_grades', true);
$response = $this->api->get("/teacher/statements/{$statementId}/pdf", [
'with_grades' => $withGrades
]);
if ($response->successful()) {
$content = $response->body();
$contentType = $response->header('Content-Type');
$contentDisposition = $response->header('Content-Disposition');
return response($content)
->header('Content-Type', $contentType)
->header('Content-Disposition', $contentDisposition);
}
return back()->withErrors($response->json()['error'] ?? 'Ошибка при генерации PDF');
} catch (\Exception $e) {
return back()->withErrors('Ошибка при генерации PDF: ' . $e->getMessage());
}
}
/**
* Финализация ведомости
*/
public function finalize(int $statementId)
{
try {
$response = $this->api->post("/teacher/statements/{$statementId}/finalize");
if ($response->successful()) {
return redirect()->back()->with('success', 'Ведомость успешно финализирована');
}
return back()->withErrors($response->json()['error'] ?? 'Ошибка при финализации ведомости');
} catch (\Exception $e) {
return back()->withErrors('Ошибка при финализации ведомости: ' . $e->getMessage());
}
}
public function myStatements(Request $request)
{
try {
$disciplinesResponse = $this->api->get('/teacher/disciplines/my');
$disciplines = $disciplinesResponse->json()['data'] ?? [];
$apiParams = [
'academic_year' => $request->input('academic_year'),
'discipline_id' => $request->input('discipline_id')
];
$statementsResponse = $this->api->get('/teacher/statements', $apiParams);
if (!$statementsResponse->successful()) {
throw new \Exception('Failed to load statements');
}
$groups = $this->api->get('/employee/groups')->json();
return view('teacher.statements.index', [
'statements' => $statementsResponse->json(),
'disciplines' => $disciplines,
'filters' => $request->only(['academic_year', 'discipline_id']),
'groups' => $groups
]);
} catch (\Exception $e) {
Log::error('Ошибка в myStatements: ' . $e->getMessage());
return back()->with('error', $e->getMessage())->withInput();
}
}
}

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Employee Login</title>
<title>Вход</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
<style>
body {
@@ -52,7 +52,7 @@
</head>
<body>
<div class="login-container">
<h1 class="text-2xl font-bold mb-6">Employee Portal</h1>
<h1 class="text-2xl font-bold mb-6">Вход</h1>
@if ($errors->any())
<div class="mb-4 p-2 bg-red-100 text-red-700 rounded">
@@ -63,7 +63,7 @@
<form method="POST" action="{{ route('login') }}">
@csrf
<div class="form-group">
<label for="email" class="form-label">Email</label>
<label for="email" class="form-label">Вход</label>
<input id="email" type="email" class="form-input" name="email" value="{{ old('email') }}" required autofocus>
@error('email')
<span class="error-message">{{ $message }}</span>
@@ -71,7 +71,7 @@
</div>
<div class="form-group">
<label for="password" class="form-label">Password</label>
<label for="password" class="form-label">Пароль</label>
<input id="password" type="password" class="form-input" name="password" required>
@error('password')
<span class="error-message">{{ $message }}</span>

View File

@@ -52,9 +52,9 @@
</head>
<body>
<div class="verify-container">
<h1 class="text-2xl font-bold mb-6">Two-Factor Authentication</h1>
<h1 class="text-2xl font-bold mb-6">Двухфакторная аутентификация</h1>
<p class="mb-4">We've sent a 6-digit verification code to your email. Please enter it below.</p>
<p class="mb-4">Мы отправили 6-значный проверочный код на ваш электронный адрес. Пожалуйста, введите его ниже.</p>
@if ($errors->any())
<div class="mb-4 p-2 bg-red-100 text-red-700 rounded">
@@ -65,7 +65,7 @@
<form method="POST" action="{{ route('verify-2fa') }}">
@csrf
<div class="form-group">
<label for="code" class="form-label">Verification Code</label>
<label for="code" class="form-label">Код подтверждения</label>
<input id="code" type="text" class="form-input" name="code" required autofocus maxlength="6" pattern="\d{6}">
@error('code')
<span class="error-message">{{ $message }}</span>
@@ -74,7 +74,7 @@
<div class="flex items-center justify-between">
<button type="submit" class="btn">
Verify
Подтвердить
</button>
</div>
</form>

View File

@@ -1,43 +1,152 @@
@extends('layouts.app')
@section('links')
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<link href="/resources/css/app.css" rel="stylesheet">
@endsection
@section('content')
<div class="container mx-auto px-4 py-8">
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold">Результаты поиска ведомостей</h1>
<a href="{{ route('teacher.statements.search') }}" class="text-blue-600 hover:text-blue-800">
Новый поиск
</a>
<div class="flex justify-between items-center mb-8">
<h1 class="text-2xl font-bold">Список ведомостей</h1>
<div class="flex flex-wrap gap-2 justify-end">
<a href="{{ url('/dashboard') }}" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded">
Перейти в панель
</a>
</div>
</div>
<!-- Фильтры -->
<div class="bg-white rounded-lg shadow p-6 mb-6">
<form method="GET" action="{{ route('teacher.statements.index') }}">
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div>
<label for="academic_year" class="block text-sm font-medium text-gray-700">Учебный год</label>
<select id="academic_year" name="academic_year" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm">
<option value="">Все годы</option>
@php
$currentYear = date('Y');
for ($i = 0; $i < 10; $i++) {
$year = $currentYear - $i;
$yearRange = $year . '-' . ($year + 1);
@endphp
<option value="{{ $yearRange }}" {{ request('academic_year') == $yearRange ? 'selected' : '' }}>
{{ $yearRange }}
</option>
@php } @endphp
</select>
</div>
<div>
<label for="discipline_id" class="block text-sm font-medium text-gray-700">Дисциплина</label>
<select id="discipline_id" name="discipline_id" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm">
<option value="">Все дисциплины</option>
@foreach($disciplines as $discipline)
<option value="{{ $discipline['id'] }}" {{ request('discipline_id') == $discipline['id'] ? 'selected' : '' }}>
{{ $discipline['name'] }}
</option>
@endforeach
</select>
</div>
<div>
<label for="group_id" class="block text-sm font-medium text-gray-700">Группа</label>
<select id="group_id" name="group_id" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm">
<option value="">Все группы</option>
@foreach($groups as $group)
<option value="{{ $group }}" {{ request('group_id') == $group ? 'selected' : '' }}>
{{ $group }}
</option>
@endforeach
</select>
</div>
<div class="flex items-end">
<button type="submit" class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 w-full">
Применить фильтры
</button>
</div>
</div>
</form>
</div>
<!-- Таблица ведомостей -->
<div class="bg-white rounded-lg shadow overflow-hidden">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Дисциплина</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Группа</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Учебный год</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Семестр</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Тип аттестации</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Действия</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@foreach($statements as $statement)
@if(count($statements) > 0)
<div class="table-responsive">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<td class="px-6 py-4 whitespace-nowrap">{{ $statement['discipline']['name'] }}</td>
<td class="px-6 py-4 whitespace-nowrap">{{ $statement['group']['name'] }}</td>
<td class="px-6 py-4 whitespace-nowrap">{{ $statement['academic_year'] }}</td>
<td class="px-6 py-4 whitespace-nowrap">{{ $statement['semester'] }}</td>
<td class="px-6 py-4 whitespace-nowrap">{{ $statement['type_certification']['name'] }}</td>
<td class="px-6 py-4 whitespace-nowrap">
<a href="{{ route('teacher.statements.show', $statement['id']) }}" class="text-blue-600 hover:text-blue-900">Просмотр</a>
</td>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Дисциплина</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Группа</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Учебный год</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Семестр</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Тип аттестации</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Статус</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"></th>
</tr>
@endforeach
</tbody>
</table>
</div>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@foreach($statements as $statement)
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap cursor-pointer" onclick="window.location='{{ route('statements.edit', $statement['id']) }}'">
<div class="text-sm font-medium text-gray-900">
{{ $statement['discipline']['name'] }}
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap cursor-pointer" onclick="window.location='{{ route('statements.edit', $statement['id']) }}'">
<div class="text-sm text-gray-500">
{{ $statement['group']['name'] }}
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap cursor-pointer" onclick="window.location='{{ route('statements.edit', $statement['id']) }}'">
<div class="text-sm text-gray-500">{{ $statement['academic_year'] }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap cursor-pointer" onclick="window.location='{{ route('statements.edit', $statement['id']) }}'">
<div class="text-sm text-gray-500">{{ $statement['semester'] }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap cursor-pointer" onclick="window.location='{{ route('statements.edit', $statement['id']) }}'">
<div class="text-sm text-gray-500">{{ $statement['type_certification']['name'] }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap cursor-pointer" onclick="window.location='{{ route('statements.edit', $statement['id']) }}'">
<div class="text-sm text-gray-500">
@if($statement['is_finalized'])
<span class="px-2 py-1 bg-green-100 text-green-800 rounded-full text-xs">
Проведена
</span>
@else
<span class="px-2 py-1 bg-yellow-100 text-yellow-800 rounded-full text-xs">
Сохранена
</span>
@endif
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium flex justify-end items-center space-x-2">
<a href="{{ route('teacher.statements.generatePdf', ['statementId' => $statement['id'], 'with_grades' => true]) }}"
class="text-blue-500 hover:text-blue-700" title="Скачать с оценками">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 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>
</a>
@if(!$statement['is_finalized'])
<form action="{{ route('teacher.statements.sendFinalizationCode', $statement['id']) }}" method="POST" class="inline">
@csrf
<button type="submit" class="text-green-500 hover:text-green-700" title="Завершить ведомость">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
</button>
</form>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="p-6 text-center text-gray-500">
Ведомости не найдены. Попробуйте изменить параметры поиска.
</div>
@endif
</div>
</div>
@endsection

View File

@@ -10,17 +10,12 @@
</div>
<div class="card-body">
<form method="POST" action="{{ route('teacher.statements.index') }}">
@csrf
@if(isset($student))
@method('PATCH')
@endif
<form method="GET" action="{{ route('teacher.statements.index') }}">
<div class="row mb-3">
<label for="surname" class="col-md-4 col-form-label text-md-end">Фамилия*</label>
<div class="col-md-6">
<select id="academic_year" class="form-control @error('academic_year') is-invalid @enderror" name="academic_year" required>
<option value="" disabled {{ empty(old('academic_year', $statement['academic_year'] ?? '')) ? 'selected' : '' }}>Выберите учебный год</option>
<option>Выберите учебный год</option>
@php
$currentYear = date('Y');
$nextYear = $currentYear + 1;
@@ -73,9 +68,9 @@
<div class="row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
Найти ведомость
Найти ведомости
</button>
<a href="{{ route('students.index') }}" class="btn btn-secondary">Отмена</a>
<a href="{{ route('teacher.statements.index') }}" class="btn btn-secondary">Отмена</a>
</div>
</div>
</form>

View File

@@ -42,6 +42,9 @@ Route::middleware(['jwt.auth'])->group(function () {
Route::resource('statements', StatementController::class)->except(['show']);
Route::get('/statements/search', [StatementController::class, 'search'])->name('teacher.statements.search');
Route::get('/my/statements', [StatementController::class, 'myStatements'])->name('teacher.statements.index');
Route::get('/statements/pdf', [StatementController::class, 'generatePdf'])->name('teacher.statements.generatePdf');
Route::get('/statements/sendFinalizationCode', [StatementController::class, 'finalize'])->name('teacher.statements.sendFinalizationCode');
/* Route::get('/statements/{id}/sendFinalizationCode', [StatementController::class, 'myStatements'])->name('teacher.statements.sendFinalizationCode');*/
// Statistics
Route::get('/statistics', [StatisticController::class, 'index'])->name('statistics.index');