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:
co-authored by
Cursor
parent
66004a0fba
commit
672091d1f5
@@ -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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user