feat: real file upload to /opt/hotelsync/uploads + serve via /uploads/ on API
This commit is contained in:
@@ -52,27 +52,35 @@ function CategoryForm({ category, onClose, onSave }: CategoryFormProps) {
|
||||
const toggleAmenity = (a: string) =>
|
||||
setAmenities(prev => prev.includes(a) ? prev.filter(x => x !== a) : [...prev, a])
|
||||
|
||||
const readFiles = (files: FileList | null) => {
|
||||
Array.from(files ?? []).forEach(file => {
|
||||
if (!file.type.startsWith('image/')) return
|
||||
const reader = new FileReader()
|
||||
reader.onload = ev => {
|
||||
const url = ev.target?.result as string
|
||||
setPhotos(prev => { const next = [...prev, url]; setPhotoIdx(next.length - 1); return next })
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
})
|
||||
const [uploading, setUploading] = useState(false)
|
||||
|
||||
const uploadFiles = async (files: FileList | null) => {
|
||||
const list = Array.from(files ?? []).filter(f => f.type.startsWith('image/'))
|
||||
if (!list.length) return
|
||||
setUploading(true)
|
||||
try {
|
||||
const urls = await Promise.all(list.map(f => api.upload.photo(f)))
|
||||
setPhotos(prev => {
|
||||
const next = [...prev, ...urls]
|
||||
setPhotoIdx(next.length - 1)
|
||||
return next
|
||||
})
|
||||
} catch {
|
||||
setSaveError('Ошибка загрузки фото')
|
||||
} finally {
|
||||
setUploading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handlePhotoUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
readFiles(e.target.files)
|
||||
uploadFiles(e.target.files)
|
||||
e.target.value = ''
|
||||
}
|
||||
|
||||
const handleDrop = (e: React.DragEvent) => {
|
||||
e.preventDefault()
|
||||
setDragging(false)
|
||||
readFiles(e.dataTransfer.files)
|
||||
uploadFiles(e.dataTransfer.files)
|
||||
}
|
||||
|
||||
const removePhoto = (i: number) => {
|
||||
@@ -263,9 +271,9 @@ function CategoryForm({ category, onClose, onSave }: CategoryFormProps) {
|
||||
</div>
|
||||
)}
|
||||
<input ref={fileRef} type="file" accept="image/*" multiple className="hidden" onChange={handlePhotoUpload} />
|
||||
<button onClick={() => fileRef.current?.click()} className="btn-secondary w-full justify-center">
|
||||
<ImagePlus size={14} />
|
||||
Загрузить фото
|
||||
<button onClick={() => fileRef.current?.click()} className="btn-secondary w-full justify-center" disabled={uploading}>
|
||||
{uploading ? <Loader2 size={14} className="animate-spin" /> : <ImagePlus size={14} />}
|
||||
{uploading ? 'Загрузка...' : 'Загрузить фото'}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user