// SONARA — Producer Ordini (3 tabs: Ordini · Offerte inviate · Vendite catalog) const OIcons = window.SonaraIcons; /* ─────── ICONS ─────── */ const ChevIc = ({ size = 12 }) => ; const StarIc = ({ size = 12, filled = true }) => ; const DotsIc = ({ size = 14 }) => ; const SearchIc = ({ size = 14 }) => ; const ChevDownIc = ({ size = 11 }) => ; const ClockIc = ({ size = 12 }) => ; const DownloadIc = ({ size = 13 }) => ; const CheckIc = ({ size = 12 }) => ; const XIc = ({ size = 12 }) => ; /* Status map for ordini */ const ORD_STATUS = { briefing: { label: "Briefing", cls: "info" }, in_progress: { label: "In lavorazione", cls: "active" }, in_review: { label: "In revisione", cls: "review" }, revisions_requested: { label: "Revisione richiesta", cls: "warn" }, waiting_files: { label: "Attesa stems", cls: "warn" }, completed: { label: "Completato", cls: "ok" }, disputed: { label: "In disputa", cls: "danger" }, cancelled: { label: "Annullato", cls: "neutral" }, }; const ORDERS = [ { id: "ORD-4821", kind: "Mix + Master", title: "EP \"Asfalto\" (5 brani)", who: "RAZZA", whoIn: "RZ", verified: true, status: "revisions_requested", net: 158, gross: 180, deadline: -2, activity: "Hai consegnato v1 · revisione richiesta su \"Vento\"", urgent: true, openType: "open" }, { id: "ORD-4815", kind: "Beat esclusivo", title: "Notturno · uso esclusivo", who: "Yoshi K.", whoIn: "YK", verified: true, status: "briefing", net: 308, gross: 350, deadline: 22, activity: "Artista ha caricato i riferimenti · 1h fa", urgent: true, openType: "open" }, { id: "ORD-4808", kind: "Mix stereo", title: "Singolo \"Mezzanotte\"", who: "M. Lentini", whoIn: "ML", verified: false, status: "in_progress", net: 105, gross: 120, deadline: 48, activity: "Stems completi · countdown attivo", urgent: false, openType: "open" }, { id: "ORD-4801", kind: "Vocal production", title: "Brano \"Senza Te\"", who: "Sara D.", whoIn: "SD", verified: false, status: "waiting_files", net: 79, gross: 90, deadline: 96, activity: "Attesa stems · countdown in pausa", urgent: false, openType: "open" }, { id: "ORD-4799", kind: "Mix + Master", title: "Singolo \"Bagliore\"", who: "Giulia P.", whoIn: "GP", verified: false, status: "completed", net: 158, gross: 180, deadline: 0, activity: "Chiuso · ★ 5.0", urgent: false, openType: "closed", closedAt: "12 maggio 2026" }, { id: "ORD-4792", kind: "Custom production", title: "Beat su brief \"Tramonto\"", who: "Federico V.", whoIn: "FV", verified: true, status: "completed", net: 308, gross: 350, deadline: 0, activity: "Chiuso · ★ 4.8", urgent: false, openType: "closed", closedAt: "8 maggio 2026" }, { id: "ORD-4780", kind: "Mix stereo", title: "Singolo \"Pioggia\"", who: "Marco T.", whoIn: "MT", verified: false, status: "completed", net: 105, gross: 120, deadline: 0, activity: "Chiuso · ★ 5.0", urgent: false, openType: "closed", closedAt: "3 maggio 2026" }, { id: "ORD-4762", kind: "Beat esclusivo", title: "Lupo · uso esclusivo", who: "Ricky F.", whoIn: "RF", verified: true, status: "cancelled", net: 0, gross: 300, deadline: 0, activity: "Annullato dal cliente entro 24h", urgent: false, openType: "closed", closedAt: "1 maggio 2026" }, ]; /* ─────── OFFERS ─────── */ const OFF_STATUS = { sent: { label: "Inviata", cls: "info" }, counter: { label: "Counter ricevuto", cls: "warn" }, accepted: { label: "Accettata", cls: "ok" }, rejected: { label: "Rifiutata", cls: "neutral" }, expired: { label: "Scaduta", cls: "neutral" }, withdrawn: { label: "Ritirata", cls: "neutral" }, }; const OFFERS = [ { id: "OFF-2014", req: "Cerco beat trap 808 pesante", reqOwner: "AlessioRap", reqInit: "AR", type: "Beat custom", myPrice: 180, deadline: "10 gg", status: "counter", time: "1h fa", message: "Lavoro su sound dark con 808 spaziali. Ti mando 2 ref qui sotto.", counter: { price: 140, note: "Per noi 140 va bene se possiamo aggiungere 1 revisione" } }, { id: "OFF-2010", req: "Mix EP 5 tracce drill", reqOwner: "Quartiere9", reqInit: "Q9", type: "Mix + Master", myPrice: 820, deadline: "21 gg", status: "sent", time: "5h fa", message: "Specializzato in drill, ho lavorato a EP simili. Pacchetto include 2 revisioni + master radio-ready." }, { id: "OFF-2007", req: "Custom production R&B Daniel Caesar vibe", reqOwner: "Lina Sky", reqInit: "LS", type: "Custom production", myPrice: 380, deadline: "7 gg", status: "accepted", time: "Ieri", message: "Posso fare beat + mix base inclusi.", convertedTo: "ORD-4821" }, { id: "OFF-1998", req: "Beat hip-hop boom bap", reqOwner: "Rino82", reqInit: "R8", type: "Beat custom", myPrice: 220, deadline: "5 gg", status: "rejected", time: "2gg fa", message: "Ho campioni vinilici puliti che fanno al caso tuo." }, { id: "OFF-1985", req: "Master per singolo trap", reqOwner: "DamoBeats", reqInit: "DB", type: "Mastering", myPrice: 80, deadline: "2 gg", status: "expired", time: "1 sett. fa", message: "Master loud + radio-ready in 48h." }, ]; /* ─────── CATALOG SALES ─────── */ const SALES = [ { id: "S-3201", beat: "Lupo Solitario", grad: "b3", artist: "RAZZA", artistInit: "RZ", date: "Oggi", license: "Non-esclusiva", price: 45, net: 39.6 }, { id: "S-3198", beat: "Lupo Solitario", grad: "b3", artist: "Yoshi K.", artistInit: "YK", date: "Ieri", license: "Non-esclusiva", price: 45, net: 39.6 }, { id: "S-3192", beat: "Notturno", grad: "b1", artist: "Marco T.", artistInit: "MT", date: "3gg fa", license: "Non-esclusiva", price: 45, net: 39.6 }, { id: "S-3187", beat: "Cassetta Vuota", grad: "b6", artist: "Lina Sky", artistInit: "LS", date: "5gg fa", license: "Non-esclusiva", price: 45, net: 39.6 }, { id: "S-3180", beat: "Mezzanotte", grad: "b1", artist: "RAZZA", artistInit: "RZ", date: "1 sett. fa", license: "Esclusiva", price: 420, net: 369.6 }, { id: "S-3175", beat: "Asfalto Bagnato", grad: "b2", artist: "Federico V.", artistInit: "FV", date: "2 sett. fa", license: "Non-esclusiva", price: 50, net: 44 }, { id: "S-3168", beat: "Lupo Solitario", grad: "b3", artist: "Damo", artistInit: "DM", date: "2 sett. fa", license: "Non-esclusiva", price: 40, net: 35.2 }, { id: "S-3162", beat: "Doppia Faccia", grad: "b4", artist: "AlessioRap", artistInit: "AR", date: "3 sett. fa", license: "Non-esclusiva", price: 45, net: 39.6 }, ]; /* ─────── ORD CARD ─────── */ function OrdCard({ o }) { const fmtDeadline = (h) => { if (h < 0) return `In ritardo ${Math.abs(h)}h`; if (h === 0) return null; if (h < 24) return `Tra ${h}h`; return `Tra ${Math.round(h/24)}gg`; }; const deadlineTxt = fmtDeadline(o.deadline); return (
{o.kind} {ORD_STATUS[o.status].label} {o.id} {deadlineTxt && ( {o.urgent && } {deadlineTxt} )} {o.closedAt && Chiuso {o.closedAt}}
{o.title}
{o.whoIn} {o.who} · €{o.net} netto · {o.activity}
{o.openType === "open" ? ( <> ) : ( <> )}
); } /* ─────── ORDERS TAB ─────── */ function OrdersTab() { const [openClosed, setOpenClosed] = React.useState("open"); const [statusFilter, setStatusFilter] = React.useState("all"); const [sort, setSort] = React.useState("deadline"); let items = ORDERS.filter(o => o.openType === openClosed); if (statusFilter !== "all") items = items.filter(o => o.status === statusFilter); // sort: deadline = urgenza items = [...items].sort((a, b) => { if (sort === "deadline") return a.deadline - b.deadline; if (sort === "amount") return b.net - a.net; return 0; }); const inProgressCount = ORDERS.filter(o => o.openType === "open").length; return ( <>
Ordina
{[ { v: "all", l: "Tutti" }, { v: "briefing", l: "Briefing" }, { v: "in_progress", l: "In lavorazione" }, { v: "in_review", l: "In revisione" }, { v: "revisions_requested", l: "Revisione richiesta" }, { v: "waiting_files", l: "Attesa stems" }, ...(openClosed === "closed" ? [{ v: "completed", l: "Completati" }, { v: "cancelled", l: "Annullati" }] : []), ].map(c => ( ))}
{items.map(o => )}
{items.length === 0 && (
Nessun ordine in questa categoria
Cambia filtro o aspetta il prossimo ordine.
)} ); } /* ─────── OFFER CARD ─────── */ function OfferCard({ o }) { return (
{o.id} {OFF_STATUS[o.status].label} {o.time}
Richiesta {o.req}
Artista
{o.reqInit} {o.reqOwner}
Tipo
{o.type}
Prezzo offerto
€{o.myPrice}
Tempi
{o.deadline}
{o.message &&

"{o.message}"

} {o.counter && (
{o.reqInit} {o.reqOwner} ha contro-offerto €{o.counter.price}

"{o.counter.note}"

)} {o.status === "accepted" && (
Diventata ordine {o.convertedTo}
)} {(o.status === "sent" || o.status === "expired") && (
Vedi richiesta {o.status === "sent" && }
)}
); } /* ─────── OFFERS TAB ─────── */ function OffersTab() { const [filter, setFilter] = React.useState("all"); const items = filter === "all" ? OFFERS : OFFERS.filter(o => o.status === filter); return ( <>
{OFFERS.filter(o => o.status === "sent" || o.status === "counter").length} offerte attive · max 15
Sfoglia opportunità →
{[ { v: "all", l: "Tutte" }, { v: "sent", l: "In attesa" }, { v: "counter", l: "Counter ricevuto" }, { v: "accepted", l: "Accettate" }, { v: "rejected", l: "Rifiutate" }, { v: "expired", l: "Scadute" }, ].map(c => ( ))}
{items.map(o => )}
); } /* ─────── SALES TAB ─────── */ function SalesTab() { const [groupBy, setGroupBy] = React.useState("date"); // date | beat const total = SALES.reduce((a, s) => a + s.net, 0); let groups; if (groupBy === "beat") { const map = {}; SALES.forEach(s => { if (!map[s.beat]) map[s.beat] = { beat: s.beat, grad: s.grad, count: 0, revenue: 0, items: [] }; map[s.beat].count++; map[s.beat].revenue += s.net; map[s.beat].items.push(s); }); groups = Object.values(map).sort((a, b) => b.revenue - a.revenue); } return ( <>
{SALES.length} vendite · €{total.toFixed(0)} netto totale
Raggruppa
{groupBy === "date" ? (
Beat
Artista
Licenza
Data
Lordo
Netto
{SALES.map(s => (
{s.beat}
{s.id}
{s.artistInit} {s.artist}
{s.license}
{s.date}
€{s.price}
€{s.net.toFixed(2)}
))}
) : (
{groups.map(g => (
{g.beat}
{g.count} vendite · €{g.revenue.toFixed(0)} netto
Apri beat
{g.items.map(s => (
{s.artistInit} {s.artist} {s.date} {s.license} €{s.net.toFixed(2)}
))}
))}
)} ); } /* ─────── PAGE ─────── */ function OrdiniPage() { const [tab, setTab] = React.useState("orders"); const completedCount = ORDERS.filter(o => o.status === "completed").length; const lifetime = 4120; const rating = 4.8; const activeOffers = OFFERS.filter(o => o.status === "sent" || o.status === "counter").length; return (

Ordini

{completedCount + 40} completati · {lifetime.toLocaleString("it-IT")} incassati · {rating} rating ricevuto
{tab === "orders" && } {tab === "offers" && } {tab === "sales" && }
); } window.OrdiniPage = OrdiniPage;