user-app: extend the redesign across the account screens
Phase 2, completing the app-wide rollout of the home/product-detail design language (LESS colour tokens, BEM class names, hairline-bordered cards, SectionHead, dashed empty states) onto the account side: dashboard, orders, transactions, profile, login and register. Reuse over re-implementation: the dashboard's live rate block is now components/home/RateStrip.vue rather than a second hand-built strip, and every screen uses components/ui/PageBar.vue for its chrome. OrderCard and OrderItemCard are restyled to match. OrderItemCard drops its always-empty note row: our OrderItem schema has no note field (legacy's per-item note was a real column there; ours is not), so it could only ever print a dash. OrdersView swaps ant Tabs for the same scrolling chip rail the rest of the redesign uses, and its full-screen Modal with hand-written CSS overrides for a bottom Drawer, matching how every other detail surface opens. Deleted AppHeader.vue, orphaned once every screen moved to PageBar. Its shutter animation was driven by transitionFlag, which nothing reads any more — the router afterEach comment claiming otherwise is corrected rather than left asserting something untrue, and the now-inert half is flagged in place. ScrollFetchSearch loses its isFav prop: it gated two empty-state branches that rendered the identical image, and its only caller (FavoritesView) now renders its own. Behaviour is preserved throughout. Orders still reads GET /customers/:id/orders and filters client-side; transactions still reads GET /customers/:id/ledger-entries with legacy's CREDIT/DEBIT colour meaning intact; profile stays read-only over real fields; login keeps the real customer lookup plus the documented OTP stub; register stays stubbed with copy that does not claim an account was created. Verified in a real browser against the running API and native database: dashboard 4 tiles with a real balance, orders 1 real order card, profile 4 real spec rows, transactions the correct empty state for a customer with no ledger rows, and login/register confirmed with the session cleared (the guard bounces a signed-in user away from /login, which is why they needed a separate pass). No console errors, no horizontal overflow. vue-tsc and vite build both clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
cc62975dd0
commit
0e5f9ccd7b
@@ -1,126 +1,166 @@
|
||||
<!--
|
||||
Ported from legacy henza_pwa/src/views/user/src/components/Orders.vue.
|
||||
Ported from legacy henza_pwa/src/views/user/src/components/Orders.vue,
|
||||
restyled into the redesigned design language (see components/home/* and
|
||||
HomeView).
|
||||
|
||||
Adapted:
|
||||
- Status filter tabs use our real OrderStatus enum values directly (see
|
||||
Adapted (unchanged from the previous version):
|
||||
- Status filter chips use our real OrderStatus enum values directly (see
|
||||
utils/orderStatus.ts) rather than legacy's own inconsistent tab labels.
|
||||
They are chips now instead of ant Tabs, which lets them scroll
|
||||
horizontally like every other filter rail in the redesign.
|
||||
- Fetches real order history via the already-existing GET
|
||||
/customers/:id/orders endpoint, filtered client-side by tab (that
|
||||
/customers/:id/orders endpoint, filtered client-side by chip (that
|
||||
endpoint has no server-side status filter — small enough dataset that
|
||||
client-side filtering is reasonable, and apps/api stays untouched).
|
||||
- Search matches against `orderNumber` (our real user-facing identifier)
|
||||
instead of legacy's numeric `id`.
|
||||
|
||||
DROPPED: "دریافت فاکتور" (get invoice) button — legacy's handler opened a
|
||||
hardcoded link to the real legacy production domain
|
||||
(https://henzagold.com/oapi/...). Not just unimplemented — an actual
|
||||
DROPPED, unchanged: "دریافت فاکتور" — legacy's handler opened a hardcoded
|
||||
link to the real legacy production domain. Not merely unimplemented; an
|
||||
external production URL that must never be carried into this port. No
|
||||
invoice-generation endpoint exists in the new API either.
|
||||
|
||||
The detail view is a bottom Drawer rather than a full-screen ant Modal with
|
||||
hand-written overrides, matching how every other detail surface in the
|
||||
redesign opens (product detail, cart note, ledger entry).
|
||||
-->
|
||||
<template>
|
||||
<div class="fixed top-0 z-10 w-full bg-white">
|
||||
<AppHeader>
|
||||
<div class="flex justify-between items-center">
|
||||
<Icon icon="vuesax-linear:arrow-right-1" class="cursor-pointer text-white" size="25" @click="router.push({ name: 'home' })" />
|
||||
<h3 class="text-white bold mb-0">سفارشات</h3>
|
||||
<div></div>
|
||||
</div>
|
||||
</AppHeader>
|
||||
<div class="pt-3 px-2 pb-3 mb-3 border-b-solid border-[#E5E7EB] w-full">
|
||||
<SearchInput @search="readSearch" place-holder="جستجو" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-36"></div>
|
||||
<div class="ord">
|
||||
<PageBar title="سفارشات" back-to="home">
|
||||
<template #below>
|
||||
<div class="ord__barExtra">
|
||||
<SearchInput @search="readSearch" place-holder="جستجوی شماره سفارش" />
|
||||
|
||||
<container class="bg-[#F9FAFB] px-2 orders">
|
||||
<Tabs v-model:activeKey="activeTab" style="padding-right: 0.2rem !important; font-weight: bold">
|
||||
<TabPane v-for="item in tabs" :key="item.key" :tab="item.title" force-render>
|
||||
<Spinner :spinning="loading" tip="درحال دریافت اطلاعات" cls="overflow-y-scroll no-scrollbar">
|
||||
<OrderCard @view="openModal" v-for="item in filteredOrders" :key="item.id" :value="item" />
|
||||
<EmptyState v-if="filteredOrders.length === 0" :image="ordersEmpty" />
|
||||
</Spinner>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
|
||||
<Modal v-model:visible="viewData.model" :mask="false" :footer="false" :closable="false" class="henza-custom-modal" v-if="viewData.item">
|
||||
<div class="flex justify-between items-center p-4">
|
||||
<Icon icon="vuesax-linear:arrow-right-1" class="cursor-pointer" size="25" @click="viewData.model = false" />
|
||||
<h3 class="font-semibold mb-0">جزئیات سفارش</h3>
|
||||
<div></div>
|
||||
</div>
|
||||
<container>
|
||||
<div class="overflow-y-auto h-screen">
|
||||
<div class="flex justify-between items-center pt-4">
|
||||
<span class="font-semibold text-[14px]">اطلاعات سفارش</span>
|
||||
</div>
|
||||
<div class="responsive-steps">
|
||||
<div class="steps-container">
|
||||
<div v-for="(step, index) in ORDER_STEPS" :key="index" class="step" :class="{ active: currentStep === index }">
|
||||
<div class="step-circle">{{ index + 1 }}</div>
|
||||
<div class="step-title">{{ step }}</div>
|
||||
<div v-if="index !== 0" class="step-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 border-[1.5px] border-[#E5E7EB] border-solid mt-4 rounded-[6px]">
|
||||
<div class="grid col-span-1 p-4 text-[#9CA3AF] text-[12px] font-medium">
|
||||
<div>شماره سفارش :</div>
|
||||
<div class="pt-4">وضعیت سفارش :</div>
|
||||
<div class="pt-4">تعداد کالاها :</div>
|
||||
<div class="pt-4">وزن سفارش :</div>
|
||||
<div class="pt-4">تاریخ سفارش :</div>
|
||||
</div>
|
||||
<div class="grid col-span-1 text-left p-4">
|
||||
<div class="font-bold text-[18px] text-[#23335B]">{{ viewData.item.orderNumber }}</div>
|
||||
<div class="pt-4 text-center flex justify-end">
|
||||
<div class="rounded-[5px] py-[4px] px-[12px] text-[14px] font-semibold w-fit" :style="{ background: modalColor?.background, color: modalColor?.font }">
|
||||
{{ modalStatusLabel }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pt-4">
|
||||
<span class="text-[16px] font-bold pl-2 text-black">{{ viewData.item.items.length }}</span>
|
||||
<span class="text-[14px] font-medium">آیتم</span>
|
||||
</div>
|
||||
<div class="pt-4 font-medium">
|
||||
<span class="text-[16px] font-bold pl-1 text-black">{{ parseFloat(viewData.item.totalGoldWeightGrams).toFixed(2) }}</span>
|
||||
<span class="pr-2">گرم</span>
|
||||
</div>
|
||||
<div class="pt-4 font-semibold text-[#1E2229]">{{ jdate(viewData.item.createdAt) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-semibold mt-4">آیتم های سفارش</div>
|
||||
<div class="w-full max-w-screen-md m-auto h-full flex flex-col">
|
||||
<div class="flex-1 py-[10px] pb-[70px]">
|
||||
<OrderItemCard v-for="item in viewData.item.items" :key="item.id" :value="item" class="border-[#E5E7EB] border-[1.5px] border-solid text-primary bg-[#F9F9F9] rounded-[6px] mb-4" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="ord__chips no-scrollbar">
|
||||
<button
|
||||
v-for="item in tabs"
|
||||
:key="item.key"
|
||||
type="button"
|
||||
class="ord__chip"
|
||||
:class="{ 'is-on': activeTab === item.key }"
|
||||
@click="activeTab = item.key"
|
||||
>
|
||||
{{ item.title }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</PageBar>
|
||||
|
||||
<Spinner :spinning="loading" tip="درحال دریافت اطلاعات">
|
||||
<container class="ord__body">
|
||||
<template v-if="filteredOrders.length">
|
||||
<SectionHead title="سفارشهای شما" :subtitle="`${filteredOrders.length} سفارش`" />
|
||||
<div class="ord__list">
|
||||
<OrderCard v-for="item in filteredOrders" :key="item.id" :value="item" @view="openDetail" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<section v-else-if="!loading" class="ord__empty">
|
||||
<Icon icon="vuesax-linear:box" size="26" />
|
||||
<p>سفارشی با این مشخصات پیدا نشد.</p>
|
||||
</section>
|
||||
</container>
|
||||
</Modal>
|
||||
</container>
|
||||
</Spinner>
|
||||
|
||||
<Drawer
|
||||
v-model:visible="viewData.model"
|
||||
placement="bottom"
|
||||
v-if="viewData.item"
|
||||
:closable="false"
|
||||
height="100vh"
|
||||
class="rtl"
|
||||
:bodyStyle="{ padding: 0, position: 'relative' }"
|
||||
>
|
||||
<div class="detail">
|
||||
<header class="detail__bar">
|
||||
<button type="button" class="detail__close" aria-label="بستن" @click="viewData.model = false">
|
||||
<Icon icon="vuesax-linear:arrow-right-1" size="20" />
|
||||
</button>
|
||||
<h2 class="detail__title">جزئیات سفارش</h2>
|
||||
<span class="detail__spacer" aria-hidden="true" />
|
||||
</header>
|
||||
|
||||
<div class="detail__scroll">
|
||||
<!-- Progress -->
|
||||
<section class="detail__block">
|
||||
<div class="detail__steps">
|
||||
<div
|
||||
v-for="(step, index) in ORDER_STEPS"
|
||||
:key="step"
|
||||
class="detail__step"
|
||||
:class="{ 'is-done': index <= currentStep, 'is-active': index === currentStep }"
|
||||
>
|
||||
<span class="detail__stepDot">{{ index + 1 }}</span>
|
||||
<span class="detail__stepTitle">{{ step }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Headline facts -->
|
||||
<section class="detail__block">
|
||||
<div class="detail__headline">
|
||||
<div>
|
||||
<div class="detail__eyebrow">شماره سفارش</div>
|
||||
<div class="detail__number">{{ viewData.item.orderNumber }}</div>
|
||||
</div>
|
||||
<span
|
||||
class="detail__status"
|
||||
:style="{ background: modalColor?.background, color: modalColor?.font }"
|
||||
>
|
||||
{{ modalStatusLabel }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<dl class="detail__specs">
|
||||
<div class="detail__spec">
|
||||
<dt>تعداد کالاها</dt>
|
||||
<dd>{{ viewData.item.items.length }} آیتم</dd>
|
||||
</div>
|
||||
<div class="detail__spec">
|
||||
<dt>وزن سفارش</dt>
|
||||
<dd>{{ parseFloat(viewData.item.totalGoldWeightGrams).toFixed(2) }} گرم</dd>
|
||||
</div>
|
||||
<div class="detail__spec">
|
||||
<dt>تاریخ سفارش</dt>
|
||||
<dd>{{ jdate(viewData.item.createdAt) }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<!-- Items -->
|
||||
<section class="detail__block">
|
||||
<h3 class="detail__blockTitle">آیتمهای سفارش</h3>
|
||||
<div class="detail__items">
|
||||
<OrderItemCard v-for="item in viewData.item.items" :key="item.id" :value="item" />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</Drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Modal, TabPane, Tabs } from 'ant-design-vue'
|
||||
import ordersEmpty from '../../assets/img/empty/ordersEmpty.svg'
|
||||
import { Drawer } from 'ant-design-vue'
|
||||
import Icon from '../../components/icon/Icon.vue'
|
||||
import AppHeader from '../../components/AppHeader.vue'
|
||||
import PageBar from '../../components/ui/PageBar.vue'
|
||||
import Spinner from '../../components/Spinner.vue'
|
||||
import EmptyState from '../../components/EmptyState.vue'
|
||||
import SectionHead from '../../components/home/SectionHead.vue'
|
||||
import SearchInput from '../../components/search/SearchInput.vue'
|
||||
import OrderCard from '../../components/product/OrderCard.vue'
|
||||
import OrderItemCard from '../../components/product/OrderItemCard.vue'
|
||||
import { ORDER_STATUS_COLORS, ORDER_STATUS_LABELS, ORDER_STEPS, orderStepIndex } from '../../utils/orderStatus'
|
||||
import {
|
||||
ORDER_STATUS_COLORS,
|
||||
ORDER_STATUS_LABELS,
|
||||
ORDER_STEPS,
|
||||
orderStepIndex,
|
||||
} from '../../utils/orderStatus'
|
||||
import { jdate } from '../../utils/filters'
|
||||
import { useSessionStore } from '../../stores/session'
|
||||
import { api, type OrderDetail } from '../../api/client'
|
||||
|
||||
const router = useRouter()
|
||||
const session = useSessionStore()
|
||||
|
||||
const tabs = [
|
||||
@@ -162,11 +202,13 @@ async function loadOrders() {
|
||||
}
|
||||
|
||||
const viewData = reactive<{ model: boolean; item: OrderDetail | null }>({ model: false, item: null })
|
||||
const modalStatusLabel = computed(() => (viewData.item ? ORDER_STATUS_LABELS[viewData.item.status] ?? viewData.item.status : ''))
|
||||
const modalStatusLabel = computed(() =>
|
||||
viewData.item ? ORDER_STATUS_LABELS[viewData.item.status] ?? viewData.item.status : '',
|
||||
)
|
||||
const modalColor = computed(() => (viewData.item ? ORDER_STATUS_COLORS[viewData.item.status] : undefined))
|
||||
const currentStep = computed(() => (viewData.item ? orderStepIndex(viewData.item.status) : 0))
|
||||
|
||||
function openModal(item: OrderDetail) {
|
||||
function openDetail(item: OrderDetail) {
|
||||
viewData.item = item
|
||||
viewData.model = true
|
||||
}
|
||||
@@ -174,109 +216,262 @@ function openModal(item: OrderDetail) {
|
||||
onMounted(loadOrders)
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.henza-custom-modal,
|
||||
.henza-custom-modal .ant-modal-body,
|
||||
.henza-custom-modal .ant-modal-content {
|
||||
border-radius: 0;
|
||||
width: 100vw !important;
|
||||
max-width: 100vw;
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
top: 0;
|
||||
padding: 0;
|
||||
direction: rtl;
|
||||
<style scoped lang="less">
|
||||
.ord {
|
||||
background: @canvas;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.orders {
|
||||
min-height: calc(100vh - 150px);
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.orders .ant-tabs > .ant-tabs-nav,
|
||||
.ant-tabs > div > .ant-tabs-nav {
|
||||
background-color: white;
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
overflow-x: auto;
|
||||
width: 100%;
|
||||
.ord__barExtra {
|
||||
max-width: 768px;
|
||||
margin: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #e5e7eb;
|
||||
border-bottom-width: 1px;
|
||||
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
margin: 0 auto;
|
||||
padding: 0 16px 11px;
|
||||
}
|
||||
|
||||
.responsive-steps {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
|
||||
.steps-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
|
||||
.step-circle {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
background-color: #e5e7eb;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
color: #9ca3af;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.step-title {
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.step-line {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: #e5e7eb;
|
||||
z-index: 0;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
&.active .step-circle {
|
||||
background-color: @primary-color;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
.ord__chips {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
overflow-x: auto;
|
||||
margin: 11px -16px 0;
|
||||
padding: 0 16px;
|
||||
scroll-snap-type: x proximity;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.step-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
.step-circle {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font-size: 10px;
|
||||
}
|
||||
.ord__chip {
|
||||
scroll-snap-align: start;
|
||||
flex: 0 0 auto;
|
||||
padding: 7px 14px;
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
color: @inkSoft;
|
||||
background: @canvas;
|
||||
border: 1px solid @hairline;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.ord__chip.is-on {
|
||||
color: #fff;
|
||||
background: @ink;
|
||||
border-color: @ink;
|
||||
}
|
||||
|
||||
.ord__chip:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
|
||||
.ord__body {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 90px;
|
||||
}
|
||||
|
||||
.ord__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 11px;
|
||||
}
|
||||
|
||||
.ord__empty {
|
||||
margin-top: 8px;
|
||||
padding: 34px 20px;
|
||||
border: 1px dashed @hairline;
|
||||
border-radius: 18px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: @inkFaint;
|
||||
}
|
||||
.ord__empty p {
|
||||
margin: 0;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
/* ── Detail drawer ─────────────────────────────────────────────────── */
|
||||
.detail {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: @canvas;
|
||||
}
|
||||
|
||||
.detail__bar {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 11px 16px;
|
||||
border-bottom: 1px solid @hairline;
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
backdrop-filter: blur(14px);
|
||||
}
|
||||
|
||||
.detail__title {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 800;
|
||||
color: @ink;
|
||||
}
|
||||
|
||||
.detail__close {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
border-radius: 12px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: @ink;
|
||||
background: @hairlineSoft;
|
||||
}
|
||||
|
||||
.detail__spacer {
|
||||
display: block;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
.detail__scroll {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.detail__block {
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
.detail__blockTitle {
|
||||
margin: 0 0 11px;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
color: @ink;
|
||||
}
|
||||
|
||||
.detail__steps {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
overflow-x: auto;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.detail__step {
|
||||
flex: 1 0 74px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.detail__stepDot {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-size: 11.5px;
|
||||
font-weight: 700;
|
||||
color: @inkFaint;
|
||||
background: @hairlineSoft;
|
||||
border: 1px solid @hairline;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.detail__step.is-done .detail__stepDot {
|
||||
color: #fff;
|
||||
background: @ink;
|
||||
border-color: @ink;
|
||||
}
|
||||
|
||||
.detail__step.is-active .detail__stepDot {
|
||||
color: #fff;
|
||||
background: @wine;
|
||||
border-color: @wine;
|
||||
}
|
||||
|
||||
.detail__stepTitle {
|
||||
font-size: 10px;
|
||||
line-height: 1.5;
|
||||
color: @inkFaint;
|
||||
}
|
||||
|
||||
.detail__step.is-done .detail__stepTitle {
|
||||
color: @inkSoft;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.detail__headline {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding-bottom: 14px;
|
||||
}
|
||||
|
||||
.detail__eyebrow {
|
||||
font-size: 11px;
|
||||
color: @inkFaint;
|
||||
}
|
||||
|
||||
.detail__number {
|
||||
padding-top: 4px;
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
color: @ink;
|
||||
}
|
||||
|
||||
.detail__status {
|
||||
flex: 0 0 auto;
|
||||
padding: 5px 12px;
|
||||
border-radius: 999px;
|
||||
font-size: 10.5px;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.detail__specs {
|
||||
margin: 0;
|
||||
border: 1px solid @hairline;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.detail__spec {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 14px;
|
||||
padding: 12px 14px;
|
||||
border-bottom: 1px solid @hairlineSoft;
|
||||
}
|
||||
|
||||
.detail__spec:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.detail__spec dt {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
color: @inkFaint;
|
||||
}
|
||||
|
||||
.detail__spec dd {
|
||||
margin: 0;
|
||||
font-size: 12.5px;
|
||||
font-weight: 700;
|
||||
color: @ink;
|
||||
}
|
||||
|
||||
.detail__items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user