Fix desktop shell scroll, product cards, and profile/order layouts.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -99,8 +99,47 @@
|
||||
background: var(--color-page-background);
|
||||
}
|
||||
|
||||
.app-shell--with-nav .app-layout {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* One continuous divider under sidebar brand + top header */
|
||||
.app-shell--with-nav .app-layout::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: var(--header-height);
|
||||
inset-inline: 0;
|
||||
height: 1px;
|
||||
background: var(--color-border);
|
||||
z-index: 30;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scroll lives on app-main__content. Children must not shrink+clip
|
||||
* (flex + overflow:hidden), or short viewports hide the bottom of pages.
|
||||
*/
|
||||
.app-shell--with-nav .app-main__content {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.app-shell--with-nav .app-content,
|
||||
.app-shell--with-nav .page-transition {
|
||||
flex: 1 0 auto;
|
||||
min-height: min-content;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* Let route roots grow with content so the shell scroll area can work */
|
||||
.app-shell--with-nav .page-transition > * {
|
||||
flex: 1 0 auto;
|
||||
min-height: min-content;
|
||||
height: auto;
|
||||
max-height: none;
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +156,8 @@
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: calc(100dvh - 32px);
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.app-shell--no-nav .app-frame {
|
||||
@@ -124,9 +165,24 @@
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: calc(100dvh - 32px);
|
||||
min-height: 0;
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-frame);
|
||||
background: var(--color-surface);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.app-shell--no-nav .app-main,
|
||||
.app-shell--no-nav .app-main__content,
|
||||
.app-shell--no-nav .app-content,
|
||||
.app-shell--no-nav .page-transition {
|
||||
flex: 1 0 auto;
|
||||
min-height: min-content;
|
||||
overflow: visible;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.app-shell--no-nav .app-content {
|
||||
@@ -135,6 +191,8 @@
|
||||
|
||||
.app-shell--no-nav .page {
|
||||
background: transparent;
|
||||
overflow: visible;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.app-shell--no-nav.app-shell--wide .app-layout,
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.desktop-page {
|
||||
flex: 1;
|
||||
flex: 1 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
min-height: auto;
|
||||
width: 100%;
|
||||
max-width: var(--content-max-width);
|
||||
margin-inline: auto;
|
||||
@@ -32,14 +32,13 @@
|
||||
|
||||
.desktop-two-col {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) var(--summary-panel-width, 320px);
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: 24px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.desktop-two-col__main {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.desktop-summary-panel {
|
||||
@@ -218,6 +217,10 @@
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.desktop-two-col:has(> .desktop-summary-panel) {
|
||||
grid-template-columns: minmax(0, 1fr) var(--summary-panel-width, 320px);
|
||||
}
|
||||
|
||||
.desktop-grid-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 20px;
|
||||
|
||||
@@ -12,11 +12,15 @@
|
||||
max-width: var(--content-max-width);
|
||||
margin-inline: auto;
|
||||
width: 100%;
|
||||
overflow: visible;
|
||||
min-height: auto;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
.page__scroll {
|
||||
overflow: visible;
|
||||
flex: none;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.page__scroll--with-nav {
|
||||
@@ -201,25 +205,6 @@
|
||||
background: var(--color-primary-hover);
|
||||
}
|
||||
|
||||
.orders-toolbar__layout-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--color-surface);
|
||||
color: var(--color-text-muted);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.orders-toolbar__layout-btn:hover {
|
||||
border-color: var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.orders-toolbar__pills {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -99,12 +99,16 @@ function logout() {
|
||||
display: none;
|
||||
flex-shrink: 0;
|
||||
height: var(--header-height);
|
||||
min-height: var(--header-height);
|
||||
max-height: var(--header-height);
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
padding: 0 28px;
|
||||
background: var(--color-surface);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
/* Border drawn by shared shell rule so it aligns with the sidebar brand */
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
|
||||
@@ -80,7 +80,7 @@ function navigate(item: (typeof DESKTOP_NAV_ITEMS)[number]) {
|
||||
height: 100%;
|
||||
background: var(--color-surface);
|
||||
border-inline-start: 1px solid var(--color-border);
|
||||
padding: 20px 12px;
|
||||
padding: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@@ -90,25 +90,24 @@ function navigate(item: (typeof DESKTOP_NAV_ITEMS)[number]) {
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.desktop-sidebar {
|
||||
padding: 24px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.desktop-sidebar__brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 10px;
|
||||
padding: 0 8px 20px;
|
||||
margin-bottom: 8px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
flex-shrink: 0;
|
||||
height: var(--header-height);
|
||||
min-height: var(--header-height);
|
||||
max-height: var(--header-height);
|
||||
box-sizing: border-box;
|
||||
padding: 0 20px;
|
||||
/* Border drawn by shared shell rule so it aligns with the top header */
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.desktop-sidebar__brand {
|
||||
padding: 0 12px 24px;
|
||||
padding: 0 28px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +137,13 @@ function navigate(item: (typeof DESKTOP_NAV_ITEMS)[number]) {
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
flex: 1;
|
||||
padding: 12px 12px 0;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.desktop-sidebar__nav {
|
||||
padding: 16px 16px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.desktop-sidebar__item {
|
||||
@@ -222,7 +228,7 @@ function navigate(item: (typeof DESKTOP_NAV_ITEMS)[number]) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-top: auto;
|
||||
margin: auto 12px 20px;
|
||||
padding: 14px;
|
||||
background: var(--color-primary-soft);
|
||||
border-radius: var(--radius-lg);
|
||||
@@ -230,6 +236,7 @@ function navigate(item: (typeof DESKTOP_NAV_ITEMS)[number]) {
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.desktop-sidebar__support {
|
||||
margin: auto 16px 24px;
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
import type { StatusHeroConfig } from '@/utils/orderTracking'
|
||||
|
||||
defineProps<{
|
||||
config: StatusHeroConfig
|
||||
}>()
|
||||
|
||||
const icons = {
|
||||
waiting: 'vuesax-linear:timer-1',
|
||||
success: 'vuesax-linear:tick-circle',
|
||||
info: 'vuesax-linear:info-circle',
|
||||
none: 'vuesax-linear:info-circle',
|
||||
} as const
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="status-hero" :class="`status-hero--${config.variant}`">
|
||||
<div v-if="config.variant !== 'none'" class="status-hero" :class="`status-hero--${config.variant}`">
|
||||
<div class="status-hero__icon" aria-hidden="true">
|
||||
<span v-if="config.variant === 'waiting'">⏳</span>
|
||||
<span v-else-if="config.variant === 'success'">✓</span>
|
||||
<span v-else>ℹ</span>
|
||||
<BasicIcon :icon="icons[config.variant]" :size="28" :stroke-width="1.5" />
|
||||
</div>
|
||||
<h2 class="status-hero__title">{{ config.title }}</h2>
|
||||
<p v-if="config.description" class="status-hero__desc">{{ config.description }}</p>
|
||||
@@ -34,8 +40,8 @@ defineProps<{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 14px;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.status-hero--waiting .status-hero__icon {
|
||||
@@ -44,10 +50,8 @@ defineProps<{
|
||||
}
|
||||
|
||||
.status-hero--success .status-hero__icon {
|
||||
background: var(--color-success);
|
||||
color: var(--color-text-on-primary, #fff);
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
background: var(--color-primary-soft);
|
||||
color: var(--color-success);
|
||||
}
|
||||
|
||||
.status-hero--info .status-hero__icon {
|
||||
@@ -73,4 +77,22 @@ defineProps<{
|
||||
color: var(--color-text-muted);
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.status-hero {
|
||||
align-items: flex-start;
|
||||
text-align: start;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.status-hero__icon {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.status-hero__desc {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -101,6 +101,12 @@ function goToOrdering() {
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.cart-view {
|
||||
flex: 1 0 auto;
|
||||
min-height: auto;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.dialog-actions {
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
|
||||
@@ -249,12 +249,18 @@ const quickLinks = [
|
||||
.cart-desktop-page__layout {
|
||||
direction: ltr;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 300px;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: 20px;
|
||||
align-items: start;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
@media (min-width: 1100px) {
|
||||
.cart-desktop-page__layout {
|
||||
grid-template-columns: minmax(0, 1fr) 300px;
|
||||
}
|
||||
}
|
||||
|
||||
.cart-desktop-page__main,
|
||||
.cart-desktop-page__sidebar {
|
||||
direction: rtl;
|
||||
@@ -400,13 +406,13 @@ const quickLinks = [
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-muted);
|
||||
background: #f8fafc;
|
||||
background: var(--color-bg);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.cart-items-table td {
|
||||
padding: 16px 18px;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
color: var(--color-text);
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -423,7 +429,7 @@ const quickLinks = [
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
color: #64748b;
|
||||
color: var(--color-text-muted);
|
||||
padding: 4px;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,8 @@ watch(
|
||||
(wide) => {
|
||||
if (!wide) return
|
||||
showTracking.value = true
|
||||
showTimeline.value = true
|
||||
showOrderDetails.value = true
|
||||
if (route.query.track === '1' || route.query.receipt === '1') return
|
||||
void router.replace({
|
||||
query: { ...route.query, track: '1' },
|
||||
@@ -296,6 +298,13 @@ async function submitReceipt() {
|
||||
</div>
|
||||
|
||||
<div v-if="showTracking" class="order-detail-page__tracking">
|
||||
<section class="order-detail-page__tracking-aside">
|
||||
<div class="tracking-card tracking-card--hero">
|
||||
<OrderStatusHero v-if="statusHero" :config="statusHero" />
|
||||
<OrderTrackingSummary :order="order" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="order-detail-page__tracking-main">
|
||||
<div v-if="nextAction" class="order-next-action">
|
||||
<span class="order-next-action__text">برای ادامه سفارش، این مرحله باقی مانده است.</span>
|
||||
@@ -321,6 +330,7 @@ async function submitReceipt() {
|
||||
</button>
|
||||
|
||||
<div v-show="showTimeline" class="tracking-collapse__body">
|
||||
<h2 class="tracking-panel__title desktop-only">پیگیری وضعیت</h2>
|
||||
<div class="tracking-collapse__section tracking-collapse__section--flush">
|
||||
<OrderTrackingTimeline :order="order" />
|
||||
</div>
|
||||
@@ -344,6 +354,7 @@ async function submitReceipt() {
|
||||
</button>
|
||||
|
||||
<div v-show="showOrderDetails" class="tracking-collapse__body">
|
||||
<h2 class="tracking-panel__title desktop-only">{{ trackingFooterLabel }}</h2>
|
||||
<div class="tracking-collapse__section">
|
||||
<h3 class="tracking-card__title">اقلام سفارش</h3>
|
||||
<div v-for="item in order.items" :key="item.id" class="item-row">
|
||||
@@ -423,13 +434,6 @@ async function submitReceipt() {
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="order-detail-page__tracking-aside">
|
||||
<div class="tracking-card tracking-card--hero">
|
||||
<OrderStatusHero v-if="statusHero" :config="statusHero" />
|
||||
<OrderTrackingSummary :order="order" />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -508,12 +512,31 @@ async function submitReceipt() {
|
||||
.order-detail-page {
|
||||
padding: 20px 0 28px;
|
||||
background: transparent;
|
||||
max-width: none;
|
||||
margin-inline: 0;
|
||||
max-width: var(--content-max-width);
|
||||
margin-inline: auto;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.order-detail-back {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-bottom: 12px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--color-primary);
|
||||
font-family: inherit;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.order-detail-back:hover {
|
||||
color: var(--color-primary-hover);
|
||||
}
|
||||
|
||||
.order-detail-page__nav {
|
||||
display: grid;
|
||||
grid-template-columns: 40px 1fr 40px;
|
||||
@@ -699,7 +722,7 @@ async function submitReceipt() {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Mobile: status first, then timeline */
|
||||
/* Mobile: status first (above content) */
|
||||
.order-detail-page__tracking-aside {
|
||||
order: -1;
|
||||
}
|
||||
@@ -707,8 +730,8 @@ async function submitReceipt() {
|
||||
.tracking-card {
|
||||
padding: 16px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 16px;
|
||||
background: var(--color-elevated);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-surface);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
@@ -718,6 +741,10 @@ async function submitReceipt() {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tracking-panel__title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.order-detail-page__tracking :deep(.status-hero) {
|
||||
padding: 4px 0 8px;
|
||||
}
|
||||
@@ -806,9 +833,20 @@ async function submitReceipt() {
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.order-detail-page__desktop-head {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.order-detail-page__body {
|
||||
overflow: visible;
|
||||
min-height: auto;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.order-detail-page__tracking {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.35fr) minmax(260px, 0.9fr);
|
||||
/* RTL: first column (status) sits beside sidebar on the right */
|
||||
grid-template-columns: minmax(260px, 320px) minmax(0, 1fr);
|
||||
gap: 20px;
|
||||
align-items: start;
|
||||
margin-top: 8px;
|
||||
@@ -820,6 +858,52 @@ async function submitReceipt() {
|
||||
top: 16px;
|
||||
}
|
||||
|
||||
.order-detail-page__tracking-main {
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.tracking-card--hero {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.tracking-collapse {
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-surface);
|
||||
box-shadow: var(--shadow-sm);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tracking-collapse__trigger {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tracking-collapse__body {
|
||||
display: flex !important;
|
||||
padding: 20px;
|
||||
border-top: none;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.tracking-panel__title {
|
||||
display: block;
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.tracking-collapse__section {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.tracking-collapse__section + .tracking-collapse__section,
|
||||
.tracking-collapse__section + .tracking-alert,
|
||||
.tracking-alert + .tracking-collapse__section {
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.order-detail-page__footer {
|
||||
display: none;
|
||||
}
|
||||
@@ -827,7 +911,7 @@ async function submitReceipt() {
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.order-detail-page__tracking {
|
||||
grid-template-columns: minmax(0, 1.4fr) minmax(300px, 0.85fr);
|
||||
grid-template-columns: minmax(300px, 360px) minmax(0, 1fr);
|
||||
gap: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import IconHourglass from '@/components/icons/IconHourglass.vue'
|
||||
import IconProgressClock from '@/components/icons/IconProgressClock.vue'
|
||||
import IconCheckCircleOutline from '@/components/icons/IconCheckCircleOutline.vue'
|
||||
import IconFilter from '@/components/icons/IconFilter.vue'
|
||||
import IconLayoutGrid from '@/components/icons/IconLayoutGrid.vue'
|
||||
import IconMoreVertical from '@/components/icons/IconMoreVertical.vue'
|
||||
import IconChevronLeft from '@/components/icons/IconChevronLeft.vue'
|
||||
import { formatDateTime, formatNumber } from '@/utils/format'
|
||||
@@ -106,9 +105,6 @@ const emit = defineEmits<{
|
||||
<IconFilter :size="18" />
|
||||
فیلترها
|
||||
</button>
|
||||
<button type="button" class="orders-toolbar__layout-btn" aria-label="نمای جدول">
|
||||
<IconLayoutGrid :size="18" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="orders-toolbar__pills" role="tablist" aria-label="فیلتر وضعیت">
|
||||
<button
|
||||
|
||||
@@ -193,55 +193,65 @@ const emit = defineEmits<{
|
||||
|
||||
.product-desktop-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.product-desktop-grid {
|
||||
grid-template-columns: repeat(2, minmax(280px, 420px));
|
||||
}
|
||||
}
|
||||
|
||||
.product-desktop-card {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-surface);
|
||||
box-shadow: var(--shadow-sm);
|
||||
max-width: 420px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.product-desktop-card {
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
gap: 14px;
|
||||
max-width: 420px;
|
||||
}
|
||||
}
|
||||
|
||||
.product-desktop-card__preview {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border-radius: var(--radius-sm);
|
||||
background: linear-gradient(135deg, #e2e8f0, #cbd5e1);
|
||||
background: linear-gradient(135deg, var(--color-border-light), var(--color-border));
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.product-desktop-card__body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.product-desktop-card__head {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
align-items: baseline;
|
||||
gap: 4px 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.product-desktop-card__head h3 {
|
||||
margin: 0;
|
||||
font-size: 0.9375rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.product-desktop-card__head span {
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-text-muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.product-desktop-card__meta {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import BaseInput from '@/components/ui/BaseInput.vue'
|
||||
import FormErrorBanner from '@/components/ui/FormErrorBanner.vue'
|
||||
import BaseButton from '@/components/ui/BaseButton.vue'
|
||||
import IconChevronBack from '@/components/icons/IconChevronBack.vue'
|
||||
import ProfileSubpageLayout from '@/views/profile/components/ProfileSubpageLayout.vue'
|
||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
import {
|
||||
FORM_VALIDATION_SUMMARY,
|
||||
clearFieldError,
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
type FieldErrors,
|
||||
} from '@/utils/formErrors'
|
||||
|
||||
const router = useRouter()
|
||||
const auth = useAuthStore()
|
||||
|
||||
const firstName = ref(auth.user?.firstName ?? '')
|
||||
@@ -63,21 +62,24 @@ async function save() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page profile-edit-page">
|
||||
<header class="profile-edit-page__nav">
|
||||
<button type="button" class="nav-btn" aria-label="بازگشت" @click="router.push('/profile')">
|
||||
<IconChevronBack :size="20" />
|
||||
</button>
|
||||
<h1 class="profile-edit-page__title">اطلاعات کاربری</h1>
|
||||
<button type="button" class="nav-btn nav-btn--ghost" aria-hidden="true" tabindex="-1">
|
||||
<IconChevronBack :size="18" class="nav-btn__flip" />
|
||||
</button>
|
||||
</header>
|
||||
<ProfileSubpageLayout
|
||||
title="اطلاعات کاربری"
|
||||
subtitle="ویرایش نام و کد ملی حساب کاربری"
|
||||
>
|
||||
<FormErrorBanner :message="formError" />
|
||||
|
||||
<div class="page__scroll page__scroll--with-nav">
|
||||
<div class="profile-form card">
|
||||
<FormErrorBanner :message="formError" />
|
||||
<div class="profile-edit">
|
||||
<div class="profile-edit__intro">
|
||||
<span class="profile-edit__intro-icon" aria-hidden="true">
|
||||
<BasicIcon icon="vuesax-linear:profile-circle" :size="22" :stroke-width="1.5" />
|
||||
</span>
|
||||
<div class="profile-edit__intro-text">
|
||||
<strong>مشخصات فردی</strong>
|
||||
<p>این اطلاعات روی سفارشها و چکهای ثبتشده نمایش داده میشود.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="profile-edit__grid">
|
||||
<BaseInput
|
||||
v-model="firstName"
|
||||
label="نام"
|
||||
@@ -92,65 +94,113 @@ async function save() {
|
||||
/>
|
||||
<BaseInput
|
||||
v-model="nationalId"
|
||||
class="profile-edit__full"
|
||||
label="کد ملی"
|
||||
maxlength="10"
|
||||
inputmode="numeric"
|
||||
hint="برای ثبت چک الزامی است"
|
||||
:error="fieldErrors.nationalId"
|
||||
@update:model-value="clearField('nationalId')"
|
||||
/>
|
||||
<BaseButton block @click="save">ذخیره تغییرات</BaseButton>
|
||||
<p v-if="saved" class="profile-form__saved">ذخیره شد ✓</p>
|
||||
</div>
|
||||
|
||||
<div class="profile-edit__actions">
|
||||
<BaseButton class="profile-edit__save" @click="save">ذخیره تغییرات</BaseButton>
|
||||
<p v-if="saved" class="profile-edit__saved">ذخیره شد ✓</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ProfileSubpageLayout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.profile-edit-page {
|
||||
overflow: hidden;
|
||||
.profile-edit {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.profile-edit-page__nav {
|
||||
.profile-edit__intro {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding: 14px 16px;
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-bg);
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.profile-edit__intro-icon {
|
||||
flex-shrink: 0;
|
||||
display: grid;
|
||||
grid-template-columns: 40px 1fr 40px;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.profile-edit-page__title {
|
||||
margin: 0;
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--color-text);
|
||||
cursor: pointer;
|
||||
border-radius: var(--radius-sm);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
background: var(--color-primary-soft);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.nav-btn--ghost {
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
.profile-edit__intro-text {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.nav-btn__flip {
|
||||
transform: scaleX(-1);
|
||||
.profile-edit__intro-text strong {
|
||||
display: block;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.profile-form__saved {
|
||||
margin: 8px 0 0;
|
||||
.profile-edit__intro-text p {
|
||||
margin: 4px 0 0;
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.5;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.profile-edit__grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.profile-edit__grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.profile-edit__full {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-edit__actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.profile-edit__actions {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.profile-edit__save {
|
||||
width: auto;
|
||||
min-width: 180px;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-edit__saved {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
text-align: center;
|
||||
color: var(--color-success);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -36,7 +36,7 @@ function goBack() {
|
||||
|
||||
<!-- Desktop -->
|
||||
<div class="desktop-page profile-subpage-desktop desktop-only">
|
||||
<header class="desktop-page__head">
|
||||
<header class="desktop-page__head profile-subpage-desktop__head">
|
||||
<button type="button" class="profile-subpage-desktop__back" @click="goBack">
|
||||
<IconChevronBack :size="18" />
|
||||
بازگشت به پروفایل
|
||||
@@ -45,9 +45,9 @@ function goBack() {
|
||||
<p v-if="subtitle" class="desktop-page__subtitle">{{ subtitle }}</p>
|
||||
</header>
|
||||
|
||||
<div class="profile-subpage-desktop__card card">
|
||||
<section class="profile-subpage-desktop__panel card">
|
||||
<slot />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -97,6 +97,10 @@ function goBack() {
|
||||
padding: 20px 16px;
|
||||
}
|
||||
|
||||
.profile-subpage-desktop__head {
|
||||
max-width: 720px;
|
||||
}
|
||||
|
||||
.profile-subpage-desktop__back {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -106,13 +110,20 @@ function goBack() {
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--color-primary);
|
||||
font-family: inherit;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.profile-subpage-desktop__card {
|
||||
max-width: 560px;
|
||||
padding: 24px;
|
||||
.profile-subpage-desktop__back:hover {
|
||||
color: var(--color-primary-hover);
|
||||
}
|
||||
|
||||
.profile-subpage-desktop__panel {
|
||||
width: 100%;
|
||||
max-width: 720px;
|
||||
padding: 28px;
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user