feat(12-04): rewrite /admin/offers page.tsx as new list page entry point

- Replace legacy macro/micro/offer_services CRUD page with thin server
  component fetching getOfferListCards() + getOfferFieldOptions()
- Delegate rendering to new OfferListClient (Task 2)
This commit is contained in:
2026-06-15 10:25:03 +02:00
parent e46607d217
commit 68dc1b605b
+7 -228
View File
@@ -1,238 +1,17 @@
import { getCatalogWithMicros, getAllOfferServices } from "@/lib/offer-queries";
import {
createMacro,
createMicro,
createOfferService,
deleteMacro,
deleteMicro,
toggleOfferServiceActive,
} from "./actions";
import { ServiceCheckboxList } from "@/components/admin/offers/ServiceCheckboxList";
import { getOfferListCards, getOfferFieldOptions } from "@/lib/offer-queries";
import { OfferListClient } from "@/components/admin/offers/OfferListClient";
export const revalidate = 0;
export default async function OffersPage() {
const [catalog, allServices] = await Promise.all([
getCatalogWithMicros(),
getAllOfferServices(),
const [cards, options] = await Promise.all([
getOfferListCards(),
getOfferFieldOptions(),
]);
return (
<div className="max-w-5xl mx-auto py-8 px-4 space-y-10">
<h1 className="text-2xl font-bold text-[#1a1a1a]">Catalogo Offerte</h1>
{/* ── Sezione macro-offerte ── */}
<section>
<h2 className="text-lg font-semibold text-[#1a1a1a] mb-4">Macro-Offerte</h2>
{/* Create macro form */}
<form action={createMacro} className="bg-white rounded-lg border border-[#e5e7eb] p-4 mb-6 space-y-3 max-w-lg">
<p className="text-sm font-medium">Nuova Macro-Offerta</p>
<input
name="internal_name"
placeholder="Nome interno (es. Entry Offer)"
required
className="w-full border rounded px-3 py-1.5 text-sm"
/>
<input
name="public_name"
placeholder="Nome pubblico (es. Starter Branding)"
required
className="w-full border rounded px-3 py-1.5 text-sm"
/>
<textarea
name="transformation_promise"
placeholder="Promessa di trasformazione"
rows={2}
className="w-full border rounded px-3 py-1.5 text-sm"
/>
<button
type="submit"
className="bg-[#1A463C] text-white text-sm px-4 py-1.5 rounded hover:bg-[#163a31]"
>
Aggiungi Macro
</button>
</form>
{/* List macros */}
<div className="space-y-8">
{catalog.map((macro) => (
<div key={macro.id} className="bg-white rounded-lg border border-[#e5e7eb] p-6">
<div className="flex items-start justify-between mb-1">
<div>
<p className="font-semibold text-[#1a1a1a]">{macro.internal_name}</p>
<p className="text-sm text-[#71717a]">Pubblico: {macro.public_name}</p>
{macro.transformation_promise && (
<p className="text-xs text-[#71717a] mt-1 italic">{macro.transformation_promise}</p>
)}
</div>
<form action={deleteMacro.bind(null, macro.id)}>
<button type="submit" className="text-xs text-red-600 hover:underline">
Elimina
</button>
</form>
</div>
{/* Micro-offers under this macro */}
<div className="mt-4 space-y-4 pl-4 border-l-2 border-[#e5e7eb]">
<p className="text-xs font-semibold text-[#71717a] uppercase tracking-wider">Micro-Offerte</p>
{macro.micros.map((micro) => (
<div key={micro.id} className="bg-[#f9f9f9] rounded-lg p-4 space-y-3">
<div className="flex items-start justify-between">
<div>
<p className="text-sm font-medium">{micro.internal_name}</p>
<p className="text-xs text-[#71717a]">
Pubblico: {micro.public_name} &middot;{" "}
{micro.duration_months} {micro.duration_months === 1 ? "mese" : "mesi"}
</p>
{micro.transformation_promise && (
<p className="text-xs text-[#71717a] italic">{micro.transformation_promise}</p>
)}
<p className="text-xs text-[#1a1a1a] mt-1 font-medium">
Prezzo cumulativo: {parseFloat(micro.cumulative_price).toFixed(2)}
</p>
</div>
<form action={deleteMicro.bind(null, micro.id)}>
<button type="submit" className="text-xs text-red-600 hover:underline">
Elimina
</button>
</form>
</div>
{/* Service assignment checkbox list */}
<div>
<p className="text-xs font-medium text-[#71717a] mb-2">Servizi inclusi:</p>
<ServiceCheckboxList
allServices={allServices.map((s) => ({
id: s.id,
name: s.name,
price: String(s.price),
}))}
assignedIds={micro.services.map((s) => s.id)}
microId={micro.id}
/>
</div>
</div>
))}
{/* Create micro form */}
<form action={createMicro} className="bg-white rounded border border-[#e5e7eb] p-3 space-y-2">
<input type="hidden" name="macro_id" value={macro.id} />
<p className="text-xs font-medium">Nuova Micro-Offerta</p>
<input
name="internal_name"
placeholder="Nome interno"
required
className="w-full border rounded px-2 py-1 text-xs"
/>
<input
name="public_name"
placeholder="Nome pubblico"
required
className="w-full border rounded px-2 py-1 text-xs"
/>
<textarea
name="transformation_promise"
placeholder="Promessa di trasformazione"
rows={2}
className="w-full border rounded px-2 py-1 text-xs"
/>
<div className="flex items-center gap-2">
<label className="text-xs">Durata (mesi):</label>
<input
name="duration_months"
type="number"
min="1"
defaultValue="1"
required
className="w-16 border rounded px-2 py-1 text-xs"
/>
</div>
<button
type="submit"
className="bg-[#1A463C] text-white text-xs px-3 py-1 rounded hover:bg-[#163a31]"
>
Aggiungi Micro
</button>
</form>
</div>
</div>
))}
{catalog.length === 0 && (
<p className="text-sm text-[#71717a]">Nessuna macro-offerta ancora. Creane una sopra.</p>
)}
</div>
</section>
{/* ── Sezione servizi offerta ── */}
<section>
<h2 className="text-lg font-semibold text-[#1a1a1a] mb-4">Servizi Offerta</h2>
<p className="text-sm text-[#71717a] mb-4">
I servizi qui sono diversi dal catalogo preventivi hanno una descrizione della
trasformazione e vengono raggruppati nelle micro-offerte.
</p>
{/* List services */}
<div className="bg-white rounded-lg border border-[#e5e7eb] divide-y divide-[#e5e7eb] mb-6">
{allServices.map((svc) => (
<div key={svc.id} className="flex items-center justify-between px-4 py-3">
<div>
<p className="text-sm font-medium">{svc.name}</p>
{svc.transformation_description && (
<p className="text-xs text-[#71717a]">{svc.transformation_description}</p>
)}
</div>
<div className="flex items-center gap-4">
<span className="text-sm font-mono">{parseFloat(String(svc.price)).toFixed(2)}</span>
<form action={toggleOfferServiceActive.bind(null, svc.id, !svc.active)}>
<button type="submit" className="text-xs text-[#71717a] hover:text-[#1a1a1a]">
{svc.active ? "Disattiva" : "Attiva"}
</button>
</form>
</div>
</div>
))}
{allServices.length === 0 && (
<p className="px-4 py-3 text-sm text-[#71717a]">Nessun servizio ancora.</p>
)}
</div>
{/* Create service form */}
<form
action={createOfferService}
className="bg-white rounded-lg border border-[#e5e7eb] p-4 space-y-3 max-w-lg"
>
<p className="text-sm font-medium">Nuovo Servizio Offerta</p>
<input
name="name"
placeholder="Nome servizio"
required
className="w-full border rounded px-3 py-1.5 text-sm"
/>
<input
name="price"
type="number"
step="0.01"
min="0"
placeholder="Prezzo (€)"
required
className="w-full border rounded px-3 py-1.5 text-sm"
/>
<textarea
name="transformation_description"
placeholder="Descrizione trasformazione (marketing)"
rows={2}
className="w-full border rounded px-3 py-1.5 text-sm"
/>
<button
type="submit"
className="bg-[#1A463C] text-white text-sm px-4 py-1.5 rounded hover:bg-[#163a31]"
>
Aggiungi Servizio
</button>
</form>
</section>
<div>
<OfferListClient cards={cards} categoryOptions={options.categoria} />
</div>
);
}