Files
moonwell-web/resources/js/Pages/Admin/Shop.vue
T

160 lines
8.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import { Head, router, useForm } from '@inertiajs/vue3';
import AdminLayout from '../../Layouts/AdminLayout.vue';
import CategoryForm from './Components/CategoryForm.vue';
import ProductForm from './Components/ProductForm.vue';
import StatCards from './Components/StatCards.vue';
const props = defineProps({
site: { type: Object, required: true },
auth: { type: Object, required: true },
urls: { type: Object, required: true },
flash: { type: Object, default: () => ({}) },
categories: { type: Array, default: () => [] },
products: { type: Array, default: () => [] },
categoryStoreUrl: { type: String, required: true },
productStoreUrl: { type: String, required: true },
shopEnabled: { type: Boolean, required: true },
availabilityUpdateUrl: { type: String, required: true },
productTypes: { type: Array, required: true },
});
const availabilityForm = useForm({
enabled: props.shopEnabled,
});
const stats = [
{ label: 'Категории', value: props.categories.length },
{ label: 'Позиции', value: props.products.length },
{ label: 'Активные', value: props.products.filter((item) => item.enabled).length },
];
function destroy(url, message) {
if (window.confirm(message)) {
router.delete(url, { preserveScroll: true });
}
}
function updateAvailability(event) {
const previousValue = !event.target.checked;
availabilityForm.patch(props.availabilityUpdateUrl, {
preserveScroll: true,
onError: () => {
availabilityForm.enabled = previousValue;
},
});
}
</script>
<template>
<Head><title>Магазин {{ site.name }}</title></Head>
<AdminLayout :site="site" :auth="auth" :urls="urls" :flash="flash" current="shop">
<section class="admin-hero wrap">
<div>
<span class="eyebrow">Игровой сервер</span>
<h1 class="h-display">Магазин</h1>
<p class="lead">Категории, товары, награды и цены из базы игрового модуля.</p>
</div>
<div class="admin-hero__badge"><small>Позиций</small><strong>{{ products.length }}</strong></div>
</section>
<div class="admin-page wrap">
<section class="admin-section">
<StatCards :items="stats" />
<article class="portal-card admin-panel corners">
<span class="admin-kicker">Глобальная доступность</span>
<h2 class="h-serif">Магазин и пополнение</h2>
<p>
При выключении игровой магазин становится недоступен, а форма пополнения скрывается в личном кабинете.
</p>
<label class="admin-check">
<input
v-model="availabilityForm.enabled"
type="checkbox"
:disabled="availabilityForm.processing"
@change="updateAvailability"
/>
<span class="admin-check__box"></span>
<span>{{ availabilityForm.enabled ? 'Доступны' : 'Выключены' }}</span>
</label>
<small v-if="availabilityForm.errors.enabled" class="admin-field-error">
{{ availabilityForm.errors.enabled }}
</small>
</article>
<div class="admin-notice">
Изменения записываются прямо в базу <code>store</code>. После сохранения перезагрузи данные магазина командой модуля или перезапусти worldserver.
</div>
<div class="admin-split">
<article class="portal-card admin-panel corners">
<span class="admin-kicker">Навигация магазина</span>
<h2 class="h-serif">Новая категория</h2>
<CategoryForm :action="categoryStoreUrl" submit-label="Создать категорию" />
</article>
<article class="portal-card admin-panel corners">
<span class="admin-kicker">Контент магазина</span>
<h2 class="h-serif">Новая позиция</h2>
<ProductForm
v-if="categories.length"
:categories="categories"
:product-types="productTypes"
:action="productStoreUrl"
submit-label="Создать позицию"
/>
<div v-else class="admin-empty-inline">Сначала создай категорию.</div>
</article>
</div>
</section>
<section class="admin-section">
<div class="admin-section__head">
<div><span class="eyebrow">Навигация магазина</span><h2 class="h-serif">Категории</h2></div>
</div>
<div v-if="categories.length" class="admin-editor-list">
<details v-for="category in categories" :key="category.id" class="portal-card admin-editor">
<summary>
<span><strong>#{{ category.id }} · {{ category.name }}</strong><small>{{ category.enabled ? 'Включена' : 'Отключена' }}</small></span>
<span class="admin-pill">{{ category.products_count }} позиций</span>
</summary>
<div class="admin-editor__body">
<CategoryForm :category="category" :action="category.update_url" submit-label="Сохранить категорию" />
<button class="btn btn-ghost admin-danger-button" type="button" @click="destroy(category.destroy_url, 'Удалить категорию и её связи с товарами?')">Удалить категорию</button>
</div>
</details>
</div>
<article v-else class="portal-card admin-empty"><p>Категорий пока нет.</p></article>
</section>
<section class="admin-section">
<div class="admin-section__head">
<div><span class="eyebrow">Контент магазина</span><h2 class="h-serif">Товары и услуги</h2></div>
</div>
<div v-if="products.length" class="admin-editor-list">
<details v-for="product in products" :key="product.id" class="portal-card admin-editor">
<summary>
<span>
<strong class="admin-preline">#{{ product.id }} · {{ product.name }}</strong>
<small>{{ product.category_names.join(', ') || 'Без категории' }} · тип {{ product.type }}</small>
</span>
<span class="admin-pill">{{ product.price }} Слёз Элуны</span>
</summary>
<div class="admin-editor__body">
<ProductForm
:product="product"
:categories="categories"
:product-types="productTypes"
:action="product.update_url"
submit-label="Сохранить позицию"
/>
<button class="btn btn-ghost admin-danger-button" type="button" @click="destroy(product.destroy_url, 'Удалить позицию магазина?')">Удалить позицию</button>
</div>
</details>
</div>
<article v-else class="portal-card admin-empty"><p>Позиций пока нет.</p></article>
</section>
</div>
</AdminLayout>
</template>