schon/storefront/components/cards/order.vue
Alexandr SaVBaD Waltz 40ae24a04c Features: 1) Add SEO-related fragments to GraphQL queries including SEOMETA_FRAGMENT usage in brands, categories, and products queries; 2) Enable localized and dynamic SEO metadata handling in category pages with Vue composables and useSeoMeta; 3) Replace obsolete client-only wrapper with native Nuxt components like nuxt-marquee for enhanced rendering;
Fixes: 1) Correct file path imports by removing `.js` extensions in GraphQL fragments; 2) Resolve typo in `usePromocodeStore` composables to ensure consistent store usage; 3) Add missing `:type="submit"` to login form button for proper form submission handling;

Extra: 1) Remove unused `.idea` and `README.md` files for repository cleanup; 2) Delete extraneous dependencies from `package-lock.json` for streamlined package management; 3) Refactor category slug handling with improved composable logic for cleaner route parameters and SEO alignment.
2025-09-13 12:53:06 +03:00

248 lines
No EOL
5.4 KiB
Vue

<template>
<el-collapse-item
class="order"
>
<template #title="{ isActive }">
<div :class="['order__top', { 'is-active': isActive }]">
<div>
<p>{{ t('profile.orders.id') }}: {{ order.humanReadableId }}</p>
<p v-if="order.buyTime">{{ useDate(order.buyTime, locale) }}</p>
<el-tooltip
:content="order.status"
placement="top"
>
<p class="status" :style="[{ backgroundColor: statusColor(order.status) }]">
{{ order.status }}
<icon name="material-symbols:info-outline-rounded" size="14" />
</p>
</el-tooltip>
</div>
<icon
name="material-symbols:keyboard-arrow-down"
size="22"
class="order__top-icon"
:class="[{ active: isActive }]"
/>
</div>
<div class="order__top-bottom" :class="{ active: !isActive }">
<div>
<div>
<div>
<nuxt-img
v-for="product in order.orderProducts.edges"
:key="product.node.uuid"
:src="product.node.product.images.edges[0].node.image"
:alt="product.node.product.name"
format="webp"
densities="x1"
/>
</div>
<p>{{ order.totalPrice }}{{ CURRENCY }}</p>
</div>
</div>
</div>
</template>
<div class="order__main">
<div
class="order__product"
v-for="product in order.orderProducts.edges"
:key="product.node.uuid"
>
<div class="order__product-left">
<nuxt-img
:src="product.node.product.images.edges[0].node.image"
:alt="product.node.product.name"
format="webp"
densities="x1"
/>
<p>{{ product.node.product.name }}</p>
</div>
<div class="order__product-right">
<h6>{{ t('profile.orders.price') }}: {{ product.node.product.price * product.node.quantity }}{{ CURRENCY }}</h6>
<p>{{ product.node.quantity }} X {{ product.node.product.price }}{{ CURRENCY }}</p>
</div>
</div>
<div class="order__total">
<p>{{ t('profile.orders.total') }}: {{ order.totalPrice }}{{ CURRENCY }}</p>
</div>
</div>
</el-collapse-item>
</template>
<script setup lang="ts">
import {useDate} from "~/composables/date";
import {CURRENCY, orderStatuses} from "~/config/constants";
import type {IOrder} from "~/types";
const props = defineProps<{
order: IOrder;
}>();
const {t, locale} = useI18n();
const statusColor = (status: string) => {
switch (status) {
case orderStatuses.FAILED:
return '#FF0000';
case orderStatuses.PAYMENT:
return '#FFC107';
case orderStatuses.CREATED:
return '#007BFF';
case orderStatuses.DELIVERING:
return '#00C853';
case orderStatuses.FINISHED:
return '#00C853';
case orderStatuses.MOMENTAL:
return '#00C853';
default:
return '#000';
}
};
</script>
<style scoped lang="scss">
.order {
&__top {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
padding: 0 10px 5px 10px;
& div {
display: flex;
align-items: center;
gap: 25px;
& p {
display: flex;
align-items: center;
gap: 5px;
&.status {
border-radius: $default_border_radius;
padding: 3px 7px;
}
}
}
&-icon {
transition: 0.2s;
&.active {
transform: rotate(-180deg);
}
}
&-bottom {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.2s ease;
overflow: hidden;
&.active {
grid-template-rows: 1fr;
}
& > * {
min-height: 0;
}
& div {
& div {
padding-top: 10px;
border-top: 1px solid $accent;
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 25px;
& div {
padding-top: 0;
border: none;
display: flex;
flex-wrap: wrap;
gap: 10px;
& img {
height: 65px;
border-radius: $default_border_radius;
}
}
& p {
font-weight: 600;
font-size: 20px;
}
}
}
}
}
&__main {
display: flex;
flex-direction: column;
gap: 20px;
}
&__product {
display: flex;
align-items: flex-start;
justify-content: space-between;
padding-bottom: 15px;
border-bottom: 2px solid $accent;
&-left {
display: flex;
align-items: flex-start;
gap: 10px;
& img {
height: 150px;
border-radius: $default_border_radius;
}
& p {
font-size: 18px;
font-weight: 500;
}
}
&-right {
display: flex;
flex-direction: column;
align-items: flex-end;
& h6 {
font-size: 20px;
}
& p {
font-size: 14px;
}
}
}
&__total {
display: flex;
align-items: flex-end;
justify-content: flex-end;
& p {
font-size: 20px;
font-weight: 600;
}
}
}
:deep(.el-collapse-item__header) {
height: fit-content;
padding-block: 10px;
}
</style>