Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
99 lines
2.8 KiB
TypeScript
99 lines
2.8 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import Image from 'next/image'
|
|
|
|
import { addToast } from '@/lib/toast'
|
|
import Button from '@/components/formElements/Button'
|
|
import TrashIcon from '@/components/icons/TrashIcon'
|
|
import ImageUploadIcon from '@/components/icons/ImageUploadIcon'
|
|
import axiosInstance from '@/config/axios'
|
|
import { parseUploadedFile } from '@/helpers'
|
|
import { prepareImageForUpload, SAFE_IMAGE_ACCEPT } from '@/lib/fileValidation'
|
|
|
|
interface ArticleFeaturedImageUploaderProps {
|
|
value: string
|
|
onChange: (url: string) => void
|
|
}
|
|
|
|
const ArticleFeaturedImageUploader = ({ value, onChange }: ArticleFeaturedImageUploaderProps) => {
|
|
const [uploading, setUploading] = useState(false)
|
|
|
|
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const file = e.target.files?.[0]
|
|
|
|
e.target.value = ''
|
|
if (!file) return
|
|
|
|
const prepared = await prepareImageForUpload(file)
|
|
|
|
if (typeof prepared === 'string') {
|
|
addToast({ title: prepared, color: 'danger' })
|
|
|
|
return
|
|
}
|
|
|
|
setUploading(true)
|
|
|
|
try {
|
|
const data = new FormData()
|
|
|
|
data.append('file', prepared)
|
|
|
|
const result = await axiosInstance.post('/uploads', data)
|
|
const uploaded = parseUploadedFile(result.data)
|
|
|
|
if (!uploaded) throw new Error('آپلود ناموفق بود')
|
|
|
|
onChange(uploaded.url)
|
|
} catch {
|
|
addToast({ title: 'آپلود تصویر ناموفق بود', color: 'danger' })
|
|
} finally {
|
|
setUploading(false)
|
|
}
|
|
}
|
|
|
|
if (value) {
|
|
return (
|
|
<div className="relative w-full max-w-xs overflow-hidden rounded-2xl border border-secondary-40">
|
|
<Image
|
|
unoptimized
|
|
alt=""
|
|
className="h-40 w-full object-cover"
|
|
height={160}
|
|
src={value}
|
|
width={320}
|
|
/>
|
|
<Button
|
|
iconOnly
|
|
aria-label="حذف تصویر شاخص"
|
|
className="absolute top-2 end-2 rounded-full bg-white/90"
|
|
size="sm"
|
|
variant="light"
|
|
onClick={() => {
|
|
onChange('')
|
|
}}
|
|
>
|
|
<TrashIcon className="size-4 text-fourth-900" />
|
|
</Button>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<label className="flex h-32 w-full max-w-xs cursor-pointer flex-col items-center justify-center gap-2 rounded-2xl border-2 border-dashed border-primary-200 bg-primary-50">
|
|
<input
|
|
accept={SAFE_IMAGE_ACCEPT}
|
|
className="hidden"
|
|
disabled={uploading}
|
|
type="file"
|
|
onChange={handleFileChange}
|
|
/>
|
|
<ImageUploadIcon className="size-8 text-primary" />
|
|
<span className="text-xs text-primary">{uploading ? 'در حال آپلود...' : 'افزودن تصویر شاخص'}</span>
|
|
</label>
|
|
)
|
|
}
|
|
|
|
export default ArticleFeaturedImageUploader
|