first&last
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
<div>
|
||||
<form wire:submit="login" class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Email</label>
|
||||
<input wire:model.defer="email" type="email" class="w-full rounded-xl border border-slate-300 px-4 py-2.5 focus:border-slate-900 focus:ring-0" placeholder="name@service.local">
|
||||
@error('email') <p class="text-xs text-rose-600 mt-1">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Пароль</label>
|
||||
<div class="relative">
|
||||
<input wire:model.defer="password" type="{{ $showPassword ? 'text' : 'password' }}" class="w-full rounded-xl border border-slate-300 px-4 py-2.5 pr-24 focus:border-slate-900 focus:ring-0" placeholder="Введите пароль">
|
||||
<button type="button" wire:click="$toggle('showPassword')" class="absolute right-2 top-1/2 -translate-y-1/2 text-xs rounded-lg border border-slate-300 px-2.5 py-1">{{ $showPassword ? 'Скрыть' : 'Показать' }}</button>
|
||||
</div>
|
||||
@error('password') <p class="text-xs text-rose-600 mt-1">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<label class="inline-flex items-center gap-2 text-sm">
|
||||
<input wire:model="remember" type="checkbox" class="rounded border-slate-300">
|
||||
Запомнить меня
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="w-full rounded-xl bg-slate-900 text-white py-2.5 font-medium hover:bg-slate-800 transition">
|
||||
Войти
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,84 @@
|
||||
<div wire:poll.60s="refreshData" class="space-y-6">
|
||||
<div id="dashboard-chart-data" class="hidden"
|
||||
data-status='@json($statusChart)'
|
||||
data-priority='@json($priorityChart)'></div>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
<div class="card"><div class="card-body"><p class="text-sm text-slate-500">Открыто сегодня</p><p class="text-3xl font-semibold mt-2">{{ $openToday }}</p></div></div>
|
||||
<div class="card"><div class="card-body"><p class="text-sm text-slate-500">Просрочено</p><p class="text-3xl font-semibold mt-2 text-rose-600">{{ $overdue }}</p></div></div>
|
||||
<div class="card"><div class="card-body"><p class="text-sm text-slate-500">Среднее время решения (30 дней)</p><p class="text-3xl font-semibold mt-2">{{ number_format($avgResolutionHours, 1) }}ч</p></div></div>
|
||||
<div class="card"><div class="card-body"><p class="text-sm text-slate-500">Топ техников</p><p class="text-3xl font-semibold mt-2">{{ count($topTechnicians) }}</p></div></div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-6 xl:grid-cols-2">
|
||||
<div class="card" wire:ignore>
|
||||
<div class="card-body">
|
||||
<h2 class="text-lg font-semibold mb-4">Заявки по статусам</h2>
|
||||
<div class="dashboard-chart-wrap">
|
||||
<canvas id="statusChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card" wire:ignore>
|
||||
<div class="card-body">
|
||||
<h2 class="text-lg font-semibold mb-4">Заявки по приоритетам</h2>
|
||||
<div class="dashboard-chart-wrap">
|
||||
<canvas id="priorityChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-6 xl:grid-cols-2">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h2 class="text-lg font-semibold mb-4">Топ-5 техников</h2>
|
||||
<div class="space-y-2">
|
||||
@forelse($topTechnicians as $item)
|
||||
<div class="flex items-center justify-between rounded-lg border border-slate-200 px-3 py-2">
|
||||
<span>{{ $item['name'] }}</span>
|
||||
<span class="text-sm text-slate-500">Закрыто: {{ $item['closed_requests_count'] }}</span>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-sm text-slate-500">Нет данных.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h2 class="text-lg font-semibold mb-4">Ближайшие плановые работы</h2>
|
||||
<div class="space-y-2">
|
||||
@forelse($upcomingTasks as $item)
|
||||
<div class="rounded-lg border border-slate-200 px-3 py-2">
|
||||
<p class="font-medium text-sm">{{ $item['ticket'] }} — {{ $item['title'] }}</p>
|
||||
<p class="text-xs text-slate-500 mt-1">{{ $item['planned_at'] }} • {{ $item['assignee'] }}</p>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-sm text-slate-500">Нет запланированных задач.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h2 class="text-lg font-semibold mb-4">Лента активности</h2>
|
||||
<div class="space-y-2">
|
||||
@forelse($recentActivities as $item)
|
||||
<div class="rounded-lg border border-slate-200 px-3 py-2 flex justify-between items-start gap-4">
|
||||
<div>
|
||||
<p class="text-sm font-medium">{{ $item['user'] }}: {{ $item['description'] }}</p>
|
||||
<p class="text-xs text-slate-500">{{ $item['action'] }}</p>
|
||||
</div>
|
||||
<p class="text-xs text-slate-500 whitespace-nowrap">{{ $item['created_at'] }}</p>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-sm text-slate-500">Активность отсутствует.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,11 @@
|
||||
<div class="grid gap-4 lg:grid-cols-3">
|
||||
<div class="card lg:col-span-1"><div class="card-body">
|
||||
<h2 class="text-lg font-semibold mb-3">{{ $editingId ? 'Редактировать отдел' : 'Новый отдел' }}</h2>
|
||||
<div class="space-y-3">
|
||||
<div><label class="text-sm font-medium">Название</label><input wire:model.defer="name" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
<div><label class="text-sm font-medium">Описание</label><textarea wire:model.defer="description" rows="3" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></textarea></div>
|
||||
<button wire:click="save" class="rounded-xl bg-slate-900 text-white px-3 py-2 text-sm">Сохранить</button>
|
||||
</div>
|
||||
</div></div>
|
||||
<div class="card lg:col-span-2"><div class="card-body"><table class="min-w-full text-sm"><thead><tr><th class="text-left py-2">Название</th><th class="text-left py-2">Описание</th><th></th></tr></thead><tbody>@foreach($items as $item)<tr class="border-t border-slate-100"><td class="py-2">{{ $item->name }}</td><td class="py-2">{{ $item->description }}</td><td class="py-2 text-right"><button wire:click="edit({{ $item->id }})" class="text-xs mr-2">Изм.</button><button wire:click="delete({{ $item->id }})" class="text-xs text-rose-600">Удалить</button></td></tr>@endforeach</tbody></table></div></div>
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
<div class="grid gap-4 lg:grid-cols-3">
|
||||
<div class="card"><div class="card-body space-y-3">
|
||||
<h2 class="text-lg font-semibold">{{ $editingId ? 'Редактировать категорию' : 'Новая категория' }}</h2>
|
||||
<input wire:model.defer="name" placeholder="Название" class="w-full rounded-xl border border-slate-300 px-3 py-2">
|
||||
<textarea wire:model.defer="description" rows="3" placeholder="Описание" class="w-full rounded-xl border border-slate-300 px-3 py-2"></textarea>
|
||||
<button wire:click="save" class="rounded-xl bg-slate-900 text-white px-3 py-2 text-sm">Сохранить</button>
|
||||
</div></div>
|
||||
<div class="card lg:col-span-2"><div class="card-body"><table class="min-w-full text-sm"><thead><tr><th class="text-left py-2">Название</th><th class="text-left py-2">Описание</th><th></th></tr></thead><tbody>@foreach($items as $item)<tr class="border-t border-slate-100"><td class="py-2">{{ $item->name }}</td><td class="py-2">{{ $item->description }}</td><td class="py-2 text-right"><button wire:click="edit({{ $item->id }})" class="text-xs mr-2">Изм.</button><button wire:click="delete({{ $item->id }})" class="text-xs text-rose-600">Удалить</button></td></tr>@endforeach</tbody></table></div></div>
|
||||
</div>
|
||||
@@ -0,0 +1,36 @@
|
||||
<div class="grid gap-4 lg:grid-cols-3">
|
||||
<div class="card"><div class="card-body space-y-3">
|
||||
<h2 class="text-lg font-semibold">{{ $editingId ? 'Редактировать оборудование' : 'Новое оборудование' }}</h2>
|
||||
<input wire:model.defer="name" placeholder="Название" class="w-full rounded-xl border border-slate-300 px-3 py-2">
|
||||
<input wire:model.defer="serialNumber" placeholder="Серийный номер" class="w-full rounded-xl border border-slate-300 px-3 py-2">
|
||||
<input wire:model.defer="inventoryNumber" placeholder="Инвентарный номер" class="w-full rounded-xl border border-slate-300 px-3 py-2">
|
||||
<select wire:model.defer="categoryId" class="w-full rounded-xl border border-slate-300 px-3 py-2"><option value="">Категория</option>@foreach($categories as $c)<option value="{{ $c->id }}">{{ $c->name }}</option>@endforeach</select>
|
||||
<select wire:model.defer="clientId" class="w-full rounded-xl border border-slate-300 px-3 py-2"><option value="">Клиент</option>@foreach($clients as $c)<option value="{{ $c->id }}">{{ $c->name }}</option>@endforeach</select>
|
||||
<div class="grid gap-2 md:grid-cols-2"><input wire:model.defer="purchaseDate" type="date" class="w-full rounded-xl border border-slate-300 px-3 py-2"><input wire:model.defer="warrantyUntil" type="date" class="w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
<select wire:model.defer="condition" class="w-full rounded-xl border border-slate-300 px-3 py-2"><option value="good">Хорошее</option><option value="fair">Удовлетворительное</option><option value="poor">Плохое</option></select>
|
||||
<textarea wire:model.defer="notes" rows="3" placeholder="Примечания" class="w-full rounded-xl border border-slate-300 px-3 py-2"></textarea>
|
||||
<button wire:click="save" class="rounded-xl bg-slate-900 text-white px-3 py-2 text-sm">Сохранить</button>
|
||||
</div></div>
|
||||
|
||||
<div class="card lg:col-span-2 overflow-hidden">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead class="bg-slate-50"><tr><th class="px-3 py-2 text-left">Название</th><th class="px-3 py-2 text-left">Категория</th><th class="px-3 py-2 text-left">Клиент</th><th class="px-3 py-2 text-left">Гарантия</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
@foreach($items as $item)
|
||||
@php
|
||||
$expired = $item->warranty_until && $item->warranty_until->isPast();
|
||||
$expiring = $item->warranty_until && !$expired && $item->warranty_until->lte(now()->addDays(30));
|
||||
@endphp
|
||||
<tr class="border-t border-slate-100">
|
||||
<td class="px-3 py-2"><p class="font-medium">{{ $item->name }}</p><p class="text-xs text-slate-500">{{ $item->inventory_number }}</p></td>
|
||||
<td class="px-3 py-2">{{ $item->category?->name }}</td>
|
||||
<td class="px-3 py-2">{{ $item->client?->name ?? '—' }}</td>
|
||||
<td class="px-3 py-2 {{ $expired ? 'text-rose-600' : ($expiring ? 'text-amber-600' : '') }}">{{ $item->warranty_until?->format('d.m.Y') ?? '—' }}</td>
|
||||
<td class="px-3 py-2 text-right"><button wire:click="edit({{ $item->id }})" class="text-xs mr-2">Изм.</button><button wire:click="delete({{ $item->id }})" class="text-xs text-rose-600">Удалить</button></td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="p-4">{{ $items->links() }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
<div class="grid gap-4 lg:grid-cols-3">
|
||||
<div class="card"><div class="card-body space-y-3">
|
||||
<h2 class="text-lg font-semibold">{{ $editingId ? 'Редактировать приоритет' : 'Новый приоритет' }}</h2>
|
||||
<input wire:model.defer="name" placeholder="Название" class="w-full rounded-xl border border-slate-300 px-3 py-2">
|
||||
<input wire:model.defer="colorHex" type="color" class="w-full rounded-xl border border-slate-300 px-3 py-2 h-10">
|
||||
<input wire:model.defer="level" type="number" min="1" max="4" class="w-full rounded-xl border border-slate-300 px-3 py-2">
|
||||
<button wire:click="save" class="rounded-xl bg-slate-900 text-white px-3 py-2 text-sm">Сохранить</button>
|
||||
</div></div>
|
||||
<div class="card lg:col-span-2"><div class="card-body"><table class="min-w-full text-sm"><thead><tr><th class="text-left py-2">Название</th><th class="text-left py-2">Цвет</th><th class="text-left py-2">Уровень</th><th></th></tr></thead><tbody>@foreach($items as $item)<tr class="border-t border-slate-100"><td class="py-2">{{ $item->name }}</td><td class="py-2"><span class="inline-block h-4 w-8 rounded" style="background-color: {{ $item->color_hex }}"></span></td><td class="py-2">{{ $item->level }}</td><td class="py-2 text-right"><button wire:click="edit({{ $item->id }})" class="text-xs mr-2">Изм.</button><button wire:click="delete({{ $item->id }})" class="text-xs text-rose-600">Удалить</button></td></tr>@endforeach</tbody></table></div></div>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
<div class="grid gap-4 lg:grid-cols-3">
|
||||
<div class="card"><div class="card-body space-y-3">
|
||||
<h2 class="text-lg font-semibold">{{ $editingId ? 'Редактировать тип' : 'Новый тип' }}</h2>
|
||||
<input wire:model.defer="name" placeholder="Название" class="w-full rounded-xl border border-slate-300 px-3 py-2">
|
||||
<input wire:model.defer="slaHours" type="number" min="1" placeholder="SLA часов" class="w-full rounded-xl border border-slate-300 px-3 py-2">
|
||||
<textarea wire:model.defer="description" rows="3" placeholder="Описание" class="w-full rounded-xl border border-slate-300 px-3 py-2"></textarea>
|
||||
<button wire:click="save" class="rounded-xl bg-slate-900 text-white px-3 py-2 text-sm">Сохранить</button>
|
||||
</div></div>
|
||||
<div class="card lg:col-span-2"><div class="card-body"><table class="min-w-full text-sm"><thead><tr><th class="text-left py-2">Название</th><th class="text-left py-2">SLA</th><th class="text-left py-2">Описание</th><th></th></tr></thead><tbody>@foreach($items as $item)<tr class="border-t border-slate-100"><td class="py-2">{{ $item->name }}</td><td class="py-2">{{ $item->sla_hours }} ч</td><td class="py-2">{{ $item->description }}</td><td class="py-2 text-right"><button wire:click="edit({{ $item->id }})" class="text-xs mr-2">Изм.</button><button wire:click="delete({{ $item->id }})" class="text-xs text-rose-600">Удалить</button></td></tr>@endforeach</tbody></table></div></div>
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
<div class="grid gap-4 lg:grid-cols-3">
|
||||
<div class="card"><div class="card-body space-y-3">
|
||||
<h2 class="text-lg font-semibold">{{ $editingId ? 'Редактировать статус' : 'Новый статус' }}</h2>
|
||||
<input wire:model.defer="name" placeholder="Название" class="w-full rounded-xl border border-slate-300 px-3 py-2">
|
||||
<input wire:model.defer="slug" placeholder="Slug" class="w-full rounded-xl border border-slate-300 px-3 py-2">
|
||||
<input wire:model.defer="colorHex" type="color" class="w-full rounded-xl border border-slate-300 px-3 py-2 h-10">
|
||||
<input wire:model.defer="sortOrder" type="number" min="1" class="w-full rounded-xl border border-slate-300 px-3 py-2">
|
||||
<label class="inline-flex items-center gap-2"><input wire:model.defer="isFinal" type="checkbox" class="rounded"> <span class="text-sm">Финальный</span></label>
|
||||
<button wire:click="save" class="rounded-xl bg-slate-900 text-white px-3 py-2 text-sm">Сохранить</button>
|
||||
</div></div>
|
||||
<div class="card lg:col-span-2"><div class="card-body"><table class="min-w-full text-sm"><thead><tr><th class="text-left py-2">Название</th><th class="text-left py-2">Slug</th><th class="text-left py-2">Порядок</th><th class="text-left py-2">Финальный</th><th></th></tr></thead><tbody>@foreach($items as $item)<tr class="border-t border-slate-100"><td class="py-2">{{ $item->name }}</td><td class="py-2">{{ $item->slug }}</td><td class="py-2">{{ $item->sort_order }}</td><td class="py-2">{{ $item->is_final ? 'Да' : 'Нет' }}</td><td class="py-2 text-right"><button wire:click="edit({{ $item->id }})" class="text-xs mr-2">Изм.</button><button wire:click="delete({{ $item->id }})" class="text-xs text-rose-600">Удалить</button></td></tr>@endforeach</tbody></table></div></div>
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
<div class="card"><div class="card-body">
|
||||
<form wire:submit="save" class="space-y-4">
|
||||
<div><label class="text-sm font-medium">Заголовок</label><input wire:model.defer="title" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2">@error('title')<p class="text-xs text-rose-600">{{ $message }}</p>@enderror</div>
|
||||
<div><label class="text-sm font-medium">Описание</label><textarea wire:model.defer="description" rows="5" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></textarea>@error('description')<p class="text-xs text-rose-600">{{ $message }}</p>@enderror</div>
|
||||
<div class="grid gap-4 md:grid-cols-3">
|
||||
<div><label class="text-sm font-medium">Тип заявки</label><select wire:model.defer="typeId" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"><option value="">Выберите</option>@foreach($types as $item)<option value="{{ $item->id }}">{{ $item->name }}</option>@endforeach</select></div>
|
||||
<div><label class="text-sm font-medium">Приоритет</label><select wire:model.defer="priorityId" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"><option value="">Выберите</option>@foreach($priorities as $item)<option value="{{ $item->id }}">{{ $item->name }}</option>@endforeach</select></div>
|
||||
<div><label class="text-sm font-medium">Оборудование</label><select wire:model.defer="equipmentId" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"><option value="">Не выбрано</option>@foreach($equipmentItems as $item)<option value="{{ $item->id }}">{{ $item->name }}</option>@endforeach</select></div>
|
||||
</div>
|
||||
<div class="flex justify-end"><button class="rounded-xl bg-slate-900 text-white px-4 py-2">Создать заявку</button></div>
|
||||
</form>
|
||||
</div></div>
|
||||
@@ -0,0 +1,27 @@
|
||||
<div class="space-y-4">
|
||||
<div class="flex justify-between items-center">
|
||||
<input wire:model.live.debounce.300ms="search" type="text" placeholder="Поиск по заявкам" class="rounded-xl border border-slate-300 px-3 py-2 w-full max-w-sm">
|
||||
<a href="{{ route('my-requests.create') }}" class="rounded-xl bg-slate-900 text-white px-4 py-2 text-sm">Новая заявка</a>
|
||||
</div>
|
||||
|
||||
<div class="card overflow-hidden">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead class="bg-slate-50"><tr><th class="px-3 py-2 text-left">Ticket</th><th class="px-3 py-2 text-left">Заголовок</th><th class="px-3 py-2 text-left">Тип</th><th class="px-3 py-2 text-left">Приоритет</th><th class="px-3 py-2 text-left">Статус</th><th class="px-3 py-2 text-left">Дата</th></tr></thead>
|
||||
<tbody>
|
||||
@forelse($requests as $request)
|
||||
<tr class="border-t border-slate-100">
|
||||
<td class="px-3 py-2"><a href="{{ route('my-requests.show', $request) }}" class="font-medium hover:underline">{{ $request->ticket_number }}</a></td>
|
||||
<td class="px-3 py-2">{{ $request->title }}</td>
|
||||
<td class="px-3 py-2">{{ $request->type?->name }}</td>
|
||||
<td class="px-3 py-2"><span class="inline-flex rounded-full px-2 py-1 text-xs text-white" style="background-color: {{ $request->priority?->color_hex }}">{{ $request->priority?->name }}</span></td>
|
||||
<td class="px-3 py-2"><span class="inline-flex rounded-full px-2 py-1 text-xs text-white" style="background-color: {{ $request->status?->color_hex }}">{{ $request->status?->name }}</span></td>
|
||||
<td class="px-3 py-2">{{ $request->created_at?->format('d.m.Y H:i') }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="px-3 py-8 text-center text-slate-500">Нет заявок</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="p-4">{{ $requests->links() }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<div class="space-y-4">
|
||||
<div class="flex justify-end"><button wire:click="markAllAsRead" class="rounded-xl border border-slate-300 px-3 py-2 text-sm">Отметить все как прочитанные</button></div>
|
||||
<div class="card overflow-hidden"><table class="min-w-full text-sm"><thead class="bg-slate-50"><tr><th class="px-3 py-2 text-left">Уведомление</th><th class="px-3 py-2 text-left">Дата</th><th></th></tr></thead><tbody>@forelse($notifications as $notification)<tr class="border-t border-slate-100 {{ $notification->read_at ? '' : 'bg-blue-50' }}"><td class="px-3 py-2"><p class="font-medium">{{ $notification->data['title'] ?? 'Событие' }}</p><p class="text-xs text-slate-500">{{ $notification->data['message'] ?? '' }}</p></td><td class="px-3 py-2">{{ $notification->created_at?->format('d.m.Y H:i') }}</td><td class="px-3 py-2 text-right">@if(!$notification->read_at)<button wire:click="markAsRead('{{ $notification->id }}')" class="text-xs">Прочитано</button>@endif @if(!empty($notification->data['url']))<a href="{{ $notification->data['url'] }}" class="text-xs ml-2 text-slate-700">Открыть</a>@endif</td></tr>@empty<tr><td colspan="3" class="px-3 py-8 text-center text-slate-500">Нет уведомлений</td></tr>@endforelse</tbody></table><div class="p-4">{{ $notifications->links() }}</div></div>
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
<div class="card max-w-2xl"><div class="card-body">
|
||||
<form wire:submit="save" class="space-y-4">
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div><label class="text-sm font-medium">Имя</label><input wire:model.defer="name" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
<div><label class="text-sm font-medium">Email</label><input wire:model.defer="email" type="email" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
<div><label class="text-sm font-medium">Телефон</label><input wire:model.defer="phone" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
<div><label class="text-sm font-medium">Новый пароль</label><input wire:model.defer="password" type="password" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
<div><label class="text-sm font-medium">Подтверждение</label><input wire:model.defer="password_confirmation" type="password" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
</div>
|
||||
<div class="flex justify-end"><button class="rounded-xl bg-slate-900 text-white px-4 py-2">Сохранить</button></div>
|
||||
</form>
|
||||
</div></div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<div class="space-y-4">
|
||||
<div class="card"><div class="card-body flex flex-wrap gap-2"><input wire:model.live="dateFrom" type="date" class="rounded-xl border border-slate-300 px-3 py-2"><input wire:model.live="dateTo" type="date" class="rounded-xl border border-slate-300 px-3 py-2"></div></div>
|
||||
<div class="card overflow-hidden"><table class="min-w-full text-sm"><thead class="bg-slate-50"><tr><th class="px-3 py-2 text-left">Оборудование</th><th class="px-3 py-2 text-center">Всего обращений</th><th class="px-3 py-2 text-center">Открытые</th><th class="px-3 py-2 text-center">Закрытые</th></tr></thead><tbody>@foreach($rows as $row)<tr class="border-t border-slate-100"><td class="px-3 py-2">{{ $row['equipment'] }}</td><td class="px-3 py-2 text-center">{{ $row['total_requests'] }}</td><td class="px-3 py-2 text-center">{{ $row['open'] }}</td><td class="px-3 py-2 text-center">{{ $row['closed'] }}</td></tr>@endforeach</tbody></table></div>
|
||||
</div>
|
||||
@@ -0,0 +1,20 @@
|
||||
<div class="space-y-4">
|
||||
<div id="general-report-chart-data" class="hidden" data-chart='@json($statusChart->values()->toArray())'></div>
|
||||
|
||||
<div class="card"><div class="card-body grid gap-3 md:grid-cols-5">
|
||||
<input wire:model.live="dateFrom" type="date" class="rounded-xl border border-slate-300 px-3 py-2">
|
||||
<input wire:model.live="dateTo" type="date" class="rounded-xl border border-slate-300 px-3 py-2">
|
||||
<select wire:model.live="technicianId" class="rounded-xl border border-slate-300 px-3 py-2"><option value="">Все техники</option>@foreach($technicians as $item)<option value="{{ $item->id }}">{{ $item->name }}</option>@endforeach</select>
|
||||
<select wire:model.live="typeId" class="rounded-xl border border-slate-300 px-3 py-2"><option value="">Все типы</option>@foreach($types as $item)<option value="{{ $item->id }}">{{ $item->name }}</option>@endforeach</select>
|
||||
<select wire:model.live="statusId" class="rounded-xl border border-slate-300 px-3 py-2"><option value="">Все статусы</option>@foreach($statuses as $item)<option value="{{ $item->id }}">{{ $item->name }}</option>@endforeach</select>
|
||||
</div></div>
|
||||
|
||||
<div class="card" wire:ignore>
|
||||
<div class="card-body">
|
||||
<h2 class="text-lg font-semibold mb-3">Динамика по статусам</h2>
|
||||
<canvas id="generalStatusChart" height="120"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card overflow-hidden"><table class="min-w-full text-sm"><thead class="bg-slate-50"><tr><th class="px-3 py-2 text-left">Ticket</th><th class="px-3 py-2 text-left">Заголовок</th><th class="px-3 py-2 text-left">Тип</th><th class="px-3 py-2 text-left">Статус</th><th class="px-3 py-2 text-left">Техник</th><th class="px-3 py-2 text-left">Дата</th></tr></thead><tbody>@foreach($rows as $row)<tr class="border-t border-slate-100"><td class="px-3 py-2">{{ $row->ticket_number }}</td><td class="px-3 py-2">{{ $row->title }}</td><td class="px-3 py-2">{{ $row->type?->name }}</td><td class="px-3 py-2">{{ $row->status?->name }}</td><td class="px-3 py-2">{{ $row->assignee?->name ?? '—' }}</td><td class="px-3 py-2">{{ $row->created_at?->format('d.m.Y H:i') }}</td></tr>@endforeach</tbody></table></div>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<div class="space-y-4">
|
||||
<div class="card"><div class="card-body flex flex-wrap gap-2"><input wire:model.live="dateFrom" type="date" class="rounded-xl border border-slate-300 px-3 py-2"><input wire:model.live="dateTo" type="date" class="rounded-xl border border-slate-300 px-3 py-2"></div></div>
|
||||
<div class="card overflow-hidden"><table class="min-w-full text-sm"><thead class="bg-slate-50"><tr><th class="px-3 py-2 text-left">Тип заявки</th><th class="px-3 py-2 text-center">Всего</th><th class="px-3 py-2 text-center">В SLA</th><th class="px-3 py-2 text-center">Соблюдение</th></tr></thead><tbody>@foreach($rows as $row)<tr class="border-t border-slate-100"><td class="px-3 py-2">{{ $row['type'] }}</td><td class="px-3 py-2 text-center">{{ $row['total'] }}</td><td class="px-3 py-2 text-center">{{ $row['within_sla'] }}</td><td class="px-3 py-2 text-center">{{ $row['percent'] }}%</td></tr>@endforeach</tbody></table></div>
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
<div class="space-y-4">
|
||||
<div class="card"><div class="card-body flex flex-wrap items-center gap-2">
|
||||
<input wire:model.live="dateFrom" type="date" class="rounded-xl border border-slate-300 px-3 py-2">
|
||||
<input wire:model.live="dateTo" type="date" class="rounded-xl border border-slate-300 px-3 py-2">
|
||||
<button wire:click="exportCsv" class="rounded-xl border border-slate-300 px-3 py-2 text-sm">Экспорт CSV</button>
|
||||
<button wire:click="exportPdf" class="rounded-xl bg-slate-900 text-white px-3 py-2 text-sm">Экспорт PDF</button>
|
||||
</div></div>
|
||||
<div class="card overflow-hidden"><table class="min-w-full text-sm"><thead class="bg-slate-50"><tr><th class="px-3 py-2 text-left">Техник</th><th class="px-3 py-2">Назначено</th><th class="px-3 py-2">Решено</th><th class="px-3 py-2">Среднее время</th><th class="px-3 py-2">Рейтинг</th><th class="px-3 py-2">Просрочено</th></tr></thead><tbody>@foreach($rows as $row)<tr class="border-t border-slate-100"><td class="px-3 py-2">{{ $row['technician'] }}</td><td class="px-3 py-2 text-center">{{ $row['assigned'] }}</td><td class="px-3 py-2 text-center">{{ $row['resolved'] }}</td><td class="px-3 py-2 text-center">{{ $row['avg_resolution_hours'] }}ч</td><td class="px-3 py-2 text-center">{{ $row['rating'] }}</td><td class="px-3 py-2 text-center">{{ $row['overdue'] }}</td></tr>@endforeach</tbody></table></div>
|
||||
</div>
|
||||
@@ -0,0 +1,22 @@
|
||||
<div class="card"><div class="card-body">
|
||||
<form wire:submit="save" class="space-y-4">
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div class="md:col-span-2"><label class="text-sm font-medium">Заголовок</label><input wire:model.defer="title" type="text" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2">@error('title')<p class="text-xs text-rose-600">{{ $message }}</p>@enderror</div>
|
||||
<div class="md:col-span-2"><label class="text-sm font-medium">Описание</label><textarea wire:model.defer="description" rows="4" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></textarea>@error('description')<p class="text-xs text-rose-600">{{ $message }}</p>@enderror</div>
|
||||
<div><label class="text-sm font-medium">Тип</label><select wire:model.live="typeId" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"><option value="">Выберите</option>@foreach($types as $item)<option value="{{ $item->id }}">{{ $item->name }}</option>@endforeach</select>@error('type_id')<p class="text-xs text-rose-600">{{ $message }}</p>@enderror</div>
|
||||
<div><label class="text-sm font-medium">Приоритет</label><select wire:model.defer="priorityId" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"><option value="">Выберите</option>@foreach($priorities as $item)<option value="{{ $item->id }}">{{ $item->name }}</option>@endforeach</select>@error('priority_id')<p class="text-xs text-rose-600">{{ $message }}</p>@enderror</div>
|
||||
<div><label class="text-sm font-medium">Клиент</label><select wire:model.live="clientId" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"><option value="">Выберите</option>@foreach($clients as $item)<option value="{{ $item->id }}">{{ $item->name }}</option>@endforeach</select>@error('client_id')<p class="text-xs text-rose-600">{{ $message }}</p>@enderror</div>
|
||||
<div><label class="text-sm font-medium">Исполнитель</label><select wire:model.defer="assignedTo" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"><option value="">Не назначен</option>@foreach($technicians as $item)<option value="{{ $item->id }}">{{ $item->name }}</option>@endforeach</select></div>
|
||||
<div><label class="text-sm font-medium">Оборудование</label><select wire:model.defer="equipmentId" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"><option value="">Не выбрано</option>@foreach($equipmentItems as $item)<option value="{{ $item->id }}">{{ $item->name }}</option>@endforeach</select></div>
|
||||
<div><label class="text-sm font-medium">Статус</label><select wire:model.defer="statusId" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2">@foreach($statuses as $item)<option value="{{ $item->id }}">{{ $item->name }}</option>@endforeach</select></div>
|
||||
<div><label class="text-sm font-medium">Локация</label><input wire:model.defer="location" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
<div><label class="text-sm font-medium">Плановая дата</label><input wire:model.defer="plannedAt" type="datetime-local" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
<div><label class="text-sm font-medium">Дедлайн</label><input wire:model.defer="deadlineAt" type="datetime-local" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
<div class="md:col-span-2"><label class="text-sm font-medium">Вложения</label><input wire:model="attachments" type="file" multiple class="mt-1 block">@error('attachments.*')<p class="text-xs text-rose-600">{{ $message }}</p>@enderror</div>
|
||||
</div>
|
||||
<div class="flex justify-end gap-2">
|
||||
<a href="{{ route('requests.index') }}" class="rounded-xl border border-slate-300 px-4 py-2">Отмена</a>
|
||||
<button type="submit" class="rounded-xl bg-slate-900 text-white px-4 py-2">Сохранить</button>
|
||||
</div>
|
||||
</form>
|
||||
</div></div>
|
||||
@@ -0,0 +1,51 @@
|
||||
<div class="space-y-4">
|
||||
<div class="card"><div class="card-body">
|
||||
<div class="grid gap-3 md:grid-cols-2 xl:grid-cols-6">
|
||||
<input wire:model.live.debounce.400ms="search" type="text" placeholder="Поиск по тикету/заголовку" class="rounded-xl border border-slate-300 px-3 py-2">
|
||||
<select wire:model.live="statusId" class="rounded-xl border border-slate-300 px-3 py-2"><option value="">Все статусы</option>@foreach($statuses as $s)<option value="{{ $s->id }}">{{ $s->name }}</option>@endforeach</select>
|
||||
<select wire:model.live="priorityId" class="rounded-xl border border-slate-300 px-3 py-2"><option value="">Все приоритеты</option>@foreach($priorities as $p)<option value="{{ $p->id }}">{{ $p->name }}</option>@endforeach</select>
|
||||
<select wire:model.live="typeId" class="rounded-xl border border-slate-300 px-3 py-2"><option value="">Все типы</option>@foreach($types as $t)<option value="{{ $t->id }}">{{ $t->name }}</option>@endforeach</select>
|
||||
<select wire:model.live="technicianId" class="rounded-xl border border-slate-300 px-3 py-2"><option value="">Все техники</option>@foreach($technicians as $t)<option value="{{ $t->id }}">{{ $t->name }}</option>@endforeach</select>
|
||||
<button wire:click="clearFilters" class="rounded-xl border border-slate-300 px-3 py-2 hover:bg-slate-50">Сброс</button>
|
||||
<button wire:click="exportCsv" class="rounded-xl border border-slate-300 px-3 py-2 text-sm hover:bg-slate-50">Экспорт CSV</button>
|
||||
</div>
|
||||
</div></div>
|
||||
|
||||
<div class="card overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead class="bg-slate-50 text-slate-600">
|
||||
<tr>
|
||||
<th class="px-3 py-2"><input type="checkbox" wire:click="$set('selected', {{ json_encode($requests->pluck('id')->toArray()) }})"></th>
|
||||
<th class="px-3 py-2 text-left cursor-pointer" wire:click="sortBy('ticket_number')">#Ticket</th>
|
||||
<th class="px-3 py-2 text-left cursor-pointer" wire:click="sortBy('title')">Заголовок</th>
|
||||
<th class="px-3 py-2 text-left">Клиент</th>
|
||||
<th class="px-3 py-2 text-left">Исполнитель</th>
|
||||
<th class="px-3 py-2 text-left">Приоритет</th>
|
||||
<th class="px-3 py-2 text-left">Статус</th>
|
||||
<th class="px-3 py-2 text-left">Дедлайн</th>
|
||||
<th class="px-3 py-2 text-left">Создана</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($requests as $request)
|
||||
<tr class="border-t border-slate-100 {{ $request->isOverdue() ? 'bg-rose-50' : '' }}">
|
||||
<td class="px-3 py-2"><input type="checkbox" wire:model="selected" value="{{ $request->id }}"></td>
|
||||
<td class="px-3 py-2"><a class="font-medium text-slate-900 hover:underline" href="{{ route('requests.show', $request) }}">{{ $request->ticket_number }}</a></td>
|
||||
<td class="px-3 py-2">{{ $request->title }}</td>
|
||||
<td class="px-3 py-2">{{ $request->client?->name }}</td>
|
||||
<td class="px-3 py-2">{{ $request->assignee?->name ?? '—' }}</td>
|
||||
<td class="px-3 py-2"><span class="inline-flex items-center rounded-full px-2 py-1 text-xs text-white" style="background-color: {{ $request->priority?->color_hex }}">{{ $request->priority?->name }}</span></td>
|
||||
<td class="px-3 py-2"><span class="inline-flex items-center rounded-full px-2 py-1 text-xs text-white" style="background-color: {{ $request->status?->color_hex }}">{{ $request->status?->name }}</span></td>
|
||||
<td class="px-3 py-2 {{ $request->isOverdue() ? 'text-rose-600 font-medium' : '' }}">{{ $request->deadline_at?->format('d.m.Y H:i') ?? '—' }}</td>
|
||||
<td class="px-3 py-2">{{ $request->created_at?->format('d.m.Y H:i') }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="9" class="px-3 py-8 text-center text-slate-500">Нет заявок.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="p-4">{{ $requests->links() }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,121 @@
|
||||
<div class="space-y-4">
|
||||
<div class="card"><div class="card-body">
|
||||
<div class="flex flex-wrap justify-between gap-3">
|
||||
<div>
|
||||
<h1 class="text-xl font-semibold">{{ $request->ticket_number }} — {{ $request->title }}</h1>
|
||||
<p class="text-sm text-slate-500 mt-1">Создана {{ $request->created_at?->format('d.m.Y H:i') }}</p>
|
||||
</div>
|
||||
@if(auth()->user()->isAdmin() || auth()->user()->isManager())
|
||||
<div class="flex gap-2">
|
||||
<a href="{{ route('requests.edit', $request) }}" class="rounded-xl border border-slate-300 px-3 py-2 text-sm">Редактировать</a>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 mt-4">
|
||||
<div class="rounded-xl border border-slate-200 p-3 space-y-2">
|
||||
<p><span class="text-slate-500">Клиент:</span> {{ $request->client?->name }}</p>
|
||||
@if(!auth()->user()->isClient())
|
||||
<p><span class="text-slate-500">Исполнитель:</span> {{ $request->assignee?->name ?? '—' }}</p>
|
||||
@endif
|
||||
<p><span class="text-slate-500">Тип:</span> {{ $request->type?->name }}</p>
|
||||
<p><span class="text-slate-500">Приоритет:</span> <span class="inline-flex rounded-full px-2 py-1 text-xs text-white" style="background-color: {{ $request->priority?->color_hex }}">{{ $request->priority?->name }}</span></p>
|
||||
<p><span class="text-slate-500">Статус:</span> <span class="inline-flex rounded-full px-2 py-1 text-xs text-white" style="background-color: {{ $request->status?->color_hex }}">{{ $request->status?->name }}</span></p>
|
||||
<p><span class="text-slate-500">Дедлайн:</span> {{ $request->deadline_at?->format('d.m.Y H:i') ?? '—' }}</p>
|
||||
</div>
|
||||
<div class="rounded-xl border border-slate-200 p-3">
|
||||
<p class="text-sm text-slate-500 mb-1">Описание</p>
|
||||
<p class="text-sm leading-6 whitespace-pre-wrap">{{ $request->description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(!$allowedStatuses->isEmpty())
|
||||
<div class="mt-4 flex items-center gap-2">
|
||||
<select wire:model="nextStatusId" class="rounded-xl border border-slate-300 px-3 py-2 text-sm"><option value="">Сменить статус</option>@foreach($allowedStatuses as $status)<option value="{{ $status->id }}">{{ $status->name }}</option>@endforeach</select>
|
||||
<button wire:click="changeStatus" class="rounded-xl bg-slate-900 text-white px-3 py-2 text-sm">Применить</button>
|
||||
</div>
|
||||
@endif
|
||||
</div></div>
|
||||
|
||||
<div class="grid gap-4 lg:grid-cols-3">
|
||||
<div class="lg:col-span-2 space-y-4">
|
||||
<div class="card"><div class="card-body">
|
||||
<h2 class="text-lg font-semibold">Комментарии</h2>
|
||||
<div class="mt-3 space-y-2">
|
||||
@forelse($comments as $comment)
|
||||
<div class="rounded-xl border border-slate-200 p-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<p class="text-sm font-medium">{{ $comment->user?->name }}</p>
|
||||
<p class="text-xs text-slate-500">{{ $comment->created_at?->format('d.m.Y H:i') }}</p>
|
||||
</div>
|
||||
@if($comment->is_internal)<p class="text-[11px] text-indigo-600 font-semibold uppercase mt-1">Внутренняя заметка</p>@endif
|
||||
<p class="text-sm mt-2 whitespace-pre-wrap">{{ $comment->body }}</p>
|
||||
@if($comment->user_id === auth()->id())
|
||||
<button wire:click="deleteComment({{ $comment->id }})" class="mt-2 text-xs text-rose-600">Удалить</button>
|
||||
@endif
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-sm text-slate-500">Комментариев пока нет.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<div class="mt-4 space-y-2">
|
||||
<textarea wire:model.defer="commentBody" rows="3" class="w-full rounded-xl border border-slate-300 px-3 py-2" placeholder="Добавить комментарий"></textarea>
|
||||
@if(!auth()->user()->isClient())
|
||||
<label class="inline-flex items-center gap-2 text-sm"><input wire:model="commentInternal" type="checkbox" class="rounded"> Внутренняя заметка</label>
|
||||
@endif
|
||||
<button wire:click="addComment" class="rounded-xl bg-slate-900 text-white px-3 py-2 text-sm">Отправить</button>
|
||||
</div>
|
||||
</div></div>
|
||||
|
||||
<div class="card"><div class="card-body">
|
||||
<h2 class="text-lg font-semibold">История активности</h2>
|
||||
<div class="mt-3 space-y-2">
|
||||
@forelse($activity as $log)
|
||||
<div class="rounded-lg border border-slate-200 p-2 text-sm">
|
||||
<span class="font-medium">{{ $log->user?->name ?? 'Система' }}</span> — {{ $log->description }}
|
||||
<span class="text-xs text-slate-500">({{ $log->created_at?->format('d.m.Y H:i') }})</span>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-sm text-slate-500">История пуста.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div></div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="card"><div class="card-body">
|
||||
<h2 class="text-lg font-semibold">Вложения</h2>
|
||||
<div class="mt-3 space-y-2 text-sm">
|
||||
@forelse($request->attachments as $file)
|
||||
<a href="{{ route('attachments.download', ['request' => $request->id, 'attachment' => $file->id]) }}" class="block rounded-lg border border-slate-200 px-3 py-2 hover:bg-slate-50">{{ $file->filename }}</a>
|
||||
@empty
|
||||
<p class="text-slate-500">Вложений нет.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div></div>
|
||||
|
||||
@if($request->equipment)
|
||||
<div class="card"><div class="card-body">
|
||||
<h2 class="text-lg font-semibold">Оборудование</h2>
|
||||
<p class="text-sm mt-2">{{ $request->equipment->name }}</p>
|
||||
<p class="text-xs text-slate-500">{{ $request->equipment->serial_number }}</p>
|
||||
</div></div>
|
||||
@endif
|
||||
|
||||
@if(auth()->user()->isClient() && in_array($request->status?->slug, ['resolved','closed'], true))
|
||||
<div class="card"><div class="card-body">
|
||||
<h2 class="text-lg font-semibold">Оценка заявки</h2>
|
||||
<div class="mt-2 space-y-2">
|
||||
<select wire:model="rating" class="w-full rounded-xl border border-slate-300 px-3 py-2">
|
||||
<option value="">Выберите оценку</option>
|
||||
@for($i = 1; $i <= 5; $i++) <option value="{{ $i }}">{{ $i }} ⭐</option> @endfor
|
||||
</select>
|
||||
<textarea wire:model="ratingComment" rows="3" class="w-full rounded-xl border border-slate-300 px-3 py-2" placeholder="Комментарий"></textarea>
|
||||
<button wire:click="submitRating" class="rounded-xl bg-slate-900 text-white px-3 py-2 text-sm">Сохранить оценку</button>
|
||||
</div>
|
||||
</div></div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
<div class="space-y-4">
|
||||
<div class="card"><div class="card-body flex flex-wrap gap-2 items-center">
|
||||
<select wire:model.live="userId" class="rounded-xl border border-slate-300 px-3 py-2 text-sm"><option value="">Все пользователи</option>@foreach($users as $user)<option value="{{ $user->id }}">{{ $user->name }}</option>@endforeach</select>
|
||||
<input wire:model.live.debounce.300ms="action" class="rounded-xl border border-slate-300 px-3 py-2 text-sm" placeholder="Тип действия">
|
||||
<input wire:model.live="dateFrom" type="date" class="rounded-xl border border-slate-300 px-3 py-2 text-sm">
|
||||
<input wire:model.live="dateTo" type="date" class="rounded-xl border border-slate-300 px-3 py-2 text-sm">
|
||||
<button wire:click="exportCsv" class="rounded-xl bg-slate-900 text-white px-3 py-2 text-sm">Экспорт CSV</button>
|
||||
</div></div>
|
||||
<div class="card overflow-hidden"><table class="min-w-full text-sm"><thead class="bg-slate-50"><tr><th class="px-3 py-2 text-left">Пользователь</th><th class="px-3 py-2 text-left">Действие</th><th class="px-3 py-2 text-left">Сущность</th><th class="px-3 py-2 text-left">Описание</th><th class="px-3 py-2 text-left">IP</th><th class="px-3 py-2 text-left">Дата</th></tr></thead><tbody>@forelse($logs as $log)<tr class="border-t border-slate-100"><td class="px-3 py-2">{{ $log->user?->name ?? 'Система' }}</td><td class="px-3 py-2">{{ $log->action }}</td><td class="px-3 py-2">{{ class_basename($log->model_type) }}</td><td class="px-3 py-2">{{ $log->description }}</td><td class="px-3 py-2">{{ $log->ip_address }}</td><td class="px-3 py-2">{{ $log->created_at?->format('d.m.Y H:i:s') }}</td></tr>@empty<tr><td colspan="6" class="px-3 py-8 text-center text-slate-500">Нет записей</td></tr>@endforelse</tbody></table><div class="p-4">{{ $logs->links() }}</div></div>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
<div class="card max-w-4xl"><div class="card-body">
|
||||
<form wire:submit="save" class="space-y-4">
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div><label class="text-sm font-medium">Название компании</label><input wire:model.defer="companyName" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
<div><label class="text-sm font-medium">Часовой пояс</label><select wire:model.defer="timezone" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2">@foreach($russianTimezones as $value => $label)<option value="{{ $value }}">{{ $label }}</option>@endforeach</select></div>
|
||||
<div><label class="text-sm font-medium">SLA по умолчанию (часы)</label><input wire:model.defer="defaultSlaHours" type="number" min="1" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
<div class="md:col-span-2"><label class="inline-flex items-center gap-2"><input wire:model.defer="maintenanceMode" type="checkbox" class="rounded"> <span class="text-sm">Режим обслуживания</span></label></div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-slate-200 p-4">
|
||||
<h3 class="font-semibold mb-3">Email-уведомления</h3>
|
||||
<div class="grid gap-2 md:grid-cols-2 text-sm">
|
||||
<label class="inline-flex items-center gap-2"><input wire:model.defer="notifyAssigned" type="checkbox" class="rounded"> Назначение заявки</label>
|
||||
<label class="inline-flex items-center gap-2"><input wire:model.defer="notifyStatusChanged" type="checkbox" class="rounded"> Изменение статуса</label>
|
||||
<label class="inline-flex items-center gap-2"><input wire:model.defer="notifyCommentAdded" type="checkbox" class="rounded"> Новый комментарий</label>
|
||||
<label class="inline-flex items-center gap-2"><input wire:model.defer="notifyOverdue" type="checkbox" class="rounded"> Просрочка</label>
|
||||
<label class="inline-flex items-center gap-2"><input wire:model.defer="notifySlaWarning" type="checkbox" class="rounded"> Предупреждение SLA</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end"><button class="rounded-xl bg-slate-900 text-white px-4 py-2">Сохранить настройки</button></div>
|
||||
</form>
|
||||
</div></div>
|
||||
@@ -0,0 +1,36 @@
|
||||
<div class="space-y-4">
|
||||
<div class="card"><div class="card-body flex flex-wrap gap-2">
|
||||
<select wire:model.live="priorityId" class="rounded-xl border border-slate-300 px-3 py-2 text-sm"><option value="">Все приоритеты</option>@foreach($priorities as $p)<option value="{{ $p->id }}">{{ $p->name }}</option>@endforeach</select>
|
||||
<select wire:model.live="deadlineFilter" class="rounded-xl border border-slate-300 px-3 py-2 text-sm"><option value="">Все дедлайны</option><option value="today">Сегодня</option><option value="overdue">Просроченные</option></select>
|
||||
</div></div>
|
||||
|
||||
<div class="grid gap-4 lg:grid-cols-4">
|
||||
@foreach($statuses as $status)
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h3 class="font-semibold text-sm mb-3" style="color: {{ $status->color_hex }}">{{ $status->name }}</h3>
|
||||
<div class="space-y-3">
|
||||
@foreach(($requestsByStatus[$status->slug] ?? collect()) as $task)
|
||||
<div class="rounded-xl border border-slate-200 p-3">
|
||||
<a href="{{ route('requests.show', $task) }}" class="font-medium text-sm hover:underline">{{ $task->ticket_number }} — {{ $task->title }}</a>
|
||||
<p class="text-xs text-slate-500 mt-1">{{ $task->client?->name }} • {{ $task->equipment?->name ?? 'Без оборудования' }}</p>
|
||||
<p class="text-xs mt-1 {{ $task->isOverdue() ? 'text-rose-600 font-semibold' : 'text-slate-500' }}">{{ $task->deadline_at?->diffForHumans() ?? 'Без дедлайна' }}</p>
|
||||
|
||||
<div class="mt-2 flex gap-1 flex-wrap">
|
||||
@foreach($this->transitionOptions($task) as $transition)
|
||||
<button wire:click="updateStatus({{ $task->id }}, '{{ $transition->id }}')" class="text-[10px] rounded border border-slate-300 px-1.5 py-1">{{ $transition->name }}</button>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<input wire:model.defer="quickComments.{{ $task->id }}" type="text" class="w-full rounded-lg border border-slate-300 px-2 py-1 text-xs" placeholder="Внутренняя заметка">
|
||||
<button wire:click="addQuickComment({{ $task->id }})" class="mt-1 text-xs rounded bg-slate-900 text-white px-2 py-1">Добавить</button>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,15 @@
|
||||
<div class="card"><div class="card-body">
|
||||
<form wire:submit="save" class="space-y-4">
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div><label class="text-sm font-medium">Имя</label><input wire:model.defer="name" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
<div><label class="text-sm font-medium">Email</label><input wire:model.defer="email" type="email" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
<div><label class="text-sm font-medium">Роль</label><select wire:model.defer="role" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"><option value="admin">Админ</option><option value="manager">Менеджер</option><option value="technician">Техник</option><option value="client">Клиент</option></select></div>
|
||||
<div><label class="text-sm font-medium">Отдел</label><select wire:model.defer="departmentId" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"><option value="">Не выбран</option>@foreach($departments as $item)<option value="{{ $item->id }}">{{ $item->name }}</option>@endforeach</select></div>
|
||||
<div><label class="text-sm font-medium">Телефон</label><input wire:model.defer="phone" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
<div><label class="text-sm font-medium">Пароль</label><input wire:model.defer="password" type="password" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
<div><label class="text-sm font-medium">Подтверждение пароля</label><input wire:model.defer="password_confirmation" type="password" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></div>
|
||||
<div class="md:col-span-2"><label class="inline-flex items-center gap-2"><input wire:model.defer="isActive" type="checkbox" class="rounded"> <span class="text-sm">Активен</span></label></div>
|
||||
</div>
|
||||
<div class="flex justify-end gap-2"><a href="{{ route('users.index') }}" class="rounded-xl border border-slate-300 px-4 py-2">Отмена</a><button class="rounded-xl bg-slate-900 text-white px-4 py-2">Сохранить</button></div>
|
||||
</form>
|
||||
</div></div>
|
||||
@@ -0,0 +1,23 @@
|
||||
<div class="space-y-4">
|
||||
<div class="card"><div class="card-body flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<h1 class="text-xl font-semibold">{{ $user->name }}</h1>
|
||||
<p class="text-sm text-slate-500">{{ $user->email }} • {{ $user->role->label() }}</p>
|
||||
<p class="text-sm text-slate-500 mt-1">{{ $user->department?->name ?? 'Без отдела' }}</p>
|
||||
</div>
|
||||
@if(auth()->user()->isAdmin())
|
||||
<a href="{{ route('users.edit', $user) }}" class="rounded-xl border border-slate-300 px-3 py-2 text-sm">Редактировать</a>
|
||||
@endif
|
||||
</div></div>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-3">
|
||||
<div class="card"><div class="card-body"><p class="text-sm text-slate-500">Назначено заявок</p><p class="text-2xl font-semibold mt-1">{{ $assigned }}</p></div></div>
|
||||
<div class="card"><div class="card-body"><p class="text-sm text-slate-500">Решено заявок</p><p class="text-2xl font-semibold mt-1">{{ $resolved }}</p></div></div>
|
||||
<div class="card"><div class="card-body"><p class="text-sm text-slate-500">Средний рейтинг</p><p class="text-2xl font-semibold mt-1">{{ $avgRating }}</p></div></div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 lg:grid-cols-2">
|
||||
<div class="card"><div class="card-body"><h2 class="text-lg font-semibold">Последние заявки</h2><div class="space-y-2 mt-3">@foreach($recentRequests as $item)<a href="{{ route('requests.show', $item) }}" class="block rounded-lg border border-slate-200 px-3 py-2 hover:bg-slate-50 text-sm">{{ $item->ticket_number }} — {{ $item->title }}</a>@endforeach</div></div></div>
|
||||
<div class="card"><div class="card-body"><h2 class="text-lg font-semibold">Активность</h2><div class="space-y-2 mt-3">@foreach($activities as $item)<div class="rounded-lg border border-slate-200 px-3 py-2 text-sm">{{ $item->description }}<p class="text-xs text-slate-500 mt-1">{{ $item->created_at?->format('d.m.Y H:i') }}</p></div>@endforeach</div></div></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,33 @@
|
||||
<div class="space-y-4">
|
||||
<div class="flex flex-wrap items-center gap-2 justify-between">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<input wire:model.live.debounce.300ms="search" class="rounded-xl border border-slate-300 px-3 py-2" placeholder="Поиск пользователей">
|
||||
<select wire:model.live="role" class="rounded-xl border border-slate-300 px-3 py-2"><option value="">Все роли</option><option value="admin">Админ</option><option value="manager">Менеджер</option><option value="technician">Техник</option><option value="client">Клиент</option></select>
|
||||
<select wire:model.live="status" class="rounded-xl border border-slate-300 px-3 py-2"><option value="">Все статусы</option><option value="active">Активные</option><option value="inactive">Неактивные</option></select>
|
||||
</div>
|
||||
@if(auth()->user()->isAdmin())
|
||||
<a href="{{ route('users.create') }}" class="rounded-xl bg-slate-900 text-white px-4 py-2 text-sm">Создать пользователя</a>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="card overflow-hidden">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead class="bg-slate-50"><tr><th class="px-3 py-2 text-left">Пользователь</th><th class="px-3 py-2 text-left">Роль</th><th class="px-3 py-2 text-left">Отдел</th><th class="px-3 py-2 text-left">Статус</th><th class="px-3 py-2 text-left">Последний вход</th><th class="px-3 py-2"></th></tr></thead>
|
||||
<tbody>
|
||||
@forelse($users as $user)
|
||||
<tr class="border-t border-slate-100">
|
||||
<td class="px-3 py-2"><p class="font-medium">{{ $user->name }}</p><p class="text-xs text-slate-500">{{ $user->email }}</p></td>
|
||||
<td class="px-3 py-2">{{ $user->role->label() }}</td>
|
||||
<td class="px-3 py-2">{{ $user->department?->name ?? '—' }}</td>
|
||||
<td class="px-3 py-2">{!! $user->is_active ? '<span class="text-emerald-600">Активен</span>' : '<span class="text-rose-600">Неактивен</span>' !!}</td>
|
||||
<td class="px-3 py-2">{{ $user->last_login_at?->format('d.m.Y H:i') ?? '—' }}</td>
|
||||
<td class="px-3 py-2 text-right"><a href="{{ route('users.show', $user) }}" class="text-sm text-slate-700 hover:underline">Открыть</a></td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="px-3 py-8 text-center text-slate-500">Нет пользователей</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="p-4">{{ $users->links() }}</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user