v0.1 — Venue Image Upload & Crop
Branch: develop (commit diretto)
Data: 24/03/2026
Tipo: feat (vendor dashboard, storage, UX)
Contesto
La pagina “Le tue Venue” permetteva solo di inserire un URL immagine manualmente. Con questa modifica il vendor può scegliere tra:
- Link URL — comportamento precedente, incolla un URL diretto
- Carica file — upload di un file locale con ritaglio visuale integrato
Componente ImageUploadCrop
File: components/ui/ImageUploadCrop.tsx
Componente riutilizzabile basato su react-easy-crop e Canvas API.
Props:
| Prop | Tipo | Default | Descrizione |
|---|---|---|---|
onCropComplete | (dataUrl: string) => void | — | Callback con dataUrl JPEG del ritaglio |
aspectRatio | number | 16/9 | Rapporto d’aspetto del crop |
Flusso:
- Mostra un’area di drop/click per il file (
<input type="file" accept="image/jpeg,image/png,image/webp">) - Dopo la selezione: apre l’interfaccia di crop con slider zoom (1×–3×)
- Click “Applica ritaglio” → estrae l’area selezionata via Canvas API → chiama
onCropCompletecon la dataUrl JPEG (qualità 0.9) - Il parent riceve il dataUrl e lo usa per upload/preview
Modifica app/vendor/(dashboard)/venues/page.tsx
Tab di selezione modalità
<div className="flex gap-1 p-1 bg-gray-100 rounded-xl w-fit mb-3">
<button onClick={() => setImageMode('url')}>Link URL</button>
<button onClick={() => setImageMode('file')}>Carica file</button>
</div>Upload su Supabase Storage
Quando imageMode === 'file' e il crop è completato, prima del salvataggio viene eseguito:
async function uploadCroppedImage(dataUrl: string, userId: string): Promise<string> {
const blob = await fetch(dataUrl).then(r => r.blob())
const fileName = `${userId}/${Date.now()}.jpg`
const { data, error } = await supabase.storage
.from('venue-images')
.upload(fileName, blob, { contentType: 'image/jpeg', upsert: false })
if (error) throw error
const { data: { publicUrl } } = supabase.storage
.from('venue-images')
.getPublicUrl(data.path)
return publicUrl
}Il URL pubblico restituito viene salvato in venues.image_url.
Storage bucket venue-images
Migration: supabase/migrations/20260324000005_venue_images_storage.sql
| Parametro | Valore |
|---|---|
| ID | venue-images |
| Pubblico | sì |
| Max file size | 5 MB |
| Tipi ammessi | image/jpeg, image/png, image/webp |
RLS policies:
| Policy | Operazione | Condizione |
|---|---|---|
venue_images_public_read | SELECT | bucket_id = 'venue-images' |
venue_images_auth_insert | INSERT | autenticato |
venue_images_owner_update | UPDATE | folder = auth.uid() |
venue_images_owner_delete | DELETE | folder = auth.uid() |
I file vengono organizzati per cartella {userId}/{timestamp}.jpg.
Dipendenza aggiunta
react-easy-crop: ^5.5.7
File modificati / creati
components/ui/ImageUploadCrop.tsx(nuovo)app/vendor/(dashboard)/venues/page.tsxsupabase/migrations/20260324000005_venue_images_storage.sql(nuovo)package.json