Add unified order summary card

This commit is contained in:
Ruslan Bakiev
2026-04-04 13:52:01 +07:00
parent 2a5e38f488
commit 2f828cd164
4 changed files with 244 additions and 90 deletions

View File

@@ -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>