feat: add background image upload to banner builder
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,9 @@ export function BannerBuilder({ onApply, onClose }: Props) {
|
||||
const [price, setPrice] = useState('')
|
||||
const [comparePrice, setComparePrice] = useState('')
|
||||
const [selectedImageUrl, setSelectedImageUrl] = useState<string | null>(null)
|
||||
const [backgroundBlob, setBackgroundBlob] = useState<Blob | null>(null)
|
||||
const [backgroundPreview, setBackgroundPreview] = useState<string | null>(null)
|
||||
const bgFileRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
// Generation
|
||||
const [rendering, setRendering] = useState(false)
|
||||
@@ -112,6 +115,22 @@ export function BannerBuilder({ onApply, onClose }: Props) {
|
||||
}
|
||||
}
|
||||
|
||||
const handleBgFile = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0]
|
||||
if (!file) return
|
||||
setBackgroundBlob(file)
|
||||
const url = URL.createObjectURL(file)
|
||||
if (backgroundPreview) URL.revokeObjectURL(backgroundPreview)
|
||||
setBackgroundPreview(url)
|
||||
e.target.value = ''
|
||||
}
|
||||
|
||||
const removeBg = () => {
|
||||
if (backgroundPreview) URL.revokeObjectURL(backgroundPreview)
|
||||
setBackgroundBlob(null)
|
||||
setBackgroundPreview(null)
|
||||
}
|
||||
|
||||
const getConfig = useCallback((): BannerConfig => ({
|
||||
scheme,
|
||||
promoLabel,
|
||||
@@ -119,7 +138,8 @@ export function BannerBuilder({ onApply, onClose }: Props) {
|
||||
price,
|
||||
comparePrice,
|
||||
productImageUrl: selectedImageUrl,
|
||||
}), [scheme, promoLabel, title, price, comparePrice, selectedImageUrl])
|
||||
backgroundImageBlob: backgroundBlob,
|
||||
}), [scheme, promoLabel, title, price, comparePrice, selectedImageUrl, backgroundBlob])
|
||||
|
||||
const generate = useCallback(async () => {
|
||||
setRendering(true)
|
||||
@@ -142,7 +162,7 @@ export function BannerBuilder({ onApply, onClose }: Props) {
|
||||
if (!title && !product) return
|
||||
const t = setTimeout(() => { generate() }, 600)
|
||||
return () => clearTimeout(t)
|
||||
}, [scheme, promoLabel, title, price, comparePrice, selectedImageUrl]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
}, [scheme, promoLabel, title, price, comparePrice, selectedImageUrl, backgroundBlob]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const applyToSlide = async () => {
|
||||
if (!previewRef.current) return
|
||||
@@ -248,9 +268,40 @@ export function BannerBuilder({ onApply, onClose }: Props) {
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* Image de fond */}
|
||||
<section>
|
||||
<h3 className="text-xs font-semibold text-gray-300 uppercase tracking-wide mb-2">Image de fond</h3>
|
||||
{backgroundPreview ? (
|
||||
<div className="relative rounded overflow-hidden border border-slate-600">
|
||||
<img src={backgroundPreview} alt="Fond" className="w-full h-24 object-cover" />
|
||||
<button
|
||||
type="button"
|
||||
onClick={removeBg}
|
||||
className="absolute top-1 right-1 bg-black/70 hover:bg-red-900 text-white text-xs px-1.5 py-0.5 rounded"
|
||||
>
|
||||
✕ Retirer
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => bgFileRef.current?.click()}
|
||||
className="w-full py-3 border-2 border-dashed border-slate-600 hover:border-indigo-500 rounded text-xs text-slate-400 hover:text-indigo-400 transition-colors"
|
||||
>
|
||||
+ Uploader une image de fond
|
||||
</button>
|
||||
)}
|
||||
<input ref={bgFileRef} type="file" accept="image/*" className="hidden" onChange={handleBgFile} />
|
||||
{backgroundPreview && (
|
||||
<p className="text-xs text-slate-500 mt-1">Un overlay sombre est appliqué automatiquement pour la lisibilité du texte.</p>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* Thème couleur */}
|
||||
<section>
|
||||
<h3 className="text-xs font-semibold text-gray-300 uppercase tracking-wide mb-2">Thème couleur</h3>
|
||||
<h3 className="text-xs font-semibold text-gray-300 uppercase tracking-wide mb-2">
|
||||
{backgroundPreview ? 'Couleur du texte' : 'Thème couleur'}
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 gap-1.5">
|
||||
{schemeEntries.map(([key, s]) => (
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user