mirror of
https://git.meshkee.com/Meshkee/dashboards.git
synced 2026-08-11 22:30:58 +04:30
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
@@ -66,14 +66,14 @@ export function AddNewProductPage() {
|
||||
if (err instanceof ApiError) {
|
||||
setError(err.message)
|
||||
} else {
|
||||
setError('Unable to load form options.')
|
||||
setError(t('products.form.errorOptions'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loadFormOptions()
|
||||
return () => controller.abort()
|
||||
}, [])
|
||||
}, [t])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEdit || !id) return
|
||||
@@ -157,7 +157,7 @@ export function AddNewProductPage() {
|
||||
} else if (err instanceof Error) {
|
||||
setError(err.message)
|
||||
} else {
|
||||
setError('Unable to save product.')
|
||||
setError(t('products.form.errorSave'))
|
||||
}
|
||||
} finally {
|
||||
setIsSubmitting(false)
|
||||
@@ -202,43 +202,44 @@ export function AddNewProductPage() {
|
||||
<form className={styles.form} onSubmit={handleSubmit}>
|
||||
<div className={styles.formGrid}>
|
||||
<div className={`${styles.field} ${styles.col2} ${styles.thumbnailField}`}>
|
||||
<label>Thumbnail Image</label>
|
||||
<label>{t('products.form.thumbnail')}</label>
|
||||
<ImageCropper value={thumbnail} onChange={setThumbnail} />
|
||||
</div>
|
||||
|
||||
<div className={styles.col10}>
|
||||
<div className={styles.topFields}>
|
||||
<div className={`${styles.field} ${styles.fieldCategory}`}>
|
||||
<label>Category</label>
|
||||
<label>{t('products.form.category')}</label>
|
||||
<SearchableSelect
|
||||
options={categoryOptions}
|
||||
value={categoryId}
|
||||
onChange={setCategoryId}
|
||||
placeholder="Search and select category..."
|
||||
placeholder={t('products.form.categoryPlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={`${styles.field} ${styles.fieldBrand}`}>
|
||||
<label>
|
||||
Brand <span className={styles.optional}>(optional)</span>
|
||||
{t('products.form.brand')}{' '}
|
||||
<span className={styles.optional}>{t('products.form.optional')}</span>
|
||||
</label>
|
||||
<SearchableSelect
|
||||
options={brandOptions}
|
||||
value={brandId}
|
||||
onChange={setBrandId}
|
||||
placeholder="Search and select brand..."
|
||||
placeholder={t('products.form.brandPlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={`${styles.field} ${styles.fieldHalf}`}>
|
||||
<label htmlFor="nameFa">Name (FA)</label>
|
||||
<label htmlFor="nameFa">{t('products.form.nameFa')}</label>
|
||||
<input
|
||||
id="nameFa"
|
||||
name="nameFa"
|
||||
type="text"
|
||||
dir="rtl"
|
||||
className="faText"
|
||||
placeholder="نام محصول"
|
||||
placeholder={t('products.form.nameFaPlaceholder')}
|
||||
value={nameFa}
|
||||
onChange={(e) => setNameFa(e.target.value)}
|
||||
required
|
||||
@@ -247,13 +248,13 @@ export function AddNewProductPage() {
|
||||
</div>
|
||||
|
||||
<div className={`${styles.field} ${styles.fieldHalf}`}>
|
||||
<label htmlFor="nameEn">Name (EN)</label>
|
||||
<label htmlFor="nameEn">{t('products.form.nameEn')}</label>
|
||||
<input
|
||||
id="nameEn"
|
||||
name="nameEn"
|
||||
type="text"
|
||||
dir="ltr"
|
||||
placeholder="Product name"
|
||||
placeholder={t('products.form.nameEnPlaceholder')}
|
||||
value={nameEn}
|
||||
onChange={(e) => setNameEn(e.target.value)}
|
||||
required
|
||||
@@ -262,12 +263,12 @@ export function AddNewProductPage() {
|
||||
</div>
|
||||
|
||||
<div className={`${styles.field} ${styles.fieldFull} ${styles.summaryField}`}>
|
||||
<label htmlFor="summary">Summary</label>
|
||||
<label htmlFor="summary">{t('products.form.summary')}</label>
|
||||
<textarea
|
||||
id="summary"
|
||||
name="summary"
|
||||
rows={4}
|
||||
placeholder="Short summary shown in product listings"
|
||||
placeholder={t('products.form.summaryPlaceholder')}
|
||||
value={summary}
|
||||
onChange={(e) => setSummary(e.target.value)}
|
||||
required
|
||||
@@ -278,31 +279,39 @@ export function AddNewProductPage() {
|
||||
</div>
|
||||
|
||||
<div className={`${styles.field} ${styles.col12}`}>
|
||||
<label>Description</label>
|
||||
<label>{t('products.form.description')}</label>
|
||||
<RichTextEditor
|
||||
value={description}
|
||||
onChange={setDescription}
|
||||
placeholder="Full product description with formatting..."
|
||||
placeholder={t('products.form.descriptionPlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={`${styles.field} ${styles.col12}`}>
|
||||
<label>Product Images</label>
|
||||
<label>{t('products.form.images')}</label>
|
||||
<ImageUploader images={images} onChange={setImages} />
|
||||
</div>
|
||||
|
||||
<div className={`${styles.field} ${styles.col12}`}>
|
||||
<label>Tags</label>
|
||||
<TagInput tags={tags} onChange={setTags} />
|
||||
<label>{t('products.form.tags')}</label>
|
||||
<TagInput
|
||||
tags={tags}
|
||||
onChange={setTags}
|
||||
placeholder={t('products.form.tagsPlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.actions}>
|
||||
<Link to="/products/list" className={styles.cancelBtn}>
|
||||
Cancel
|
||||
{t('products.form.cancel')}
|
||||
</Link>
|
||||
<button type="submit" className={styles.submitBtn} disabled={isSubmitting}>
|
||||
{isSubmitting ? 'Saving...' : isEdit ? 'Update Product' : 'Save Product'}
|
||||
{isSubmitting
|
||||
? t('products.form.saving')
|
||||
: isEdit
|
||||
? t('products.form.update')
|
||||
: t('products.form.save')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user