Polish business FA layout and wire special-group keys plus SSL sync.

RTL carousels/FABs, special key+title fields, slider gallery fixes, and dashboard SSL sync agent for super-admin.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-03 00:06:44 +03:30
co-authored by Cursor
parent 66004a0fba
commit 672091d1f5
113 changed files with 4154 additions and 1451 deletions
+32 -14
View File
@@ -1,7 +1,9 @@
import { Link } from 'react-router-dom'
import { ChevronUp, Pencil, MessageSquare, Trash2 } from 'lucide-react'
import { useLocale } from '@meshkee/dashboard-ui'
import type { Portfolio } from '../types/portfolio'
import { formatPortfolioCardDate } from '../services/portfolioService'
import { useT } from '../i18n/useT'
import { textLocaleAttrs } from '../utils/textLocale'
import { Tooltip } from './Tooltip'
import cardStyles from './PortfolioCard.module.css'
@@ -28,13 +30,21 @@ export function PortfolioCard({
onComments,
onRemove,
}: PortfolioCardProps) {
const publishDate = formatPortfolioCardDate(portfolio.publishedAt ?? portfolio.createdAt)
const t = useT()
const { locale } = useLocale()
const isFa = locale === 'fa'
const publishDate = formatPortfolioCardDate(
portfolio.publishedAt ?? portfolio.createdAt,
isFa ? 'fa' : 'en',
)
const titleFa = (portfolio.titleFa || portfolio.title || '').trim()
const titleEn = (portfolio.titleEn || '').trim()
const showBothTitles = Boolean(titleFa && titleEn)
const showAbstract = !showBothTitles
const abstractLocale = textLocaleAttrs(portfolio.abstract)
const hasAbstract = Boolean(portfolio.abstract?.trim())
// In FA UI, keep cards RTL even when EN titles/abstracts are shown.
const abstractDir = isFa ? 'rtl' : abstractLocale.dir
return (
<article className={cardStyles.card}>
@@ -52,14 +62,14 @@ export function PortfolioCard({
)}
</div>
<div className={cardStyles.body}>
<div className={cardStyles.body} dir={isFa ? 'rtl' : 'ltr'}>
{titleFa ? (
<h3 className={`${cardStyles.titleFa} faText`} lang="fa" dir="rtl">
{titleFa}
</h3>
) : null}
{titleEn ? (
<p className={cardStyles.titleEn} lang="en" dir="ltr">
<p className={cardStyles.titleEn} lang="en" dir={isFa ? 'rtl' : 'ltr'}>
{titleEn}
</p>
) : null}
@@ -70,13 +80,17 @@ export function PortfolioCard({
.filter(Boolean)
.join(' ')}
lang={abstractLocale.lang}
dir={abstractLocale.dir}
dir={abstractDir}
>
{portfolio.abstract}
</p>
) : (
<p className={cardStyles.abstract} lang="en" dir="ltr">
No summary yet.
<p
className={cardStyles.abstract}
lang={isFa ? 'fa' : 'en'}
dir={isFa ? 'rtl' : 'ltr'}
>
{t('portfolio.card.noSummary')}
</p>
)
) : null}
@@ -93,27 +107,31 @@ export function PortfolioCard({
</Link>
<div className={controlStyles.controls}>
<Tooltip label="Move up">
<Tooltip label={t('portfolio.card.moveUp')}>
<button
type="button"
onClick={() => onMoveUp(portfolio.id)}
disabled={!canMoveUp || isMovingUp}
aria-label="Move up"
aria-label={t('portfolio.card.moveUp')}
>
<ChevronUp size={16} />
</button>
</Tooltip>
<Tooltip label="Edit portfolio">
<button type="button" onClick={() => onEdit(portfolio.id)} aria-label="Edit">
<Tooltip label={t('portfolio.card.edit')}>
<button
type="button"
onClick={() => onEdit(portfolio.id)}
aria-label={t('portfolio.card.edit')}
>
<Pencil size={16} />
</button>
</Tooltip>
<Tooltip label="View comments">
<Tooltip label={t('portfolio.card.comments')}>
<button
type="button"
className={controlStyles.iconBtn}
onClick={() => onComments(portfolio.id)}
aria-label={`Comments (${commentCount})`}
aria-label={`${t('portfolio.card.comments')} (${commentCount})`}
>
<MessageSquare size={16} />
{commentCount > 0 && (
@@ -121,12 +139,12 @@ export function PortfolioCard({
)}
</button>
</Tooltip>
<Tooltip label="Remove portfolio">
<Tooltip label={t('portfolio.card.remove')}>
<button
type="button"
className={controlStyles.danger}
onClick={() => onRemove(portfolio.id)}
aria-label="Remove"
aria-label={t('portfolio.card.remove')}
>
<Trash2 size={16} />
</button>