Add business workshop management UI with Jalali scheduling and shared dashboard polish.
Includes workshop list/edit/details, registrations modal, category tree, shared EmptyState/RichTextEditor/toasts, and fixes for the Jalali date picker and thumbnail field layout. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
89269dff04
commit
d5aca8bea7
@@ -0,0 +1,6 @@
|
||||
export {
|
||||
EmptyState,
|
||||
EmptyStateEmphasis,
|
||||
EmptyTableRow,
|
||||
type EmptyStateProps,
|
||||
} from '@meshkee/dashboard-ui'
|
||||
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { X } from 'lucide-react'
|
||||
import { useT } from '../i18n/useT'
|
||||
import { EmptyState } from './EmptyState'
|
||||
import type { Order } from '../services/orderService'
|
||||
import { formatCellForDisplay } from '../lib/cellNumber'
|
||||
import { formatIrtPrice } from '../utils/irtPrice'
|
||||
@@ -106,7 +107,7 @@ export function OrderItemsModal({ open, order, onClose }: OrderItemsModalProps)
|
||||
</div>
|
||||
|
||||
{order.items.length === 0 ? (
|
||||
<p className={styles.empty}>{t('orderItems.empty')}</p>
|
||||
<EmptyState layout="inline" message={t('orderItems.empty')} />
|
||||
) : (
|
||||
<div className={styles.tableBlock}>
|
||||
<div className={styles.tableHead}>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
position: fixed;
|
||||
bottom: 24px;
|
||||
left: 24px;
|
||||
z-index: 300;
|
||||
z-index: var(--toast-z-index, 11000);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
@@ -19,12 +19,12 @@
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
color: var(--text-primary);
|
||||
background: var(--elevated-surface);
|
||||
background: var(--toast-bg, rgba(255, 255, 255, 0.94));
|
||||
backdrop-filter: blur(var(--blur-glass));
|
||||
-webkit-backdrop-filter: blur(var(--blur-glass));
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-sm);
|
||||
box-shadow: var(--glass-shadow);
|
||||
box-shadow: var(--toast-shadow, 0 12px 40px rgba(15, 23, 42, 0.14));
|
||||
}
|
||||
|
||||
.toastIn {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
--surface: rgba(255, 255, 255, 0.82);
|
||||
--card-media-bg: #ffffff;
|
||||
--elevated-surface: rgba(255, 255, 255, 0.96);
|
||||
--toast-bg: rgba(255, 255, 255, 0.94);
|
||||
--icon-bg: color-mix(in srgb, var(--primary) 14%, #ffffff);
|
||||
--icon-bg-end: color-mix(in srgb, var(--primary) 8%, #f1f5f9);
|
||||
}
|
||||
@@ -55,6 +56,7 @@ html[data-theme='dark'] {
|
||||
--surface: rgba(55, 59, 68, 0.95);
|
||||
--card-media-bg: #32363f;
|
||||
--elevated-surface: #3a3e48;
|
||||
--toast-bg: rgba(58, 62, 72, 0.96);
|
||||
|
||||
--card-hover-shadow: 0 16px 48px rgba(0, 0, 0, 0.45);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useCallback, useEffect, useState } from 'react'
|
||||
import { MapPin, Pencil, Plus, Trash2 } from 'lucide-react'
|
||||
import { Breadcrumbs, useToast } from '@meshkee/dashboard-ui'
|
||||
import { AddressFormModal } from '../components/AddressFormModal'
|
||||
import { EmptyState } from '../components/EmptyState'
|
||||
import { Tooltip } from '../components/Tooltip'
|
||||
import { useT } from '../i18n/useT'
|
||||
import { ApiError, isAbortError } from '../lib/api'
|
||||
@@ -125,10 +126,12 @@ export function AddressesPage() {
|
||||
{loading && <p className={styles.status}>{t('addresses.loading')}</p>}
|
||||
|
||||
{!loading && addresses.length === 0 && (
|
||||
<div className={styles.empty}>
|
||||
<MapPin size={28} />
|
||||
<p>{t('addresses.empty')}</p>
|
||||
</div>
|
||||
<EmptyState
|
||||
icon={MapPin}
|
||||
message={t('addresses.empty')}
|
||||
actionLabel={t('addresses.add')}
|
||||
onAction={openCreateModal}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!loading && addresses.length > 0 && (
|
||||
|
||||
@@ -2,6 +2,7 @@ import { PageTitle } from '../components/PageTitle'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Heart } from 'lucide-react'
|
||||
import { Breadcrumbs, Pagination, useToast } from '@meshkee/dashboard-ui'
|
||||
import { EmptyState } from '../components/EmptyState'
|
||||
import { FavoriteStoreItemCard } from '../components/FavoriteStoreItemCard'
|
||||
import { useT } from '../i18n/useT'
|
||||
import { ApiError, isAbortError } from '../lib/api'
|
||||
@@ -97,10 +98,7 @@ export function FavoritesPage() {
|
||||
{loading && <p className={styles.status}>{t('favorites.loading')}</p>}
|
||||
|
||||
{!loading && data?.items.length === 0 && (
|
||||
<div className={styles.empty}>
|
||||
<Heart size={32} />
|
||||
<p>{t('favorites.empty')}</p>
|
||||
</div>
|
||||
<EmptyState icon={Heart} message={t('favorites.empty')} layout="page" />
|
||||
)}
|
||||
|
||||
{!!data?.items.length && (
|
||||
|
||||
@@ -71,10 +71,10 @@
|
||||
}
|
||||
|
||||
.domain {
|
||||
font-size: 20px;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
line-height: 1.25;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.appName {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useEffect, useState } from 'react'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import { Package, Plus } from 'lucide-react'
|
||||
import { Breadcrumbs, Pagination, useToast } from '@meshkee/dashboard-ui'
|
||||
import { EmptyState } from '../components/EmptyState'
|
||||
import { UserProductCard } from '../components/UserProductCard'
|
||||
import { useT } from '../i18n/useT'
|
||||
import { ApiError, isAbortError } from '../lib/api'
|
||||
@@ -141,13 +142,12 @@ export function MyProductsPage() {
|
||||
{loading ? <p className={styles.status}>{t('myProducts.loading')}</p> : null}
|
||||
|
||||
{!loading && !error && products.length === 0 ? (
|
||||
<div className={styles.empty}>
|
||||
<Package size={32} />
|
||||
<p>{t('myProducts.empty')}</p>
|
||||
<Link to="/my-products/new" className={styles.emptyLink}>
|
||||
{t('myProducts.add')}
|
||||
</Link>
|
||||
</div>
|
||||
<EmptyState
|
||||
icon={Package}
|
||||
message={t('myProducts.empty')}
|
||||
actionLabel={t('myProducts.add')}
|
||||
actionTo="/my-products/new"
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{!loading && products.length > 0 ? (
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { PageTitle } from '../components/PageTitle'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { Breadcrumbs, Pagination, useLocale } from '@meshkee/dashboard-ui'
|
||||
import { EmptyTableRow } from '../components/EmptyState'
|
||||
import { OrderItemsModal } from '../components/OrderItemsModal'
|
||||
import { OrderRow } from '../components/OrderRow'
|
||||
import { useT } from '../i18n/useT'
|
||||
@@ -131,11 +132,7 @@ export function OrdersPage() {
|
||||
)}
|
||||
|
||||
{!loading && data?.items.length === 0 && (
|
||||
<tr>
|
||||
<td className={styles.td} colSpan={COLUMN_COUNT}>
|
||||
{t('orders.empty')}
|
||||
</td>
|
||||
</tr>
|
||||
<EmptyTableRow colSpan={COLUMN_COUNT} message={t('orders.empty')} />
|
||||
)}
|
||||
|
||||
{!loading &&
|
||||
|
||||
Reference in New Issue
Block a user