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
+21 -11
View File
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react'
import Cropper, { type Area } from 'react-easy-crop'
import { ImagePlus, X } from 'lucide-react'
import { getCroppedImage } from '../utils/cropImage'
import { useT } from '../i18n/useT'
import styles from './ImageCropper.module.css'
interface ImageCropperProps {
@@ -22,10 +23,14 @@ export function ImageCropper({
aspect = 1,
outputFormat = 'jpeg',
accept = 'image/*',
uploadLabel = 'Upload thumbnail image',
hint = 'Click to select, then crop',
changeLabel = 'Change thumbnail',
uploadLabel,
hint,
changeLabel,
}: ImageCropperProps) {
const t = useT()
const resolvedUploadLabel = uploadLabel ?? t('products.form.thumbnailUpload')
const resolvedHint = hint ?? t('products.form.thumbnailHint')
const resolvedChangeLabel = changeLabel ?? t('products.form.thumbnailChange')
const [imageSrc, setImageSrc] = useState<string | null>(null)
const [crop, setCrop] = useState({ x: 0, y: 0 })
const [zoom, setZoom] = useState(1)
@@ -93,8 +98,13 @@ export function ImageCropper({
<div className={styles.wrapper}>
{value && !imageSrc && (
<div className={styles.preview}>
<img src={value} alt="Thumbnail preview" className={styles.previewImgNatural} />
<button type="button" className={styles.removeBtn} onClick={removeThumbnail} aria-label="Remove thumbnail">
<img src={value} alt={resolvedUploadLabel} className={styles.previewImgNatural} />
<button
type="button"
className={styles.removeBtn}
onClick={removeThumbnail}
aria-label={t('products.form.thumbnailRemove')}
>
<X size={16} />
</button>
</div>
@@ -103,8 +113,8 @@ export function ImageCropper({
{!value && !imageSrc && (
<label className={styles.uploadZone} style={frameStyle}>
<ImagePlus size={28} />
<span>{uploadLabel}</span>
<span className={styles.hint}>{hint}</span>
<span>{resolvedUploadLabel}</span>
<span className={styles.hint}>{resolvedHint}</span>
<input type="file" accept={accept} onChange={handleFile} hidden />
</label>
)}
@@ -125,7 +135,7 @@ export function ImageCropper({
</div>
<div className={styles.cropControls}>
<label className={styles.zoomLabel}>
Zoom
{t('products.form.zoom')}
<input
type="range"
min={1}
@@ -137,7 +147,7 @@ export function ImageCropper({
</label>
<div className={styles.cropActions}>
<button type="button" className={styles.cancelBtn} onClick={cancelCrop}>
Cancel
{t('products.form.cancel')}
</button>
<button
type="button"
@@ -145,7 +155,7 @@ export function ImageCropper({
onClick={() => void applyCrop()}
disabled={!croppedArea}
>
Apply Crop
{t('products.form.applyCrop')}
</button>
</div>
</div>
@@ -154,7 +164,7 @@ export function ImageCropper({
{value && !imageSrc && (
<label className={styles.changeBtn}>
{changeLabel}
{resolvedChangeLabel}
<input type="file" accept={accept} onChange={handleFile} hidden />
</label>
)}