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
+14 -13
View File
@@ -7,6 +7,7 @@ import { PortfolioCommentsModal } from '../components/PortfolioCommentsModal'
import { ConfirmDeleteModal } from '../components/ConfirmDeleteModal'
import { Pagination } from '../components/Pagination'
import { useToast } from '../context/ToastContext'
import { useT } from '../i18n/useT'
import { ApiError } from '../lib/api'
import {
PORTFOLIOS_PER_PAGE,
@@ -21,6 +22,7 @@ import styles from './BlogPage.module.css'
export function PortfolioListPage() {
const navigate = useNavigate()
const { showToast } = useToast()
const t = useT()
const [portfolios, setPortfolios] = useState<Portfolio[]>([])
const [totalPortfolios, setTotalPortfolios] = useState(0)
const [currentPage, setCurrentPage] = useState(1)
@@ -58,7 +60,7 @@ export function PortfolioListPage() {
if (err instanceof ApiError) {
setError(err.message)
} else {
setError('Unable to load portfolios.')
setError(t('portfolio.list.errorLoad'))
}
} finally {
setIsLoading(false)
@@ -94,7 +96,6 @@ export function PortfolioListPage() {
try {
if (index === 0) {
// First on this page, but not global first — bump above the current band.
await updatePortfolio(current.id, { sortOrder: current.sortOrder - 1 })
} else {
const previous = portfolios[index - 1]
@@ -107,13 +108,13 @@ export function PortfolioListPage() {
])
}
}
showToast('Portfolio moved up.', 'success')
showToast(t('portfolio.list.toast.movedUp'), 'success')
await loadPortfolios(currentPage)
} catch (err) {
if (err instanceof ApiError) {
setError(err.message)
} else {
setError('Unable to move portfolio.')
setError(t('portfolio.list.errorMove'))
}
} finally {
setMovingUpId(null)
@@ -128,7 +129,7 @@ export function PortfolioListPage() {
try {
await deletePortfolio(deleteTarget.id)
showToast('Portfolio removed.', 'success')
showToast(t('portfolio.list.toast.removed'), 'success')
const nextTotal = totalPortfolios - 1
const nextTotalPages = Math.max(1, Math.ceil(nextTotal / PORTFOLIOS_PER_PAGE))
const nextPage = currentPage > nextTotalPages ? nextTotalPages : currentPage
@@ -139,7 +140,7 @@ export function PortfolioListPage() {
if (err instanceof ApiError) {
setError(err.message)
} else {
setError('Unable to delete portfolio.')
setError(t('portfolio.list.errorDelete'))
}
} finally {
setIsDeleting(false)
@@ -171,9 +172,9 @@ export function PortfolioListPage() {
<div className={pageStyles.pageHeader}>
<div>
<h2 className={pageStyles.pageTitle}>My Portfolios</h2>
<h2 className={pageStyles.pageTitle}>{t('title.myPortfolios')}</h2>
<p className={pageStyles.pageSubtitle}>
{totalPortfolios} items · View, edit and manage your portfolio content.
{t('portfolio.list.subtitle', { count: totalPortfolios })}
</p>
</div>
</div>
@@ -185,9 +186,9 @@ export function PortfolioListPage() {
)}
{isLoading ? (
<p className={styles.empty}>Loading portfolios...</p>
<p className={styles.empty}>{t('portfolio.list.loading')}</p>
) : portfolios.length === 0 ? (
<p className={styles.empty}>No portfolio items found.</p>
<p className={styles.empty}>{t('portfolio.list.empty')}</p>
) : (
<>
<div className={pageStyles.gridCols4}>
@@ -218,17 +219,17 @@ export function PortfolioListPage() {
type="button"
className={styles.addFab}
onClick={() => navigate('/portfolios/new')}
aria-label="Add new portfolio"
aria-label={t('portfolio.list.addNew')}
>
<Plus size={26} strokeWidth={2.5} />
</button>
<ConfirmDeleteModal
open={!!deleteTarget}
title="Delete Portfolio"
title={t('portfolio.list.deleteTitle')}
message={
deleteTarget
? `Are you sure you want to delete "${deleteTarget.title}"? This action cannot be undone.`
? t('portfolio.list.deleteMessage', { name: deleteTarget.title })
: ''
}
onConfirm={confirmDelete}