Vercel odmietal poziadavky nad 4,5 MB (Request Entity Too Large) a bucket berie najviac 10 MB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
120 lines
3.7 KiB
TypeScript
120 lines
3.7 KiB
TypeScript
'use client';
|
|
|
|
import React, { useRef, useState } from 'react';
|
|
import { Upload, Loader2, Image as ImageIcon } from 'lucide-react';
|
|
import { shrinkImage, uploadFile } from '@/lib/upload';
|
|
|
|
interface ImageUploadInputProps {
|
|
label?: string;
|
|
value: string;
|
|
onChange: (url: string) => void;
|
|
placeholder?: string;
|
|
// Voliteľný ručný alt text; prázdny = použije sa automatický (altPlaceholder)
|
|
altValue?: string;
|
|
onAltChange?: (alt: string) => void;
|
|
altPlaceholder?: string;
|
|
}
|
|
|
|
export const ImageUploadInput: React.FC<ImageUploadInputProps> = ({
|
|
label = 'Obrázok',
|
|
value,
|
|
onChange,
|
|
placeholder = 'https://... alebo nahrajte súbor',
|
|
altValue,
|
|
onAltChange,
|
|
altPlaceholder
|
|
}) => {
|
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
const [isUploading, setIsUploading] = useState(false);
|
|
|
|
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const file = e.target.files?.[0];
|
|
if (!file) return;
|
|
|
|
setIsUploading(true);
|
|
try {
|
|
const small = await shrinkImage(file);
|
|
onChange(await uploadFile(small, small.name));
|
|
} catch (err: any) {
|
|
alert(`Chyba pri nahrávaní obrázka: ${err.message}`);
|
|
} finally {
|
|
setIsUploading(false);
|
|
if (fileInputRef.current) {
|
|
fileInputRef.current.value = '';
|
|
}
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-1.5">
|
|
{label && <label className="block text-[11px] font-semibold text-gray-700">{label}</label>}
|
|
|
|
<div className="flex items-center gap-2">
|
|
{/* Náhľad miniatúry */}
|
|
<div className="w-10 h-10 rounded-lg bg-gray-100 border border-gray-200 flex-shrink-0 flex items-center justify-center overflow-hidden">
|
|
{value ? (
|
|
<img
|
|
src={value}
|
|
alt="Náhľad"
|
|
className="w-full h-full object-contain"
|
|
onError={(e) => {
|
|
(e.target as HTMLElement).style.display = 'none';
|
|
}}
|
|
/>
|
|
) : (
|
|
<ImageIcon className="w-4 h-4 text-gray-400" />
|
|
)}
|
|
</div>
|
|
|
|
{/* Textové pole s URL */}
|
|
<input
|
|
type="text"
|
|
value={value || ''}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
placeholder={placeholder}
|
|
className="flex-1 min-w-0 text-xs font-mono border border-gray-300 rounded-lg p-2 bg-white"
|
|
/>
|
|
|
|
{/* Tlačidlo nahrávania */}
|
|
<input
|
|
type="file"
|
|
ref={fileInputRef}
|
|
onChange={handleFileChange}
|
|
accept="image/*"
|
|
className="hidden"
|
|
/>
|
|
<button
|
|
type="button"
|
|
onClick={() => fileInputRef.current?.click()}
|
|
disabled={isUploading}
|
|
className="px-2.5 py-2 text-xs font-bold text-gray-700 bg-gray-100 hover:bg-gray-200 border border-gray-300 rounded-lg flex items-center gap-1 transition-colors whitespace-nowrap disabled:opacity-50"
|
|
title="Nahrať obrázok zo zariadenia"
|
|
>
|
|
{isUploading ? (
|
|
<>
|
|
<Loader2 className="w-3.5 h-3.5 animate-spin text-red-600" />
|
|
<span>Nahrávam...</span>
|
|
</>
|
|
) : (
|
|
<>
|
|
<Upload className="w-3.5 h-3.5" />
|
|
<span>Nahrať</span>
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
|
|
{onAltChange && (
|
|
<input
|
|
type="text"
|
|
value={altValue || ''}
|
|
onChange={(e) => onAltChange(e.target.value)}
|
|
placeholder={altPlaceholder ? `Alt text (automaticky: ${altPlaceholder})` : 'Alt text'}
|
|
title="Alt text obrázka. Ak ho nevyplníte, doplní sa automaticky."
|
|
className="w-full text-[11px] border border-gray-200 rounded-lg px-2 py-1.5 bg-white placeholder:text-gray-400"
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|