mirror of
https://git.meshkee.com/Meshkee/dashboards.git
synced 2026-08-11 22:30:58 +04:30
Polish dashboard page chrome and user access UI.
Rename Customers to Users with FA/EN titles, compact headers and filter bars across apps, and refine manager role copy plus a customer-header link to the business dashboard. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
beec3035e5
commit
6c52ce1247
@@ -122,6 +122,28 @@
|
||||
|
||||
.profileWrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.businessDashBtn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
color: var(--primary);
|
||||
background: rgba(var(--primary-rgb) / 0.1);
|
||||
border: 1px solid rgba(var(--primary-rgb) / 0.18);
|
||||
transition: background 0.15s, transform 0.15s, box-shadow 0.15s;
|
||||
}
|
||||
|
||||
.businessDashBtn:hover {
|
||||
background: rgba(var(--primary-rgb) / 0.16);
|
||||
box-shadow: 0 4px 12px rgba(var(--primary-rgb) / 0.18);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.profile {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import { Menu, Bell, MessageSquare, ChevronDown, User, KeyRound, LogOut } from 'lucide-react'
|
||||
import { Menu, Bell, MessageSquare, ChevronDown, User, KeyRound, LogOut, LayoutDashboard } from 'lucide-react'
|
||||
import { LanguageSelect, PasswordResetModal, useLocale } from '@meshkee/dashboard-ui'
|
||||
import { useAuth } from '../context/AuthContext'
|
||||
import { useTenantBranding } from '../context/TenantBrandingContext'
|
||||
import { useT } from '../i18n/useT'
|
||||
import { getActiveBusinessId } from '../lib/businessContext'
|
||||
import { getBusinessDashboardUrl } from '../lib/config'
|
||||
import { changePassword } from '../services/authService'
|
||||
import meshkeeLogo from '../assets/meshkee-logo.png'
|
||||
import styles from './Header.module.css'
|
||||
@@ -57,6 +59,17 @@ export function Header({ hideMenu = false, showBrand = false }: HeaderProps) {
|
||||
? (businessName && businessName !== brandTitle ? businessName : '')
|
||||
: (businessNameEn && businessNameEn !== brandTitle ? businessNameEn : '')
|
||||
|
||||
const activeBusinessId = getActiveBusinessId()
|
||||
const canOpenBusinessDashboard = Boolean(
|
||||
user?.isSuperAdmin ||
|
||||
user?.roles.includes('super_admin') ||
|
||||
user?.businesses.some(
|
||||
(business) =>
|
||||
String(business.id) === String(activeBusinessId) &&
|
||||
(business.isOwner || Boolean(business.teamRole)),
|
||||
),
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!menuOpen) return
|
||||
|
||||
@@ -128,6 +141,18 @@ export function Header({ hideMenu = false, showBrand = false }: HeaderProps) {
|
||||
</button>
|
||||
|
||||
<div className={styles.profileWrap} ref={menuRef}>
|
||||
{canOpenBusinessDashboard ? (
|
||||
<a
|
||||
className={styles.businessDashBtn}
|
||||
href={getBusinessDashboardUrl()}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={t('header.openBusinessDashboard')}
|
||||
title={t('header.openBusinessDashboard')}
|
||||
>
|
||||
<LayoutDashboard size={18} />
|
||||
</a>
|
||||
) : null}
|
||||
<button
|
||||
type="button"
|
||||
className={`${styles.profile} ${menuOpen ? styles.profileOpen : ''}`}
|
||||
|
||||
@@ -8,19 +8,44 @@
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
margin-bottom: 36px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.pageTitle {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
gap: 0.15em 0.4em;
|
||||
}
|
||||
|
||||
.pageTitleEn {
|
||||
font-size: 0.52em;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.22em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
font-family: var(--font-en), var(--font-ui);
|
||||
}
|
||||
|
||||
.pageTitleEn::before {
|
||||
content: '•';
|
||||
margin-inline-end: 0.4em;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.pageSubtitle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pageSubtitleContent {
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.dateBadge {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useLocale } from '@meshkee/dashboard-ui'
|
||||
import type { ReactNode } from 'react'
|
||||
import pageStyles from './PageContent.module.css'
|
||||
|
||||
interface PageTitleProps {
|
||||
children: ReactNode
|
||||
/** English accent shown after a bullet in FA locale (e.g. USERS). */
|
||||
en?: string
|
||||
}
|
||||
|
||||
export function PageTitle({ children, en }: PageTitleProps) {
|
||||
const { locale } = useLocale()
|
||||
return (
|
||||
<h2 className={pageStyles.pageTitle}>
|
||||
{children}
|
||||
{en && locale === 'fa' ? <span className={pageStyles.pageTitleEn}>{en}</span> : null}
|
||||
</h2>
|
||||
)
|
||||
}
|
||||
@@ -21,6 +21,7 @@ const en = {
|
||||
'header.notifications': 'Notifications',
|
||||
'header.changePassword': 'Change password',
|
||||
'header.myProfile': 'My Profile',
|
||||
'header.openBusinessDashboard': 'Open business dashboard',
|
||||
|
||||
'home.welcome': 'Welcome, dear {name}.',
|
||||
'home.welcomeFallback': 'there',
|
||||
@@ -406,6 +407,7 @@ const fa: Record<MessageKey, string> = {
|
||||
'header.notifications': 'اعلانها',
|
||||
'header.changePassword': 'تغییر رمز عبور',
|
||||
'header.myProfile': 'پروفایل من',
|
||||
'header.openBusinessDashboard': 'رفتن به داشبورد کسبوکار',
|
||||
|
||||
'home.welcome': '{name} عزیز، خوش آمدی.',
|
||||
'home.welcomeFallback': 'کاربر',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
CUSTOMER_SUBDOMAIN_PREFIX,
|
||||
getBaseDomainFromHost,
|
||||
getBusinessDashboardHost,
|
||||
getCustomerDashboardHost,
|
||||
isCustomerDashboardHost,
|
||||
} from '@meshkee/dashboard-core'
|
||||
@@ -31,3 +32,12 @@ export function isAllowedCustomerHost(hostname = window.location.hostname): bool
|
||||
export function getTenantDomain(): string {
|
||||
return getBaseBusinessDomain()
|
||||
}
|
||||
|
||||
/** Open the matching business dashboard (local: port 5173). */
|
||||
export function getBusinessDashboardUrl(): string {
|
||||
const host = getBusinessDashboardHost(getBaseBusinessDomain())
|
||||
const { protocol, port } = window.location
|
||||
const businessPort = port === '5175' ? '5173' : port
|
||||
const portSuffix = businessPort ? `:${businessPort}` : ''
|
||||
return `${protocol}//${host}${portSuffix}/`
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PageTitle } from '../components/PageTitle'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { MapPin, Pencil, Plus, Trash2 } from 'lucide-react'
|
||||
import { Breadcrumbs, useToast } from '@meshkee/dashboard-ui'
|
||||
@@ -107,7 +108,7 @@ export function AddressesPage() {
|
||||
|
||||
<div className={pageStyles.pageHeader}>
|
||||
<div>
|
||||
<h2 className={pageStyles.pageTitle}>{t('addresses.title')}</h2>
|
||||
<PageTitle en="ADDRESSES">{t('addresses.title')}</PageTitle>
|
||||
<p className={pageStyles.pageSubtitle}>{t('addresses.subtitle')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PageTitle } from '../components/PageTitle'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Heart } from 'lucide-react'
|
||||
import { Breadcrumbs, Pagination, useToast } from '@meshkee/dashboard-ui'
|
||||
@@ -82,7 +83,7 @@ export function FavoritesPage() {
|
||||
|
||||
<div className={pageStyles.pageHeader}>
|
||||
<div>
|
||||
<h2 className={pageStyles.pageTitle}>{t('favorites.title')}</h2>
|
||||
<PageTitle en="FAVORITES">{t('favorites.title')}</PageTitle>
|
||||
<p className={pageStyles.pageSubtitle}>{t('favorites.subtitle')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PageTitle } from '../components/PageTitle'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { Link, useNavigate, useParams } from 'react-router-dom'
|
||||
import { ImageOff, MapPin, Pencil } from 'lucide-react'
|
||||
@@ -172,8 +173,8 @@ export function MyProductDetailsPage() {
|
||||
|
||||
<div className={styles.headerRow}>
|
||||
<div>
|
||||
<h2 className={pageStyles.pageTitle}>{t('myProducts.detailsTitle')}</h2>
|
||||
<p className={pageStyles.pageSubtitle}>{t('myProducts.detailsSubtitle')}</p>
|
||||
<PageTitle en="PRODUCT">{t('myProducts.detailsTitle')}</PageTitle>
|
||||
<p className={pageStyles.pageSubtitleContent}>{t('myProducts.detailsSubtitle')}</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PageTitle } from '../components/PageTitle'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import { Package, Plus } from 'lucide-react'
|
||||
@@ -126,7 +127,7 @@ export function MyProductsPage() {
|
||||
|
||||
<div className={pageStyles.pageHeader}>
|
||||
<div>
|
||||
<h2 className={pageStyles.pageTitle}>{t('myProducts.title')}</h2>
|
||||
<PageTitle en="MY PRODUCTS">{t('myProducts.title')}</PageTitle>
|
||||
<p className={pageStyles.pageSubtitle}>{t('myProducts.subtitle')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PageTitle } from '../components/PageTitle'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { Breadcrumbs, Pagination, useLocale } from '@meshkee/dashboard-ui'
|
||||
import { OrderItemsModal } from '../components/OrderItemsModal'
|
||||
@@ -71,7 +72,7 @@ export function OrdersPage() {
|
||||
|
||||
<div className={pageStyles.pageHeader}>
|
||||
<div>
|
||||
<h2 className={pageStyles.pageTitle}>{t('orders.title')}</h2>
|
||||
<PageTitle en="ORDERS">{t('orders.title')}</PageTitle>
|
||||
<p className={pageStyles.pageSubtitle}>{t('orders.subtitle')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PageTitle } from '../components/PageTitle'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Breadcrumbs, useToast } from '@meshkee/dashboard-ui'
|
||||
import { useAuth } from '../context/AuthContext'
|
||||
@@ -81,7 +82,7 @@ export function ProfilePage() {
|
||||
|
||||
<div className={pageStyles.pageHeader}>
|
||||
<div>
|
||||
<h2 className={pageStyles.pageTitle}>{t('profile.title')}</h2>
|
||||
<PageTitle en="PROFILE">{t('profile.title')}</PageTitle>
|
||||
<p className={pageStyles.pageSubtitle}>{t('profile.subtitle')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user