админка

This commit is contained in:
2026-03-13 23:33:35 +04:00
parent bae063a4fa
commit eaedeeb52b
41 changed files with 2506 additions and 67 deletions
@@ -0,0 +1,166 @@
@extends('layouts.portal')
@section('portal-content')
<section class="section">
<div class="section-heading">
<span class="eyebrow">Инвайты</span>
<h2>Управление инвайт-кодами</h2>
<p>Без действующего инвайт-кода регистрация нового аккаунта недоступна.</p>
</div>
<div class="portal-grid">
<article class="content-card">
<h3>Создать инвайт-коды</h3>
<dl class="stats-list stats-list--compact">
<div>
<dt>Всего</dt>
<dd>{{ $inviteStats['total'] }}</dd>
</div>
<div>
<dt>Доступно</dt>
<dd>{{ $inviteStats['available'] }}</dd>
</div>
<div>
<dt>Использовано</dt>
<dd>{{ $inviteStats['redeemed'] }}</dd>
</div>
</dl>
<form class="inline-form" method="POST" action="{{ route('admin.invite-codes.store') }}">
@csrf
<label class="form-field">
<span>Количество</span>
<input class="form-control @error('quantity') is-invalid @enderror" type="number" min="1" max="50" name="quantity" value="{{ old('quantity', 1) }}" required>
@error('quantity')<span class="form-error">{{ $message }}</span>@enderror
</label>
<button class="button button--gold" type="submit">Создать коды</button>
</form>
</article>
<article class="content-card">
<h3>Последние инвайт-коды</h3>
@if ($inviteCodes->isNotEmpty())
<div class="character-list">
@foreach ($inviteCodes as $invite)
<div class="character-row">
<div>
<strong>{{ $invite->code }}</strong>
<span>
Создал: {{ $invite->created_by_username ?: '—' }}
@if ($invite->created_at)
· {{ $invite->created_at->format('d.m.Y H:i') }}
@endif
</span>
</div>
<div>
<strong>{{ $invite->redeemed_at ? 'Использован' : 'Свободен' }}</strong>
<span>
{{ $invite->redeemed_by_username ?: 'ещё не использован' }}
</span>
</div>
</div>
@endforeach
</div>
@else
<div class="empty-inline">Инвайт-коды ещё не создавались.</div>
@endif
</article>
</div>
</section>
<section class="section">
<div class="section-heading">
<span class="eyebrow">Админка</span>
<h2>Аккаунты и персонажи</h2>
<p>Здесь видны все игровые аккаунты, их персонажи и текущий уровень доступа.</p>
</div>
<form class="search-form" method="GET" action="{{ route('admin.accounts.index') }}">
<input class="form-control" type="search" name="search" value="{{ $search }}" placeholder="Поиск по логину или email">
<button class="button button--gold" type="submit">Найти</button>
@if ($search !== '')
<a class="button button--ghost" href="{{ route('admin.accounts.index') }}">Сбросить</a>
@endif
</form>
</section>
<section class="section">
<div class="account-list">
@forelse ($accounts as $account)
<article class="content-card account-card">
<div class="account-card__header">
<div>
<h3>{{ $account['username'] }}</h3>
<p>ID #{{ $account['id'] }} · {{ $account['email'] !== '' ? $account['email'] : 'email не указан' }}</p>
</div>
<span class="tag">{{ $account['access_label'] }}</span>
</div>
<dl class="stats-list stats-list--compact">
<div>
<dt>Регистрация</dt>
<dd>{{ $account['joined_at'] }}</dd>
</div>
<div>
<dt>Последний вход</dt>
<dd>{{ $account['last_login_at'] }}</dd>
</div>
<div>
<dt>Статус</dt>
<dd>{{ $account['locked'] ? 'Заблокирован' : 'Активен' }}</dd>
</div>
<div>
<dt>Персонажей</dt>
<dd>{{ $account['characters']->count() }}</dd>
</div>
</dl>
<form class="inline-form" method="POST" action="{{ route('admin.accounts.access.update', $account['id']) }}">
@csrf
@method('PATCH')
<label class="form-field">
<span>Уровень доступа</span>
<select class="form-control @error('gmlevel') is-invalid @enderror" name="gmlevel">
@foreach ($accessLevels as $level => $label)
<option value="{{ $level }}" @selected($account['gm_level'] === $level)>{{ $label }}</option>
@endforeach
</select>
</label>
<button class="button button--gold" type="submit">Сохранить</button>
</form>
@if ($account['characters']->isNotEmpty())
<div class="character-list">
@foreach ($account['characters'] as $character)
<div class="character-row">
<div>
<strong>{{ $character['name'] }}</strong>
<span>{{ $character['race'] }} · {{ $character['class'] }}</span>
</div>
<div>
<strong>{{ $character['level'] }} ур.</strong>
<span>{{ $character['online'] ? 'Онлайн' : 'Оффлайн' }}</span>
</div>
</div>
@endforeach
</div>
@else
<div class="empty-inline">На аккаунте пока нет персонажей.</div>
@endif
</article>
@empty
<article class="content-card empty-state">
<h3>Аккаунты не найдены</h3>
<p>Измени поисковый запрос или попробуй позже.</p>
</article>
@endforelse
</div>
</section>
@endsection