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
@@ -1,9 +1,11 @@
import { useEffect, useMemo, useState } from 'react'
import { Link, useParams } from 'react-router-dom'
import { CalendarDays } from 'lucide-react'
import { useLocale } from '@meshkee/dashboard-ui'
import { Breadcrumbs } from '../components/Breadcrumbs'
import { ImageLightbox } from '../components/ImageLightbox'
import { PortfolioCommentsSection } from '../components/PortfolioCommentsSection'
import { useT } from '../i18n/useT'
import { ApiError } from '../lib/api'
import { formatPortfolioDate, getPortfolioDetail } from '../services/portfolioService'
import type { PortfolioDetail } from '../types/portfolio'
@@ -11,6 +13,8 @@ import pageStyles from '../components/PageContent.module.css'
import styles from './PortfolioDetailsPage.module.css'
export function PortfolioDetailsPage() {
const t = useT()
const { locale } = useLocale()
const { id } = useParams()
const [portfolio, setPortfolio] = useState<PortfolioDetail | null>(null)
const [isLoading, setIsLoading] = useState(true)
@@ -35,7 +39,7 @@ export function PortfolioDetailsPage() {
if (err instanceof ApiError) {
setError(err.message)
} else {
setError('Unable to load portfolio.')
setError(t('portfolio.details.errorLoad'))
}
setPortfolio(null)
} finally {
@@ -60,7 +64,7 @@ export function PortfolioDetailsPage() {
if (isLoading) {
return (
<main className={pageStyles.content}>
<p className={styles.status}>Loading portfolio...</p>
<p className={styles.status}>{t('portfolio.details.loading')}</p>
</main>
)
}
@@ -68,15 +72,18 @@ export function PortfolioDetailsPage() {
if (error || !portfolio || !id) {
return (
<main className={pageStyles.content}>
<p className={styles.error}>{error || 'Portfolio not found.'}</p>
<p className={styles.error}>{error || t('portfolio.details.notFound')}</p>
<Link to="/portfolios/list" className={styles.backLink}>
Back to My Portfolios
{t('portfolio.details.back')}
</Link>
</main>
)
}
const displayDate = formatPortfolioDate(portfolio.publishedAt ?? portfolio.createdAt)
const displayDate = formatPortfolioDate(
portfolio.publishedAt ?? portfolio.createdAt,
locale === 'fa' ? 'fa' : 'en',
)
return (
<>
@@ -145,16 +152,16 @@ export function PortfolioDetailsPage() {
dangerouslySetInnerHTML={{ __html: portfolio.mainTextHtml }}
/>
) : (
<p className={styles.content}>No content yet.</p>
<p className={styles.content}>{t('portfolio.details.noContent')}</p>
)}
</div>
</article>
</div>
{galleryImages.length > 0 && (
<section className={styles.galleryBand} aria-label="Portfolio gallery">
<section className={styles.galleryBand} aria-label={t('portfolio.details.gallery')}>
<div className={styles.galleryInner}>
<h3 className={styles.galleryTitle}>Gallery</h3>
<h3 className={styles.galleryTitle}>{t('portfolio.details.gallery')}</h3>
<div className={styles.galleryGrid}>
{galleryImages.map((src, index) => (
<button
@@ -162,7 +169,7 @@ export function PortfolioDetailsPage() {
type="button"
className={styles.galleryItem}
onClick={() => openLightbox(index)}
aria-label={`View gallery image ${index + 1}`}
aria-label={t('portfolio.details.galleryImage', { index: index + 1 })}
>
<img src={src} alt={`${portfolio.title} gallery ${index + 1}`} loading="lazy" />
</button>