first&last
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user