Unify section search headers

This commit is contained in:
Ruslan Bakiev
2026-04-04 08:39:01 +07:00
parent 1c2070b8d8
commit 26b44f30bf
7 changed files with 126 additions and 142 deletions

View File

@@ -433,37 +433,11 @@ function decrementSelected(group: ProductGroup) {
<template> <template>
<section class="space-y-5"> <section class="space-y-5">
<h1 class="text-3xl font-extrabold text-[#0f2f20]">Каталог</h1> <UiSectionSearchHero
v-model="search"
<label class="input input-bordered flex w-full items-center gap-3 rounded-full bg-white"> title="Каталог"
<svg search-placeholder="Поиск по артикулу, типу товара или параметрам"
class="h-4 w-4 shrink-0 text-base-content/45" />
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.1667 14.1667L17.5 17.5"
stroke="currentColor"
stroke-width="1.6"
stroke-linecap="round"
stroke-linejoin="round"
/>
<circle
cx="8.75"
cy="8.75"
r="5.83333"
stroke="currentColor"
stroke-width="1.6"
/>
</svg>
<input
v-model="search"
type="text"
class="grow"
placeholder="Поиск по артикулу, типу товара или параметрам"
>
</label>
<div v-if="loading" class="alert surface-card border-0">Загрузка каталога...</div> <div v-if="loading" class="alert surface-card border-0">Загрузка каталога...</div>
<div v-else-if="error" class="alert alert-error">{{ error.message }}</div> <div v-else-if="error" class="alert alert-error">{{ error.message }}</div>

View File

@@ -0,0 +1,54 @@
<script setup lang="ts">
const props = defineProps<{
title: string;
modelValue: string;
searchPlaceholder: string;
}>();
const emit = defineEmits<{
'update:modelValue': [value: string];
}>();
function updateValue(event: Event) {
emit('update:modelValue', (event.target as HTMLInputElement).value);
}
</script>
<template>
<div class="space-y-5">
<h1 class="text-3xl font-extrabold text-[#0f2f20]">{{ props.title }}</h1>
<label class="input input-bordered flex w-full items-center gap-3 rounded-full bg-white">
<svg
class="h-4 w-4 shrink-0 text-base-content/45"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.1667 14.1667L17.5 17.5"
stroke="currentColor"
stroke-width="1.6"
stroke-linecap="round"
stroke-linejoin="round"
/>
<circle
cx="8.75"
cy="8.75"
r="5.83333"
stroke="currentColor"
stroke-width="1.6"
/>
</svg>
<input
:value="props.modelValue"
type="text"
class="grow"
:placeholder="props.searchPlaceholder"
@input="updateValue"
>
</label>
<slot />
</div>
</template>

View File

@@ -41,45 +41,31 @@ const filteredTransactions = computed(() => {
<template> <template>
<section class="space-y-6"> <section class="space-y-6">
<div class="flex flex-col gap-3 md:flex-row md:items-end md:justify-between"> <UiSectionSearchHero
<div class="manager-hero"> v-model="search"
<p class="manager-eyebrow">Бонусы</p> title="Бонусы"
<h1 class="manager-title">Отдельный раздел для бонусной системы</h1> search-placeholder="Пользователь, причина, заказ или сумма"
<p class="manager-copy">Здесь остаются история начислений и заявки на вывод, без смешивания с клиентами.</p> >
</div>
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
<NuxtLink to="/bonus-system/referrals/new" class="btn btn-secondary border-0">Создать связь</NuxtLink> <NuxtLink to="/bonus-system/referrals/new" class="btn btn-secondary border-0">Создать связь</NuxtLink>
<NuxtLink to="/bonus-system/transactions/new" class="btn btn-primary border-0">Добавить бонус</NuxtLink> <NuxtLink to="/bonus-system/transactions/new" class="btn btn-primary border-0">Добавить бонус</NuxtLink>
</div> </div>
</div>
<div class="grid gap-4 lg:grid-cols-3"> <div class="grid gap-4 lg:grid-cols-3">
<div class="manager-stat-card"> <div class="manager-stat-card">
<p class="manager-stat-label">Баланс</p> <p class="manager-stat-label">Баланс</p>
<p class="manager-stat-value">{{ bonusQuery.result.value?.referralStats.availableBalance ?? 0 }}</p> <p class="manager-stat-value">{{ bonusQuery.result.value?.referralStats.availableBalance ?? 0 }}</p>
</div>
<div class="manager-stat-card">
<p class="manager-stat-label">Рефералы</p>
<p class="manager-stat-value">{{ bonusQuery.result.value?.referralStats.referralsCount ?? 0 }}</p>
</div>
<div class="manager-stat-card">
<p class="manager-stat-label">Заявки на вывод</p>
<p class="manager-stat-value">{{ withdrawals.length }}</p>
</div>
</div> </div>
<div class="manager-stat-card"> </UiSectionSearchHero>
<p class="manager-stat-label">Рефералы</p>
<p class="manager-stat-value">{{ bonusQuery.result.value?.referralStats.referralsCount ?? 0 }}</p>
</div>
<div class="manager-stat-card">
<p class="manager-stat-label">Заявки на вывод</p>
<p class="manager-stat-value">{{ withdrawals.length }}</p>
</div>
</div>
<div class="surface-card rounded-3xl p-4 md:p-5">
<label class="form-control">
<span class="label-text">Search</span>
<input
v-model="search"
type="text"
class="input manager-field w-full"
placeholder="Пользователь, причина, заказ или сумма"
>
</label>
</div>
<div class="grid gap-4 xl:grid-cols-[1.1fr_0.9fr]"> <div class="grid gap-4 xl:grid-cols-[1.1fr_0.9fr]">
<div class="surface-card rounded-3xl p-5"> <div class="surface-card rounded-3xl p-5">

View File

@@ -146,7 +146,7 @@ async function submitCart() {
<div class="surface-card rounded-3xl p-4 md:p-5"> <div class="surface-card rounded-3xl p-4 md:p-5">
<h2 class="text-lg font-bold text-[#123824]">Адрес доставки</h2> <h2 class="text-lg font-bold text-[#123824]">Адрес доставки</h2>
<div v-if="deliveryAddressesQuery.loading" class="alert mt-3 surface-card"> <div v-if="deliveryAddressesQuery.loading.value" class="alert mt-3 surface-card">
Загружаем адреса... Загружаем адреса...
</div> </div>
<div v-else-if="!hasDeliveryAddresses" class="alert alert-warning mt-3"> <div v-else-if="!hasDeliveryAddresses" class="alert alert-warning mt-3">

View File

@@ -54,35 +54,25 @@ const filteredOrders = computed(() => {
<template> <template>
<section class="space-y-6"> <section class="space-y-6">
<div class="manager-hero"> <UiSectionSearchHero
<p class="manager-eyebrow">Заказы клиентов</p> v-model="search"
<h1 class="manager-title">Только список заказов и быстрый поиск</h1> title="Заказы клиентов"
<p class="manager-copy">Карточки заказов без форм на странице списка. Детали и действия открываются внутри заказа.</p> search-placeholder="Номер заказа, клиент, адрес или товар"
</div> >
<div class="surface-card rounded-3xl p-4 md:p-5">
<div class="surface-card rounded-3xl p-4 md:p-5"> <div class="grid gap-3 md:grid-cols-[1fr_auto]">
<div class="grid gap-3 md:grid-cols-[1fr_auto]"> <label class="form-control md:min-w-60">
<label class="form-control"> <span class="label-text">Фильтр</span>
<span class="label-text">Search</span> <select v-model="statusFilter" class="select manager-field w-full">
<input <option value="ALL">Все заказы</option>
v-model="search" <option value="WAITING">Ожидают подтверждения</option>
type="text" <option value="ACTIVE">Активные</option>
class="input manager-field w-full" <option value="CLOSED">Закрытые</option>
placeholder="Номер заказа, клиент, адрес или товар" </select>
> </label>
</label> </div>
<label class="form-control md:min-w-60">
<span class="label-text">Фильтр</span>
<select v-model="statusFilter" class="select manager-field w-full">
<option value="ALL">Все заказы</option>
<option value="WAITING">Ожидают подтверждения</option>
<option value="ACTIVE">Активные</option>
<option value="CLOSED">Закрытые</option>
</select>
</label>
</div> </div>
</div> </UiSectionSearchHero>
<div v-if="ordersQuery.loading.value" class="manager-empty-state"> <div v-if="ordersQuery.loading.value" class="manager-empty-state">
Загружаем заказы... Загружаем заказы...

View File

@@ -60,35 +60,21 @@ const filteredClients = computed(() => {
<template> <template>
<section class="space-y-6"> <section class="space-y-6">
<div class="flex flex-col gap-3 md:flex-row md:items-end md:justify-between"> <UiSectionSearchHero
<div class="manager-hero"> v-model="search"
<p class="manager-eyebrow">Клиенты</p> title="Клиенты"
<h1 class="manager-title">Карточки клиентов без лишней нагрузки</h1> search-placeholder="Компания, контакт, email или ИНН"
<p class="manager-copy">Список заявок и клиентов, с которыми менеджер уже работает.</p> >
</div> <div class="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
<NuxtLink to="/clients/invite" class="btn btn-primary border-0">
Пригласить клиента
</NuxtLink>
</div>
<div class="surface-card rounded-3xl p-4 md:p-5">
<div class="grid gap-3 md:grid-cols-[1fr_auto] md:items-end">
<label class="form-control">
<span class="label-text">Search</span>
<input
v-model="search"
type="text"
class="input manager-field w-full"
placeholder="Компания, контакт, email или ИНН"
>
</label>
<div class="manager-mini-card text-sm text-[#123824] md:w-56"> <div class="manager-mini-card text-sm text-[#123824] md:w-56">
Всего карточек: <span class="font-semibold">{{ filteredClients.length }}</span> Всего карточек: <span class="font-semibold">{{ filteredClients.length }}</span>
</div> </div>
<NuxtLink to="/clients/invite" class="btn btn-primary border-0">
Пригласить клиента
</NuxtLink>
</div> </div>
</div> </UiSectionSearchHero>
<div v-if="clientsQuery.loading.value" class="manager-empty-state"> <div v-if="clientsQuery.loading.value" class="manager-empty-state">
Загружаем клиентов... Загружаем клиентов...

View File

@@ -48,31 +48,25 @@ const filteredOrders = computed(() => {
<template> <template>
<section class="space-y-6"> <section class="space-y-6">
<h1 class="text-3xl font-extrabold text-[#0f2f20]">Заказы</h1> <UiSectionSearchHero
v-model="search"
<div class="surface-card rounded-3xl p-4 md:p-5"> title="Мои заказы"
<div class="grid gap-3 md:grid-cols-[1fr_auto]"> search-placeholder="Номер заказа или товар"
<label class="form-control"> >
<span class="label-text">Поиск</span> <div class="surface-card rounded-3xl p-4 md:p-5">
<input <div class="grid gap-3 md:grid-cols-[1fr_auto]">
v-model="search" <label class="form-control md:min-w-60">
type="text" <span class="label-text">Фильтр</span>
class="input input-bordered w-full" <select v-model="statusFilter" class="select select-bordered w-full">
placeholder="Номер заказа или товар" <option value="ALL">Все заказы</option>
> <option value="WAITING">Ожидают подтверждения</option>
</label> <option value="ACTIVE">Активные</option>
<option value="CLOSED">Закрытые</option>
<label class="form-control md:min-w-60"> </select>
<span class="label-text">Фильтр</span> </label>
<select v-model="statusFilter" class="select select-bordered w-full"> </div>
<option value="ALL">Все заказы</option>
<option value="WAITING">Ожидают подтверждения</option>
<option value="ACTIVE">Активные</option>
<option value="CLOSED">Закрытые</option>
</select>
</label>
</div> </div>
</div> </UiSectionSearchHero>
<div v-if="allOrders.loading.value" class="alert surface-card border-0">Загрузка заказов...</div> <div v-if="allOrders.loading.value" class="alert surface-card border-0">Загрузка заказов...</div>
<div v-else-if="filteredOrders.length === 0" class="alert surface-card border-0"> <div v-else-if="filteredOrders.length === 0" class="alert surface-card border-0">