3fcb10dac6
Dalle impostazioni si poteva solo aggiungere o eliminare un valore: per cambiargli nome bisognava cancellarlo — strappandolo via da ogni servizio che lo usava — e ricrearlo a mano. La matita nel chip fa il rename in un passo. renamePoolValue esisteva gia' e propagava ovunque, tranne in un punto: importOfferIntoProject copia services.fase dentro phases.title e poi ritrova la fase confrontando i titoli (phases.offer_phase_id non viene mai popolata, quindi il titolo e' l'unico legame). Un rename fermo al catalogo lasciava le fasi dei progetti col vecchio nome e al re-import ne nasceva una duplicata. Ora propaga anche li', con lo stesso match trim+lowercase del merge. E' l'unico rename di tassonomia che scrive fuori dal dominio catalogo/offerte, quindi e' l'unico che chiede conferma, dicendo quante fasi e quanti progetti sta per toccare. Tolte anche le UPDATE manuali in renameServiceOption/renameOfferOption: erano la stessa scrittura che renamePoolValue faceva subito dopo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
109 lines
3.7 KiB
TypeScript
109 lines
3.7 KiB
TypeScript
"use client";
|
|
|
|
import { Tag, BookOpen } from "lucide-react";
|
|
import { PoolManager } from "./PoolManager";
|
|
import {
|
|
addTaxonomyValue,
|
|
getFaseRenameImpact,
|
|
removeTaxonomyValue,
|
|
renameTaxonomyValue,
|
|
} from "@/app/admin/impostazioni/actions";
|
|
|
|
type FieldDef = { id: string; label: string };
|
|
|
|
const OFFER_FIELDS: FieldDef[] = [
|
|
{ id: "offer_categoria", label: "Categoria" },
|
|
{ id: "offer_ticket", label: "Ticket" },
|
|
{ id: "offer_tipo", label: "Tipo" },
|
|
{ id: "offer_obiettivo", label: "Obiettivo" },
|
|
];
|
|
|
|
const CATALOG_FIELDS: FieldDef[] = [
|
|
{ id: "service_fase", label: "Fase" },
|
|
{ id: "service_offerta", label: "Offerta" },
|
|
{ id: "service_pacchetto", label: "Pacchetto" },
|
|
];
|
|
|
|
// Renaming a Fase also rewrites `phases.title` in projects that already imported
|
|
// it — the only taxonomy rename that touches delivery data, so it asks first and
|
|
// says exactly how much it will touch.
|
|
async function faseRenameWarning(oldValue: string, newValue: string): Promise<string | null> {
|
|
const { phases, projects } = await getFaseRenameImpact(oldValue);
|
|
if (phases === 0) return null;
|
|
const fasi = phases === 1 ? "1 fase" : `${phases} fasi`;
|
|
const prog = projects === 1 ? "1 progetto" : `${projects} progetti`;
|
|
return `Rinominare "${oldValue}" in "${newValue}"?\n\nVerrà aggiornato anche il titolo di ${fasi} in ${prog} già avviati.`;
|
|
}
|
|
|
|
function Section({
|
|
icon,
|
|
title,
|
|
subtitle,
|
|
fields,
|
|
pools,
|
|
gridClass,
|
|
}: {
|
|
icon: React.ReactNode;
|
|
title: string;
|
|
subtitle: string;
|
|
fields: FieldDef[];
|
|
pools: Record<string, string[]>;
|
|
gridClass: string;
|
|
}) {
|
|
return (
|
|
<section className="rounded-xl border border-border-light bg-card p-6 shadow-card">
|
|
<div className="mb-6 flex items-start gap-3">
|
|
<div className="grid h-9 w-9 shrink-0 place-items-center rounded-lg border border-border-light bg-muted text-muted-foreground">
|
|
{icon}
|
|
</div>
|
|
<div>
|
|
<h2 className="text-xs font-bold uppercase tracking-wider text-foreground">{title}</h2>
|
|
<p className="mt-0.5 text-xs text-tertiary">{subtitle}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className={`grid gap-6 border-t border-border-light pt-4 ${gridClass}`}>
|
|
{fields.map((f) => (
|
|
<PoolManager
|
|
key={f.id}
|
|
label={f.label}
|
|
pool={pools[f.id] ?? []}
|
|
onAdd={(v) => addTaxonomyValue(f.id, v)}
|
|
onRemove={(v) => removeTaxonomyValue(f.id, v)}
|
|
onRename={(o, n) => renameTaxonomyValue(f.id, o, n)}
|
|
getRenameWarning={f.id === "service_fase" ? faseRenameWarning : undefined}
|
|
/>
|
|
))}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
export function TaxonomyManager({ pools }: { pools: Record<string, string[]> }) {
|
|
return (
|
|
<div className="space-y-8">
|
|
<Section
|
|
icon={<Tag className="h-5 w-5" />}
|
|
title="Tassonomie Offerte"
|
|
subtitle="Valori dei campi Categoria, Ticket, Tipo e Obiettivo dell'editor offerte."
|
|
fields={OFFER_FIELDS}
|
|
pools={pools}
|
|
gridClass="grid-cols-1 md:grid-cols-2"
|
|
/>
|
|
<Section
|
|
icon={<BookOpen className="h-5 w-5" />}
|
|
title="Tassonomie Catalogo"
|
|
subtitle="Valori dei campi Fase, Offerta e Pacchetto del catalogo servizi."
|
|
fields={CATALOG_FIELDS}
|
|
pools={pools}
|
|
gridClass="grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
|
|
/>
|
|
<p className="max-w-4xl px-2 text-xs leading-relaxed text-tertiary">
|
|
I valori si sincronizzano ovunque: creandone uno dall'editor offerte o dal catalogo
|
|
comparirà qui automaticamente. Rinominando un valore da qui il nuovo nome sostituisce il
|
|
vecchio ovunque sia usato; eliminandolo viene rimosso ovunque.
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|