feat: Quiet Luxury design system + Pipeline (ex-Lead) redesign
- Design system foundations: Plus Jakarta Sans font, shadow-card/radius tokens in @theme, design-reference/DESIGN-SYSTEM.md with component inventory - Collapsible admin shell (AdminShell): smooth w-64<->w-20 sidebar, new top header with "Admin" + avatar placeholder, localStorage-persisted state - Rename route /admin/leads -> /admin/pipeline (redirect stubs preserved), nav label Lead -> Pipeline; DB table unchanged - Reusable primitives: StatusBadge, SearchInput, SegmentedToggle (dual-theme) - Luxury restyle of lead table, kanban (6 stages + @dnd-kit intact), PageHeader - Tokenize editable-cell / option-multi-select for dark-mode legibility Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -81,11 +81,11 @@ export function OptionMultiSelect({
|
||||
onClick={() => !disabled && setIsOpen((v) => !v)}
|
||||
className={cn(
|
||||
"flex flex-wrap gap-1 items-center px-2 py-1 rounded transition-colors duration-150 min-h-[28px]",
|
||||
disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer hover:bg-[#f0f0f0]"
|
||||
disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer hover:bg-muted"
|
||||
)}
|
||||
>
|
||||
{values.length === 0 ? (
|
||||
<span className="text-xs text-[#71717a]">—</span>
|
||||
<span className="text-xs text-muted-foreground">—</span>
|
||||
) : (
|
||||
values.map((v) => (
|
||||
<Badge
|
||||
@@ -111,11 +111,11 @@ export function OptionMultiSelect({
|
||||
</Badge>
|
||||
))
|
||||
)}
|
||||
<Plus className="h-3.5 w-3.5 text-[#71717a]" />
|
||||
<Plus className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
</div>
|
||||
|
||||
{isOpen && (
|
||||
<div className="absolute top-full left-0 mt-1 bg-white border border-[#e5e7eb] rounded-md shadow-md p-1.5 z-20 min-w-[220px] max-h-[280px] overflow-y-auto">
|
||||
<div className="absolute top-full left-0 mt-1 bg-popover border border-border rounded-md shadow-md p-1.5 z-20 min-w-[220px] max-h-[280px] overflow-y-auto">
|
||||
<Input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
@@ -160,7 +160,7 @@ export function OptionMultiSelect({
|
||||
) : (
|
||||
<div
|
||||
key={opt}
|
||||
className="flex items-center justify-between gap-1 rounded px-1.5 py-1 hover:bg-[#f5f5f5] group"
|
||||
className="flex items-center justify-between gap-1 rounded px-1.5 py-1 hover:bg-muted group"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
@@ -168,7 +168,7 @@ export function OptionMultiSelect({
|
||||
className="flex items-center gap-2 flex-1 min-w-0 text-left"
|
||||
>
|
||||
<span className="w-3.5 flex-shrink-0">
|
||||
{selected.has(opt) && <Check className="h-3.5 w-3.5 text-[#1A463C]" />}
|
||||
{selected.has(opt) && <Check className="h-3.5 w-3.5 text-primary" />}
|
||||
</span>
|
||||
<Badge
|
||||
variant="outline"
|
||||
@@ -187,7 +187,7 @@ export function OptionMultiSelect({
|
||||
setRenameValue(opt);
|
||||
setRenaming(opt);
|
||||
}}
|
||||
className="opacity-0 group-hover:opacity-100 text-[#71717a] hover:text-[#1a1a1a] p-0.5"
|
||||
className="opacity-0 group-hover:opacity-100 text-muted-foreground hover:text-foreground p-0.5"
|
||||
aria-label={`Rinomina ${opt}`}
|
||||
>
|
||||
<Pencil className="h-3 w-3" />
|
||||
@@ -201,15 +201,15 @@ export function OptionMultiSelect({
|
||||
<button
|
||||
type="button"
|
||||
onClick={createFromQuery}
|
||||
className="flex items-center gap-2 rounded px-1.5 py-1.5 hover:bg-[#f5f5f5] text-left text-sm"
|
||||
className="flex items-center gap-2 rounded px-1.5 py-1.5 hover:bg-muted text-left text-sm"
|
||||
>
|
||||
<Plus className="h-3.5 w-3.5 text-[#1A463C]" />
|
||||
<Plus className="h-3.5 w-3.5 text-primary" />
|
||||
Crea <span className="font-medium">«{query.trim()}»</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
{filtered.length === 0 && !q && (
|
||||
<p className="text-xs text-[#71717a] px-1.5 py-1">Nessuna opzione ancora</p>
|
||||
<p className="text-xs text-muted-foreground px-1.5 py-1">Nessuna opzione ancora</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user