style: refine settings taxonomy UI (field cells, icon headers, 2-col grid)
- PoolManager: defined field cells with value count, refined chips, inline ghost + button, focus ring, pending/empty states - TaxonomyManager: icon-badged section headers, ordered 2-col grid, sync explainer note - Settings page: wider container (max-w-4xl) for the grid to breathe Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useTransition } from "react";
|
||||
import { X } from "lucide-react";
|
||||
import { X, Plus } from "lucide-react";
|
||||
|
||||
export function PoolManager({
|
||||
label,
|
||||
@@ -18,6 +18,8 @@ export function PoolManager({
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const canAdd = input.trim().length > 0;
|
||||
|
||||
function handleAdd() {
|
||||
const trimmed = input.trim();
|
||||
if (!trimmed) return;
|
||||
@@ -48,52 +50,68 @@ export function PoolManager({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-1 min-w-[180px]">
|
||||
<p className="text-sm font-medium text-[#1a1a1a] mb-2">{label}</p>
|
||||
|
||||
<div className="flex flex-wrap gap-1.5 mb-3 min-h-[28px]">
|
||||
{pool.length === 0 && (
|
||||
<span className="text-xs text-[#a1a1aa]">Nessun valore</span>
|
||||
<div
|
||||
className={`rounded-lg border border-[#ececee] bg-[#fafafa] p-3.5 transition-opacity ${
|
||||
isPending ? "opacity-60" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between mb-2.5">
|
||||
<p className="text-sm font-medium text-[#1a1a1a]">{label}</p>
|
||||
{pool.length > 0 && (
|
||||
<span className="text-[11px] text-[#a1a1aa] tabular-nums">{pool.length}</span>
|
||||
)}
|
||||
{pool.map((v) => (
|
||||
<span
|
||||
key={v}
|
||||
className="inline-flex items-center gap-1 bg-[#f4f4f5] text-[#1a1a1a] text-xs px-2 py-0.5 rounded-full"
|
||||
>
|
||||
{v}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemove(v)}
|
||||
disabled={isPending}
|
||||
className="text-[#71717a] hover:text-red-500 transition-colors disabled:opacity-50"
|
||||
aria-label={`Rimuovi ${v}`}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex gap-1.5">
|
||||
<div className="flex flex-wrap gap-1.5 mb-3 min-h-[26px]">
|
||||
{pool.length === 0 ? (
|
||||
<span className="text-xs text-[#a1a1aa] italic">Nessun valore</span>
|
||||
) : (
|
||||
pool.map((v) => (
|
||||
<span
|
||||
key={v}
|
||||
className="group inline-flex items-center gap-1 rounded-md border border-[#e5e7eb] bg-white pl-2.5 pr-1 py-1 text-xs text-[#27272a] shadow-[0_1px_1px_rgba(0,0,0,0.02)]"
|
||||
>
|
||||
{v}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemove(v)}
|
||||
disabled={isPending}
|
||||
className="rounded p-0.5 text-[#c4c4c8] transition-colors hover:bg-red-50 hover:text-red-500 disabled:opacity-50"
|
||||
aria-label={`Elimina ${v}`}
|
||||
title={`Elimina "${v}" ovunque`}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</button>
|
||||
</span>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
value={input}
|
||||
onChange={(e) => { setInput(e.target.value); setError(null); }}
|
||||
onChange={(e) => {
|
||||
setInput(e.target.value);
|
||||
setError(null);
|
||||
}}
|
||||
onKeyDown={onKeyDown}
|
||||
placeholder="Aggiungi..."
|
||||
placeholder="Aggiungi valore"
|
||||
disabled={isPending}
|
||||
className="border border-[#e5e7eb] rounded-lg px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-[#1A463C]/20 flex-1 min-w-0 disabled:opacity-50"
|
||||
aria-label={`Aggiungi valore a ${label}`}
|
||||
className="h-8 w-full rounded-md border border-[#e5e7eb] bg-white pl-3 pr-9 text-sm text-[#1a1a1a] placeholder:text-[#a1a1aa] transition-shadow focus:border-[#1A463C]/30 focus:outline-none focus:ring-2 focus:ring-[#1A463C]/15 disabled:opacity-50"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleAdd}
|
||||
disabled={isPending || !input.trim()}
|
||||
className="bg-[#1A463C] text-white px-3 py-1.5 rounded-lg text-sm font-medium hover:bg-[#1A463C]/90 transition-colors disabled:opacity-40"
|
||||
disabled={isPending || !canAdd}
|
||||
aria-label={`Conferma aggiunta a ${label}`}
|
||||
className="absolute right-1 top-1/2 -translate-y-1/2 grid h-6 w-6 place-items-center rounded text-[#1A463C] transition-colors hover:bg-[#1A463C]/10 disabled:text-[#c4c4c8] disabled:hover:bg-transparent"
|
||||
>
|
||||
+
|
||||
<Plus className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
{error && <p className="text-xs text-red-500 mt-1">{error}</p>}
|
||||
{error && <p className="mt-1.5 text-xs text-red-500">{error}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user