новый дизайн
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
<script setup>
|
||||
import { Head, useForm, usePage } from '@inertiajs/vue3';
|
||||
import { computed, ref } from 'vue';
|
||||
import PortalLayout from '../../Layouts/PortalLayout.vue';
|
||||
import { arrowSvg } from '../Landing/Components/symbols';
|
||||
|
||||
const props = defineProps({
|
||||
site: { type: Object, required: true },
|
||||
auth: { type: Object, required: true },
|
||||
urls: { type: Object, required: true },
|
||||
csrfToken: { type: String, required: true },
|
||||
flash: { type: Object, default: () => ({}) },
|
||||
account: { type: Object, required: true },
|
||||
characters: { type: Array, default: () => [] },
|
||||
client: { type: Object, required: true },
|
||||
wallet: { type: Object, required: true },
|
||||
});
|
||||
|
||||
const page = usePage();
|
||||
const sharedErrors = computed(() => page.props.errors || {});
|
||||
const activeSection = ref('overview');
|
||||
const passwordForm = useForm({
|
||||
current_password: '',
|
||||
password: '',
|
||||
password_confirmation: '',
|
||||
});
|
||||
|
||||
const sections = [
|
||||
{ id: 'overview', label: 'Обзор', number: '01' },
|
||||
{ id: 'characters', label: 'Персонажи', number: '02' },
|
||||
{ id: 'wallet', label: 'Слёзы Элуны', number: '03' },
|
||||
{ id: 'security', label: 'Безопасность', number: '04' },
|
||||
];
|
||||
|
||||
function goTo(id) {
|
||||
activeSection.value = id;
|
||||
document.getElementById(id)?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
|
||||
function updatePassword() {
|
||||
passwordForm.post(props.urls.password, {
|
||||
preserveScroll: true,
|
||||
onSuccess: () => passwordForm.reset(),
|
||||
onError: () => {
|
||||
passwordForm.reset('password', 'password_confirmation');
|
||||
goTo('security');
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Head>
|
||||
<title>Личный кабинет — {{ site.name }}</title>
|
||||
<meta name="description" content="Персонажи, баланс и настройки игрового аккаунта MoonWell." />
|
||||
</Head>
|
||||
|
||||
<PortalLayout :site="site" :auth="auth" :urls="urls" :flash="flash">
|
||||
<section class="portal-welcome wrap">
|
||||
<div>
|
||||
<span class="eyebrow">Личный кабинет</span>
|
||||
<h1 class="h-display">С возвращением, <em>{{ account.username }}</em></h1>
|
||||
<p class="lead">Твой аккаунт, герои и всё необходимое для следующего путешествия.</p>
|
||||
</div>
|
||||
<div class="portal-welcome__status">
|
||||
<span :class="['portal-status-dot', { danger: account.locked }]"></span>
|
||||
<span>
|
||||
<small>Статус аккаунта</small>
|
||||
<strong>{{ account.status_label }}</strong>
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="portal-dashboard wrap">
|
||||
<aside class="portal-sidebar">
|
||||
<span class="portal-sidebar__label">Навигация</span>
|
||||
<button
|
||||
v-for="section in sections"
|
||||
:key="section.id"
|
||||
:class="{ active: activeSection === section.id }"
|
||||
type="button"
|
||||
@click="goTo(section.id)"
|
||||
>
|
||||
<span>{{ section.number }}</span>
|
||||
{{ section.label }}
|
||||
</button>
|
||||
<div class="portal-sidebar__balance">
|
||||
<small>Баланс</small>
|
||||
<strong>{{ wallet.formatted_amount }}</strong>
|
||||
<span>Слёз Элуны</span>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div class="portal-content">
|
||||
<section id="overview" class="portal-section">
|
||||
<div class="portal-section__head">
|
||||
<div>
|
||||
<span class="eyebrow">Обзор</span>
|
||||
<h2 class="h-serif">Данные аккаунта</h2>
|
||||
</div>
|
||||
<a v-if="auth.is_admin" class="btn btn-ghost portal-small-button" :href="urls.admin">Открыть админку</a>
|
||||
</div>
|
||||
|
||||
<div class="portal-grid portal-grid--account">
|
||||
<article class="portal-card portal-account-card corners">
|
||||
<dl class="portal-stats">
|
||||
<div><dt>Логин</dt><dd>{{ account.username }}</dd></div>
|
||||
<div><dt>Email</dt><dd>{{ account.email }}</dd></div>
|
||||
<div><dt>Уровень доступа</dt><dd>{{ account.access_label }}</dd></div>
|
||||
<div><dt>Регистрация</dt><dd>{{ account.joined_at }}</dd></div>
|
||||
<div><dt>Последний вход</dt><dd>{{ account.last_login_at }}</dd></div>
|
||||
<div><dt>Персонажей</dt><dd>{{ characters.length }}</dd></div>
|
||||
</dl>
|
||||
</article>
|
||||
|
||||
<article class="portal-card portal-launcher-card corners">
|
||||
<span class="eyebrow">Лаунчер</span>
|
||||
<h3 class="h-serif">Врата в Азерот</h3>
|
||||
<p>Лаунчер автоматически скачает клиент, настроит realmlist и установит обновления.</p>
|
||||
<a class="btn btn-primary" :href="urls.client">
|
||||
Скачать лаунчер <span v-html="arrowSvg"></span>
|
||||
</a>
|
||||
<span class="portal-file-note">{{ client.filename }}</span>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="characters" class="portal-section">
|
||||
<div class="portal-section__head">
|
||||
<div>
|
||||
<span class="eyebrow">Мои персонажи</span>
|
||||
<h2 class="h-serif">Герои аккаунта</h2>
|
||||
</div>
|
||||
<span class="portal-section__count">{{ characters.length }} {{ characters.length === 1 ? 'герой' : 'героев' }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="characters.length" class="portal-character-grid">
|
||||
<article v-for="character in characters" :key="character.name" class="portal-card portal-character-card">
|
||||
<div class="portal-character-card__head">
|
||||
<span class="portal-character-level"><span>{{ character.level }}</span></span>
|
||||
<span :class="['portal-online', { online: character.online }]">
|
||||
{{ character.online ? 'Онлайн' : 'Оффлайн' }}
|
||||
</span>
|
||||
</div>
|
||||
<h3 class="h-serif">{{ character.name }}</h3>
|
||||
<p>{{ character.race }} · {{ character.class }}</p>
|
||||
<dl>
|
||||
<div><dt>Пол</dt><dd>{{ character.gender }}</dd></div>
|
||||
<div><dt>Золото</dt><dd>{{ character.money }}</dd></div>
|
||||
<div><dt>Наработано</dt><dd>{{ character.played }}</dd></div>
|
||||
</dl>
|
||||
</article>
|
||||
</div>
|
||||
<article v-else class="portal-card portal-empty">
|
||||
<span class="portal-empty__moon">☾</span>
|
||||
<h3 class="h-serif">Персонажей пока нет</h3>
|
||||
<p>Создай первого героя в игре — он появится здесь автоматически.</p>
|
||||
<a class="btn btn-primary" :href="urls.client">Скачать лаунчер</a>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section id="wallet" class="portal-section">
|
||||
<div class="portal-section__head">
|
||||
<div>
|
||||
<span class="eyebrow">Слёзы Элуны</span>
|
||||
<h2 class="h-serif">Баланс и операции</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="portal-grid portal-grid--wallet">
|
||||
<article class="portal-card portal-wallet-card corners">
|
||||
<span class="portal-card-label">Доступно сейчас</span>
|
||||
<div class="portal-balance">{{ wallet.formatted_amount }}</div>
|
||||
<p>Слёз Элуны</p>
|
||||
|
||||
<form class="portal-deposit" method="POST" :action="urls.deposit">
|
||||
<input type="hidden" name="_token" :value="csrfToken" />
|
||||
<label class="portal-field">
|
||||
<span>Сумма пополнения, ₽</span>
|
||||
<input
|
||||
type="number"
|
||||
name="amount"
|
||||
:min="wallet.minimum_deposit"
|
||||
:max="wallet.maximum_deposit"
|
||||
step="0.01"
|
||||
:value="wallet.deposit_amount"
|
||||
required
|
||||
/>
|
||||
<small v-if="sharedErrors.amount" class="portal-field-error">{{ sharedErrors.amount }}</small>
|
||||
</label>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
Пополнить <span v-html="arrowSvg"></span>
|
||||
</button>
|
||||
</form>
|
||||
<div class="portal-rate">
|
||||
<span>1 ₽ = {{ wallet.tears_per_ruble }} Слёз Элуны</span>
|
||||
<span>От {{ wallet.minimum_deposit }} до {{ wallet.formatted_maximum_deposit }} ₽</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="portal-card portal-history-card">
|
||||
<span class="portal-card-label">Последние операции</span>
|
||||
<div v-if="wallet.transactions.length" class="portal-history">
|
||||
<div v-for="transaction in wallet.transactions" :key="transaction.id" class="portal-history__row">
|
||||
<span>
|
||||
<strong>{{ transaction.label }}</strong>
|
||||
<small>{{ transaction.created_at }}</small>
|
||||
</span>
|
||||
<strong :class="{ positive: transaction.is_positive }">
|
||||
{{ transaction.is_positive ? '+' : '' }}{{ transaction.formatted_amount }}
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="portal-history-empty">Операций пока нет.</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="security" class="portal-section">
|
||||
<div class="portal-section__head">
|
||||
<div>
|
||||
<span class="eyebrow">Безопасность</span>
|
||||
<h2 class="h-serif">Сменить пароль</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="portal-card portal-security-card corners">
|
||||
<div class="portal-security-card__copy">
|
||||
<h3 class="h-serif">Защити свой аккаунт</h3>
|
||||
<p>После обновления новый пароль сразу начнёт действовать и в игре, и в личном кабинете.</p>
|
||||
<ul>
|
||||
<li>От 8 до 32 символов</li>
|
||||
<li>Хотя бы одна буква и одна цифра</li>
|
||||
<li>Не используй пароль от почты</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<form class="portal-password-form" @submit.prevent="updatePassword">
|
||||
<label :class="['portal-field', { 'has-error': passwordForm.errors.current_password }]">
|
||||
<span>Текущий пароль</span>
|
||||
<input v-model="passwordForm.current_password" type="password" autocomplete="current-password" maxlength="32" required />
|
||||
<small v-if="passwordForm.errors.current_password">{{ passwordForm.errors.current_password }}</small>
|
||||
</label>
|
||||
<label :class="['portal-field', { 'has-error': passwordForm.errors.password }]">
|
||||
<span>Новый пароль</span>
|
||||
<input v-model="passwordForm.password" type="password" autocomplete="new-password" maxlength="32" required />
|
||||
<small v-if="passwordForm.errors.password">{{ passwordForm.errors.password }}</small>
|
||||
</label>
|
||||
<label :class="['portal-field', { 'has-error': passwordForm.errors.password }]">
|
||||
<span>Повтори новый пароль</span>
|
||||
<input v-model="passwordForm.password_confirmation" type="password" autocomplete="new-password" maxlength="32" required />
|
||||
</label>
|
||||
<button class="btn btn-primary portal-submit" type="submit" :disabled="passwordForm.processing">
|
||||
{{ passwordForm.processing ? 'Обновляем…' : 'Обновить пароль' }}
|
||||
<span v-html="arrowSvg"></span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</PortalLayout>
|
||||
</template>
|
||||
Reference in New Issue
Block a user