Add unified order summary card
This commit is contained in:
@@ -3,23 +3,27 @@ import { useQuery } from '@vue/apollo-composable';
|
||||
import FullCalendar from '@fullcalendar/vue3';
|
||||
import dayGridPlugin from '@fullcalendar/daygrid';
|
||||
import ruLocale from '@fullcalendar/core/locales/ru';
|
||||
import OrderStatusBadge from '~/components/orders/OrderStatusBadge.vue';
|
||||
import {
|
||||
ManagerOrdersDocument,
|
||||
ManagerUsersDocument,
|
||||
type ManagerOrdersQuery,
|
||||
type ManagerUsersQuery,
|
||||
} from '~/composables/graphql/generated';
|
||||
import { messengerConnectionAvatarSrc } from '~/composables/useMessengerConnectionPresentation';
|
||||
|
||||
definePageMeta({
|
||||
middleware: ['manager-only'],
|
||||
});
|
||||
|
||||
type ManagerOrderItem = ManagerOrdersQuery['managerOrders'][number];
|
||||
type ManagerUserItem = ManagerUsersQuery['managerUsers'][number];
|
||||
type StatusFilter = 'ALL' | 'NEW' | 'PRICED' | 'IN_PROGRESS' | 'CLOSED';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const ordersQuery = useQuery(ManagerOrdersDocument, { status: null });
|
||||
const usersQuery = useQuery(ManagerUsersDocument);
|
||||
const search = ref('');
|
||||
const statusFilter = ref<StatusFilter>('ALL');
|
||||
|
||||
@@ -40,6 +44,32 @@ function setViewMode(view: 'cards' | 'calendar' | 'kanban') {
|
||||
});
|
||||
}
|
||||
|
||||
const usersById = computed<Record<string, ManagerUserItem>>(() => Object.fromEntries(
|
||||
(usersQuery.result.value?.managerUsers ?? []).map((user) => [user.id, user]),
|
||||
));
|
||||
|
||||
function customerCardMeta(customerId: string) {
|
||||
const customer = usersById.value[customerId];
|
||||
if (!customer) {
|
||||
return {
|
||||
name: customerId,
|
||||
avatarSrc: '',
|
||||
initials: customerId.slice(0, 2).toUpperCase(),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
name: customer.fullName,
|
||||
avatarSrc: messengerConnectionAvatarSrc(customer.telegramConnection),
|
||||
initials: customer.fullName
|
||||
.split(/\s+/)
|
||||
.filter(Boolean)
|
||||
.slice(0, 2)
|
||||
.map((part) => part.charAt(0).toUpperCase())
|
||||
.join(''),
|
||||
};
|
||||
}
|
||||
|
||||
function getOrderGroup(order: ManagerOrderItem) {
|
||||
if (order.status === 'NEW' || order.status === 'MANAGER_PROCESSING') {
|
||||
return 'NEW';
|
||||
@@ -68,6 +98,7 @@ const searchedOrders = computed<ManagerOrderItem[]>(() => {
|
||||
const text = [
|
||||
order.code,
|
||||
order.customerId,
|
||||
customerCardMeta(order.customerId).name,
|
||||
order.deliveryAddress || '',
|
||||
...order.items.map((item) => item.productName),
|
||||
]
|
||||
@@ -248,68 +279,33 @@ const calendarOptions = computed(() => ({
|
||||
</div>
|
||||
|
||||
<div v-else class="mt-4 space-y-3">
|
||||
<NuxtLink
|
||||
<OrdersOrderSummaryCard
|
||||
v-for="order in column.orders"
|
||||
:key="order.id"
|
||||
:to="`/client-orders/${order.id}`"
|
||||
class="block rounded-[28px] border border-[#dbece2] bg-white p-4 shadow-[0_18px_40px_rgba(18,56,36,0.08)] transition hover:-translate-y-0.5"
|
||||
>
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="space-y-1">
|
||||
<h3 class="text-base font-bold text-[#123824]">{{ order.code }}</h3>
|
||||
<p class="text-sm text-[#5c7b69]">Клиент: {{ order.customerId }}</p>
|
||||
</div>
|
||||
<OrderStatusBadge :status="order.status" />
|
||||
</div>
|
||||
|
||||
<p class="mt-3 text-sm text-[#5c7b69]">Создан: {{ new Date(order.createdAt).toLocaleString() }}</p>
|
||||
<p v-if="order.deliveryAddress" class="mt-1 text-sm text-[#5c7b69]">{{ order.deliveryAddress }}</p>
|
||||
|
||||
<ul class="mt-3 space-y-2 text-sm text-[#214735]">
|
||||
<li
|
||||
v-for="item in order.items.slice(0, 3)"
|
||||
:key="item.id"
|
||||
class="rounded-2xl border border-[#e2efe7] bg-[#f8fcf9] px-3 py-2"
|
||||
>
|
||||
{{ item.productName }} × {{ item.quantity }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p v-if="order.items.length > 3" class="mt-2 text-xs font-semibold uppercase tracking-[0.18em] text-[#6a8a76]">
|
||||
ещё {{ order.items.length - 3 }} поз.
|
||||
</p>
|
||||
</NuxtLink>
|
||||
:code="order.code"
|
||||
:status="order.status"
|
||||
:created-at="order.createdAt"
|
||||
:total-price="order.totalPrice"
|
||||
:items="order.items"
|
||||
:customer="customerCardMeta(order.customerId)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="space-y-4">
|
||||
<NuxtLink
|
||||
<OrdersOrderSummaryCard
|
||||
v-for="order in filteredOrders"
|
||||
:key="order.id"
|
||||
:to="`/client-orders/${order.id}`"
|
||||
class="surface-card block rounded-3xl p-5"
|
||||
>
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div class="space-y-1">
|
||||
<h2 class="text-lg font-bold text-[#123824]">{{ order.code }}</h2>
|
||||
<p class="text-sm text-[#5c7b69]">Клиент: {{ order.customerId }}</p>
|
||||
<p class="text-sm text-[#5c7b69]">Создан: {{ new Date(order.createdAt).toLocaleString() }}</p>
|
||||
<p v-if="order.deliveryAddress" class="text-sm text-[#5c7b69]">Адрес: {{ order.deliveryAddress }}</p>
|
||||
</div>
|
||||
<OrderStatusBadge :status="order.status" />
|
||||
</div>
|
||||
|
||||
<ul class="mt-4 grid gap-2 text-sm text-[#214735]">
|
||||
<li
|
||||
v-for="item in order.items"
|
||||
:key="item.id"
|
||||
class="rounded-2xl border border-[#d6ebde] bg-white px-4 py-3"
|
||||
>
|
||||
{{ item.productName }} × {{ item.quantity }}
|
||||
</li>
|
||||
</ul>
|
||||
</NuxtLink>
|
||||
:code="order.code"
|
||||
:status="order.status"
|
||||
:created-at="order.createdAt"
|
||||
:total-price="order.totalPrice"
|
||||
:items="order.items"
|
||||
:customer="customerCardMeta(order.customerId)"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -178,21 +178,21 @@ async function rejectRequest() {
|
||||
У пользователя пока нет заказов.
|
||||
</div>
|
||||
<div v-else class="mt-4 space-y-3">
|
||||
<NuxtLink
|
||||
<OrdersOrderSummaryCard
|
||||
v-for="order in currentUserOrders"
|
||||
:key="order.id"
|
||||
:to="`/client-orders/${order.id}`"
|
||||
class="manager-mini-card block"
|
||||
>
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div class="space-y-1">
|
||||
<p class="text-sm font-semibold text-[#123824]">{{ order.code }}</p>
|
||||
<p class="text-sm text-[#355947]">{{ new Date(order.createdAt).toLocaleString() }}</p>
|
||||
<p v-if="order.deliveryAddress" class="text-sm text-[#355947]">{{ order.deliveryAddress }}</p>
|
||||
</div>
|
||||
<p class="text-sm font-semibold text-[#466653]">{{ order.status }}</p>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
:code="order.code"
|
||||
:status="order.status"
|
||||
:created-at="order.createdAt"
|
||||
:total-price="order.totalPrice"
|
||||
:items="order.items"
|
||||
:customer="{
|
||||
name: currentUser.fullName,
|
||||
avatarSrc: messengerConnectionAvatarSrc(currentUser.telegramConnection),
|
||||
initials: userInitials(currentUser.fullName),
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { useQuery } from '@vue/apollo-composable';
|
||||
import OrderStatusBadge from '~/components/orders/OrderStatusBadge.vue';
|
||||
import {
|
||||
MyOrdersDocument,
|
||||
type MyOrdersQuery,
|
||||
@@ -69,34 +68,16 @@ const filteredOrders = computed(() => {
|
||||
</div>
|
||||
|
||||
<div v-else class="space-y-3">
|
||||
<NuxtLink
|
||||
<OrdersOrderSummaryCard
|
||||
v-for="order in filteredOrders"
|
||||
:key="order.id"
|
||||
:to="`/orders/${order.id}`"
|
||||
class="surface-card block rounded-3xl p-4 md:p-5"
|
||||
>
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div class="space-y-1">
|
||||
<h2 class="text-lg font-bold text-[#123824]">{{ order.code }}</h2>
|
||||
<p class="text-sm text-[#355947]">Создан: {{ new Date(order.createdAt).toLocaleString() }}</p>
|
||||
<p v-if="order.deliveryAddress" class="text-sm text-[#355947]">Адрес: {{ order.deliveryAddress }}</p>
|
||||
<p v-if="order.deliveryTerms" class="text-sm text-[#355947]">Доставка: {{ order.deliveryTerms }}</p>
|
||||
</div>
|
||||
<div class="flex flex-col items-end gap-2">
|
||||
<OrderStatusBadge :status="order.status" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="mt-4 grid gap-2 text-sm text-[#214735]">
|
||||
<li
|
||||
v-for="item in order.items"
|
||||
:key="item.id"
|
||||
class="rounded-2xl border border-[#d6ebde] bg-white px-4 py-3"
|
||||
>
|
||||
{{ item.productName }} × {{ item.quantity }}
|
||||
</li>
|
||||
</ul>
|
||||
</NuxtLink>
|
||||
:code="order.code"
|
||||
:status="order.status"
|
||||
:created-at="order.createdAt"
|
||||
:total-price="order.totalPrice"
|
||||
:items="order.items"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user