mirror of
https://git.meshkee.com/Meshkee/dashboards.git
synced 2026-08-11 22:30:58 +04:30
Polish category edit, product image placeholder, and menu blur.
Add a clear pencil edit on category rows, show a proper no-image placeholder on product cards, and strengthen overflow menu glass blur. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
12ab1f8006
commit
5eaf7fb046
@@ -1,4 +1,4 @@
|
||||
import { FolderPlus, Layers, ListTree, Trash2, ChevronRight, ClipboardList } from 'lucide-react'
|
||||
import { FolderPlus, Layers, Trash2, ChevronRight, ClipboardList, Pencil } from 'lucide-react'
|
||||
import { useLocale } from '@meshkee/dashboard-ui'
|
||||
import type { Category } from '../types/category'
|
||||
import { useT } from '../i18n/useT'
|
||||
@@ -16,7 +16,7 @@ interface CategoryRowProps {
|
||||
onAddSub: (id: string) => void
|
||||
onVariations: (id: string) => void
|
||||
onTechnicalForm: (id: string) => void
|
||||
onOptions: (id: string) => void
|
||||
onEdit: (id: string) => void
|
||||
onRemove: (id: string) => void
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export function CategoryRow({
|
||||
onAddSub,
|
||||
onVariations,
|
||||
onTechnicalForm,
|
||||
onOptions,
|
||||
onEdit,
|
||||
onRemove,
|
||||
}: CategoryRowProps) {
|
||||
const t = useT()
|
||||
@@ -121,13 +121,13 @@ export function CategoryRow({
|
||||
)}
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip label={t('categories.tooltip.options')}>
|
||||
<Tooltip label={t('categories.tooltip.edit')}>
|
||||
<button
|
||||
className={styles.controlBtn}
|
||||
onClick={() => onOptions(category.id)}
|
||||
aria-label={t('categories.tooltip.options')}
|
||||
onClick={() => onEdit(category.id)}
|
||||
aria-label={t('categories.tooltip.edit')}
|
||||
>
|
||||
<ListTree size={17} />
|
||||
<Pencil size={17} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip label={t('categories.tooltip.remove')}>
|
||||
|
||||
@@ -12,7 +12,7 @@ interface CategoryTreeProps {
|
||||
onAddSub: (id: string) => void
|
||||
onVariations: (id: string) => void
|
||||
onTechnicalForm: (id: string) => void
|
||||
onOptions: (id: string) => void
|
||||
onEdit: (id: string) => void
|
||||
onRemove: (id: string) => void
|
||||
parentId?: string | null
|
||||
depth?: number
|
||||
@@ -27,7 +27,7 @@ export function CategoryTree({
|
||||
onAddSub,
|
||||
onVariations,
|
||||
onTechnicalForm,
|
||||
onOptions,
|
||||
onEdit,
|
||||
onRemove,
|
||||
parentId = null,
|
||||
depth = 0,
|
||||
@@ -53,7 +53,7 @@ export function CategoryTree({
|
||||
onAddSub={onAddSub}
|
||||
onVariations={onVariations}
|
||||
onTechnicalForm={onTechnicalForm}
|
||||
onOptions={onOptions}
|
||||
onEdit={onEdit}
|
||||
onRemove={onRemove}
|
||||
/>
|
||||
{childCount && (
|
||||
@@ -68,7 +68,7 @@ export function CategoryTree({
|
||||
onAddSub={onAddSub}
|
||||
onVariations={onVariations}
|
||||
onTechnicalForm={onTechnicalForm}
|
||||
onOptions={onOptions}
|
||||
onEdit={onEdit}
|
||||
onRemove={onRemove}
|
||||
parentId={category.id}
|
||||
depth={depth + 1}
|
||||
|
||||
@@ -46,6 +46,20 @@
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.imagePlaceholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: rgba(148, 163, 184, 0.75);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(148, 163, 184, 0.14) 0%,
|
||||
rgba(148, 163, 184, 0.05) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Pencil, Info, Trash2, Layers, MessageSquare, ClipboardList } from 'lucide-react'
|
||||
import {
|
||||
Pencil,
|
||||
Info,
|
||||
Trash2,
|
||||
Layers,
|
||||
MessageSquare,
|
||||
ClipboardList,
|
||||
ImageOff,
|
||||
} from 'lucide-react'
|
||||
import { useLocale } from '@meshkee/dashboard-ui'
|
||||
import type { Product } from '../types/product'
|
||||
import { useT } from '../i18n/useT'
|
||||
@@ -39,17 +48,31 @@ export function ProductCard({
|
||||
: ''
|
||||
: product.nameFa
|
||||
const categoryLabel = isFa ? product.categoryFa || product.category : product.category
|
||||
const imageSrc = product.image?.trim() || ''
|
||||
const [imageFailed, setImageFailed] = useState(false)
|
||||
const showImage = Boolean(imageSrc) && !imageFailed
|
||||
|
||||
useEffect(() => {
|
||||
setImageFailed(false)
|
||||
}, [imageSrc])
|
||||
|
||||
return (
|
||||
<article className={styles.card}>
|
||||
<Link to={`/products/detail/${product.id}`} className={styles.clickable}>
|
||||
<div className={styles.imageWrap}>
|
||||
<img
|
||||
src={product.image}
|
||||
alt={primaryName}
|
||||
className={styles.image}
|
||||
loading="lazy"
|
||||
/>
|
||||
{showImage ? (
|
||||
<img
|
||||
src={imageSrc}
|
||||
alt={primaryName}
|
||||
className={styles.image}
|
||||
loading="lazy"
|
||||
onError={() => setImageFailed(true)}
|
||||
/>
|
||||
) : (
|
||||
<div className={styles.imagePlaceholder} aria-hidden="true">
|
||||
<ImageOff size={28} strokeWidth={1.5} />
|
||||
</div>
|
||||
)}
|
||||
{product.status === 'draft' && (
|
||||
<span className={styles.badge}>{t('products.card.draft')}</span>
|
||||
)}
|
||||
|
||||
@@ -453,7 +453,7 @@ export function CategoriesPage() {
|
||||
onAddSub={(id) => openCreateModal(id, t('categories.addSub'))}
|
||||
onVariations={openVariations}
|
||||
onTechnicalForm={openTechnicalForm}
|
||||
onOptions={(id) => {
|
||||
onEdit={(id) => {
|
||||
const category = categories.find((item) => item.id === id)
|
||||
if (category) openEditModal(category)
|
||||
}}
|
||||
|
||||
@@ -31,11 +31,14 @@
|
||||
min-width: 220px;
|
||||
padding: 6px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.85);
|
||||
background: rgba(255, 255, 255, 0.78);
|
||||
backdrop-filter: blur(72px) saturate(1.35);
|
||||
-webkit-backdrop-filter: blur(72px) saturate(1.35);
|
||||
box-shadow: 0 12px 40px rgba(15, 23, 42, 0.16);
|
||||
border: 1px solid rgba(255, 255, 255, 0.92);
|
||||
background: rgba(255, 255, 255, 0.58);
|
||||
backdrop-filter: blur(160px) saturate(1.85);
|
||||
-webkit-backdrop-filter: blur(160px) saturate(1.85);
|
||||
box-shadow:
|
||||
0 12px 40px rgba(15, 23, 42, 0.16),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.65);
|
||||
isolation: isolate;
|
||||
animation: menuIn 0.14s ease;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user