fix: flag A/B/C persistence, catalog sort+reorder, offer category pools

- Fix duplicate tier INSERT bug (useState not syncing after router.refresh)
- Add column sort by clicking headers in service catalog
- Add drag-and-drop column reordering (persisted in localStorage)
- Add Categorie Offerta section in Impostazioni (tipo/obiettivo/categoria pools)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 12:12:49 +02:00
parent ba3e824157
commit 320827e13a
9 changed files with 654 additions and 216 deletions
+50 -36
View File
@@ -1,9 +1,15 @@
import { getTargetHourlyRate, updateSetting, SETTINGS_KEYS } from "@/lib/settings";
import { getTargetHourlyRate, getJsonPool, updateSetting, SETTINGS_KEYS } from "@/lib/settings";
import { OfferPoolsSection } from "@/components/admin/impostazioni/OfferPoolsSection";
export const revalidate = 0;
export default async function ImpostazioniPage() {
const targetRate = await getTargetHourlyRate();
const [targetRate, tipoPool, obiettivoPool, categoriaPool] = await Promise.all([
getTargetHourlyRate(),
getJsonPool(SETTINGS_KEYS.OFFER_TIPO_POOL),
getJsonPool(SETTINGS_KEYS.OFFER_OBIETTIVO_POOL),
getJsonPool(SETTINGS_KEYS.OFFER_CATEGORIA_POOL),
]);
async function handleSave(fd: FormData) {
"use server";
@@ -17,43 +23,51 @@ export default async function ImpostazioniPage() {
<div>
<h1 className="text-2xl font-bold text-[#1a1a1a] mb-6">Impostazioni</h1>
<div className="bg-white rounded-xl border border-[#e5e7eb] p-6 max-w-md">
<h2 className="text-base font-semibold text-[#1a1a1a] mb-4">Analytics Profittabilità</h2>
<div className="space-y-6 max-w-2xl">
<div className="bg-white rounded-xl border border-[#e5e7eb] p-6">
<h2 className="text-base font-semibold text-[#1a1a1a] mb-4">Analytics Profittabilità</h2>
<form action={handleSave} className="space-y-4">
<div>
<label
htmlFor="target_hourly_rate"
className="block text-sm font-medium text-[#1a1a1a] mb-1"
>
Tariffa oraria target (/h)
</label>
<p className="text-xs text-[#71717a] mb-2">
Usata per calcolare il costo ideale e il delta profitto/perdita per ogni progetto.
</p>
<div className="flex items-center gap-2">
<span className="text-sm text-[#71717a]"></span>
<input
id="target_hourly_rate"
name="target_hourly_rate"
type="number"
step="0.01"
min="0"
defaultValue={targetRate.toFixed(2)}
className="border border-[#e5e7eb] rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#1A463C]/20 w-32"
/>
<span className="text-sm text-[#71717a]">/h</span>
<form action={handleSave} className="space-y-4">
<div>
<label
htmlFor="target_hourly_rate"
className="block text-sm font-medium text-[#1a1a1a] mb-1"
>
Tariffa oraria target (/h)
</label>
<p className="text-xs text-[#71717a] mb-2">
Usata per calcolare il costo ideale e il delta profitto/perdita per ogni progetto.
</p>
<div className="flex items-center gap-2">
<span className="text-sm text-[#71717a]"></span>
<input
id="target_hourly_rate"
name="target_hourly_rate"
type="number"
step="0.01"
min="0"
defaultValue={targetRate.toFixed(2)}
className="border border-[#e5e7eb] rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#1A463C]/20 w-32"
/>
<span className="text-sm text-[#71717a]">/h</span>
</div>
</div>
</div>
<button
type="submit"
className="bg-[#1A463C] text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-[#1A463C]/90 transition-colors"
>
Salva
</button>
</form>
<button
type="submit"
className="bg-[#1A463C] text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-[#1A463C]/90 transition-colors"
>
Salva
</button>
</form>
</div>
<OfferPoolsSection
tipoPool={tipoPool}
obiettivoPool={obiettivoPool}
categoriaPool={categoriaPool}
/>
</div>
</div>
);
}
}