dc78d8fea3
Componentes React reutilizables: card, dialog, tabs, select, alert, badge, button, input, label, skeleton, tooltip, progress_bar, page_header, form_field, settings_page, crud_page, analytics_page, dashboard_layout. Charts: area, bar, line, sparkline, kpi_card, chart_container. Hooks Wails: use_wails_query, use_wails_mutation, use_wails_stream, use_wails_event, use_animated_canvas. Funciones core: cn, format_compact, chart_colors, get_series_color, wails_cache, theme_config_to_colors. Tipos: chart_series, wails_ipc, theme_config, component_variants. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
113 lines
4.4 KiB
TypeScript
113 lines
4.4 KiB
TypeScript
import * as React from 'react'
|
|
import { cn } from '../core/cn'
|
|
|
|
interface SettingField {
|
|
key: string
|
|
label: string
|
|
description?: string
|
|
type: 'text' | 'number' | 'toggle' | 'select' | 'textarea'
|
|
value?: unknown
|
|
options?: Array<{ label: string; value: string }>
|
|
placeholder?: string
|
|
}
|
|
|
|
interface SettingSection {
|
|
id: string
|
|
title: string
|
|
description?: string
|
|
fields: SettingField[]
|
|
}
|
|
|
|
interface SettingsPageProps {
|
|
title?: string
|
|
subtitle?: string
|
|
sections: SettingSection[]
|
|
onSave?: (values: Record<string, unknown>) => void
|
|
className?: string
|
|
}
|
|
|
|
export function settingsPage({
|
|
title = 'Settings',
|
|
subtitle,
|
|
sections,
|
|
onSave,
|
|
className,
|
|
}: SettingsPageProps): React.ReactElement {
|
|
return (
|
|
<div className={cn('space-y-6', className)}>
|
|
{/* Header */}
|
|
<div className="border-b pb-4">
|
|
<h1 className="text-2xl font-semibold tracking-tight">{title}</h1>
|
|
{subtitle && <p className="text-sm text-muted-foreground">{subtitle}</p>}
|
|
</div>
|
|
|
|
{/* Tabs navigation */}
|
|
<div className="flex gap-6">
|
|
<nav className="hidden w-48 shrink-0 md:block">
|
|
<div className="space-y-1">
|
|
{sections.map((section) => (
|
|
<a
|
|
key={section.id}
|
|
href={`#${section.id}`}
|
|
className="block rounded-md px-3 py-2 text-sm font-medium text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
>
|
|
{section.title}
|
|
</a>
|
|
))}
|
|
</div>
|
|
</nav>
|
|
|
|
{/* Sections */}
|
|
<div className="flex-1 space-y-8">
|
|
{sections.map((section) => (
|
|
<div key={section.id} id={section.id} className="space-y-4">
|
|
<div>
|
|
<h2 className="text-lg font-medium">{section.title}</h2>
|
|
{section.description && <p className="text-sm text-muted-foreground">{section.description}</p>}
|
|
</div>
|
|
<div className="space-y-4 rounded-lg border p-4">
|
|
{section.fields.map((field) => (
|
|
<div key={field.key} className="flex flex-col gap-1.5 sm:flex-row sm:items-center sm:justify-between">
|
|
<div className="space-y-0.5">
|
|
<label className="text-sm font-medium">{field.label}</label>
|
|
{field.description && <p className="text-xs text-muted-foreground">{field.description}</p>}
|
|
</div>
|
|
<div className="w-full sm:w-64">
|
|
{field.type === 'toggle' ? (
|
|
<button className={cn(
|
|
'relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors',
|
|
field.value ? 'bg-primary' : 'bg-input'
|
|
)}>
|
|
<span className={cn('pointer-events-none block size-4 rounded-full bg-background shadow-lg ring-0 transition-transform', field.value ? 'translate-x-4' : 'translate-x-0')} />
|
|
</button>
|
|
) : field.type === 'select' ? (
|
|
<select className="h-8 w-full rounded-lg border border-input bg-transparent px-2.5 text-sm">
|
|
{field.options?.map((opt) => <option key={opt.value} value={opt.value}>{opt.label}</option>)}
|
|
</select>
|
|
) : field.type === 'textarea' ? (
|
|
<textarea className="w-full rounded-lg border border-input bg-transparent px-2.5 py-1.5 text-sm" rows={3} placeholder={field.placeholder} defaultValue={String(field.value ?? '')} />
|
|
) : (
|
|
<input type={field.type} className="h-8 w-full rounded-lg border border-input bg-transparent px-2.5 text-sm" placeholder={field.placeholder} defaultValue={String(field.value ?? '')} />
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
))}
|
|
|
|
{onSave && (
|
|
<div className="flex justify-end border-t pt-4">
|
|
<button className="inline-flex h-8 items-center rounded-lg bg-primary px-4 text-sm font-medium text-primary-foreground hover:bg-primary/80">
|
|
Save changes
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export type { SettingsPageProps, SettingSection, SettingField }
|