Improve customer orders: own list, payment/order status, and transaction details.
Also widen order item modals, link FA product names, and polish related dashboard UI. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
55071af98c
commit
06589600bd
@@ -1,5 +1,6 @@
|
||||
.modalWide {
|
||||
max-width: 720px;
|
||||
max-width: 960px;
|
||||
width: min(960px, calc(100vw - 32px));
|
||||
}
|
||||
|
||||
.body {
|
||||
@@ -42,7 +43,7 @@
|
||||
|
||||
.itemRow {
|
||||
display: grid;
|
||||
grid-template-columns: 64px minmax(0, 1fr) 72px 120px 120px;
|
||||
grid-template-columns: 64px minmax(220px, 1.6fr) 64px 110px 110px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
@@ -105,7 +106,7 @@
|
||||
|
||||
.tableHead {
|
||||
display: grid;
|
||||
grid-template-columns: 64px minmax(0, 1fr) 72px 120px 120px;
|
||||
grid-template-columns: 64px minmax(220px, 1.6fr) 64px 110px 110px;
|
||||
gap: 10px;
|
||||
padding: 0 12px 2px;
|
||||
font-size: 11px;
|
||||
@@ -176,10 +177,10 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 10px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.22);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface);
|
||||
padding: 8px 0;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.shippingGroupHeader {
|
||||
@@ -218,6 +219,19 @@
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.itemTitleLink {
|
||||
display: inline;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--primary);
|
||||
line-height: 1.3;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.itemTitleLink:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.totalsBlock {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useT } from '../i18n/useT'
|
||||
import type { Order } from '../services/orderService'
|
||||
import { formatCellForDisplay } from '../lib/cellNumber'
|
||||
import { formatIrtPrice } from '../utils/irtPrice'
|
||||
import { buildWebsiteContentUrl } from '../utils/websiteLinks'
|
||||
import modalStyles from './VariationsModal.module.css'
|
||||
import styles from './OrderItemsModal.module.css'
|
||||
|
||||
@@ -181,7 +182,7 @@ export function OrderItemsModal({ open, order, onClose }: OrderItemsModalProps)
|
||||
{group.methodLabel ? (
|
||||
<div className={styles.shippingMethod}>{group.methodLabel}</div>
|
||||
) : null}
|
||||
{group.itemNames.length > 0 ? (
|
||||
{shippingInfo.groups.length > 1 && group.itemNames.length > 0 ? (
|
||||
<ul className={styles.shippingItemNames}>
|
||||
{group.itemNames.map((name) => (
|
||||
<li key={`${group.groupId}-${name}`}>{name}</li>
|
||||
@@ -206,7 +207,15 @@ export function OrderItemsModal({ open, order, onClose }: OrderItemsModalProps)
|
||||
<span>{t('orderItems.lineTotal')}</span>
|
||||
</div>
|
||||
<ul className={styles.itemList}>
|
||||
{order.items.map((item) => (
|
||||
{order.items.map((item) => {
|
||||
const displayTitle = item.productNameFa?.trim() || item.productTitle
|
||||
const productHref = buildWebsiteContentUrl('product', {
|
||||
id: item.productId,
|
||||
nameFa: item.productNameFa,
|
||||
title: item.productTitle,
|
||||
slug: item.productSlug,
|
||||
})
|
||||
return (
|
||||
<li key={item.id} className={styles.itemRow}>
|
||||
<div className={styles.itemThumb}>
|
||||
{item.productImage ? (
|
||||
@@ -216,7 +225,18 @@ export function OrderItemsModal({ open, order, onClose }: OrderItemsModalProps)
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.itemTitle}>{item.productTitle}</div>
|
||||
{productHref ? (
|
||||
<a
|
||||
className={styles.itemTitleLink}
|
||||
href={productHref}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{displayTitle}
|
||||
</a>
|
||||
) : (
|
||||
<div className={styles.itemTitle}>{displayTitle}</div>
|
||||
)}
|
||||
<div className={styles.itemVariant}>{formatVariantLabel(item.selections)}</div>
|
||||
{item.variantSku && (
|
||||
<div className={styles.itemSku}>
|
||||
@@ -228,7 +248,8 @@ export function OrderItemsModal({ open, order, onClose }: OrderItemsModalProps)
|
||||
<div className={styles.unitCell}>{formatIrtPrice(item.unitPrice)}</div>
|
||||
<div className={styles.totalCell}>{formatIrtPrice(item.lineTotal)}</div>
|
||||
</li>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -4,6 +4,42 @@
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
@keyframes homeReveal {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(18px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.revealItem {
|
||||
opacity: 0;
|
||||
animation: homeReveal 0.58s cubic-bezier(0.22, 1, 0.36, 1) forwards;
|
||||
animation-delay: var(--reveal-delay, 0ms);
|
||||
}
|
||||
|
||||
.revealItemFill {
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.revealItemFill > * {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.revealItem {
|
||||
opacity: 1;
|
||||
animation: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.pageHeader {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--glass-shadow);
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
@@ -45,8 +46,8 @@
|
||||
.image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
padding: 10px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.imagePlaceholder {
|
||||
|
||||
@@ -51,6 +51,13 @@
|
||||
box-shadow: 0 12px 28px rgba(var(--primary-rgb) / 0.45);
|
||||
}
|
||||
|
||||
.addFab:disabled {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.fabDock {
|
||||
inset-inline-end: 20px;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useState, type CSSProperties } from 'react'
|
||||
import { CalendarDays } from 'lucide-react'
|
||||
import {
|
||||
ShoppingBag,
|
||||
@@ -135,6 +135,14 @@ async function loadSectionCounts(signal: AbortSignal): Promise<SectionCounts> {
|
||||
return counts
|
||||
}
|
||||
|
||||
const REVEAL_STEP_MS = 75
|
||||
const REVEAL_HEADER_MS = 40
|
||||
const REVEAL_TILE_BASE_MS = 120
|
||||
|
||||
function revealStyle(delayMs: number): CSSProperties {
|
||||
return { ['--reveal-delay' as string]: `${delayMs}ms` }
|
||||
}
|
||||
|
||||
export function HomePage() {
|
||||
const { user } = useAuth()
|
||||
const { locale } = useLocale()
|
||||
@@ -170,9 +178,15 @@ export function HomePage() {
|
||||
weekday: 'long',
|
||||
}).format(new Date())
|
||||
|
||||
const chartRevealBase =
|
||||
REVEAL_TILE_BASE_MS + visibleSections.length * REVEAL_STEP_MS
|
||||
|
||||
return (
|
||||
<main className={styles.content}>
|
||||
<div className={styles.pageHeader}>
|
||||
<div
|
||||
className={`${styles.pageHeader} ${styles.revealItem}`}
|
||||
style={revealStyle(REVEAL_HEADER_MS)}
|
||||
>
|
||||
<div>
|
||||
<h2 className={styles.welcomeTitle}>{t('home.welcome', { name: firstName })}</h2>
|
||||
<p className={styles.welcomeSubtitle}>{t('home.subtitle')}</p>
|
||||
@@ -185,25 +199,39 @@ export function HomePage() {
|
||||
</div>
|
||||
|
||||
<div className={styles.gridHome}>
|
||||
{visibleSections.map((section) => (
|
||||
<SectionCard
|
||||
{visibleSections.map((section, index) => (
|
||||
<div
|
||||
key={section.href}
|
||||
icon={section.icon}
|
||||
title={t(section.titleKey)}
|
||||
description={t(section.descKey)}
|
||||
linkText={t(section.linkKey)}
|
||||
href={section.href}
|
||||
className={section.wide ? styles.span4 : undefined}
|
||||
count={section.countKey ? counts[section.countKey] : undefined}
|
||||
countLabel={section.countLabelKey ? t(section.countLabelKey) : undefined}
|
||||
/>
|
||||
className={[
|
||||
styles.revealItem,
|
||||
styles.revealItemFill,
|
||||
section.wide ? styles.span4 : undefined,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
style={revealStyle(REVEAL_TILE_BASE_MS + index * REVEAL_STEP_MS)}
|
||||
>
|
||||
<SectionCard
|
||||
icon={section.icon}
|
||||
title={t(section.titleKey)}
|
||||
description={t(section.descKey)}
|
||||
linkText={t(section.linkKey)}
|
||||
href={section.href}
|
||||
count={section.countKey ? counts[section.countKey] : undefined}
|
||||
countLabel={section.countLabelKey ? t(section.countLabelKey) : undefined}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{visibleCharts.length > 0 ? (
|
||||
<div className={styles.grid12}>
|
||||
{visibleCharts.map((chartId, index) => (
|
||||
<div key={`${chartId}-${index}`} className={styles.col6}>
|
||||
<div
|
||||
key={`${chartId}-${index}`}
|
||||
className={`${styles.col6} ${styles.revealItem} ${styles.revealItemFill}`}
|
||||
style={revealStyle(chartRevealBase + index * REVEAL_STEP_MS)}
|
||||
>
|
||||
<HomeChartSlot chartId={chartId} />
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -16,6 +16,8 @@ export interface OrderItem {
|
||||
storeItemVariantId: string | null
|
||||
productId: string
|
||||
productTitle: string
|
||||
productNameFa?: string | null
|
||||
productSlug?: string | null
|
||||
productImage: string | null
|
||||
variantSku: string | null
|
||||
unitPrice: number
|
||||
|
||||
Reference in New Issue
Block a user