import type { ReactNode } from 'react'
import { Link } from 'react-router-dom'
export function PageTitle({ title, subtitle }: { title: string; subtitle?: string }) {
return (
{title}
{subtitle ? (
{subtitle}
) : null}
)
}
export function Card({ children, className = '' }: { children: ReactNode; className?: string }) {
return (
{children}
)
}
export function Field({
label,
children,
}: {
label: string
children: ReactNode
}) {
return (
)
}
export function Input(props: React.InputHTMLAttributes) {
return (
)
}
export function Textarea(props: React.TextareaHTMLAttributes) {
return (
)
}
export function Button({
children,
variant = 'primary',
...props
}: React.ButtonHTMLAttributes & { variant?: 'primary' | 'ghost' | 'danger' | 'soft' }) {
const styles =
variant === 'primary'
? 'bg-brand text-white hover:bg-brand-hover shadow-soft'
: variant === 'danger'
? 'bg-danger text-white hover:opacity-90'
: variant === 'soft'
? 'bg-brand-soft text-brand hover:opacity-90'
: 'border border-line bg-surface text-ink hover:bg-surface-muted'
const { className, ...rest } = props
return (
)
}
export function Badge({
children,
tone = 'neutral',
}: {
children: ReactNode
tone?: 'neutral' | 'brand' | 'sky' | 'success' | 'warning' | 'danger'
}) {
const tones = {
neutral: 'bg-accent-soft text-ink',
brand: 'bg-brand-soft text-brand',
sky: 'bg-glow text-brand',
success: 'bg-success-soft text-success',
warning: 'bg-warning-soft text-warning',
danger: 'bg-danger-soft text-danger',
}
return (
{children}
)
}
export function ErrorText({ message }: { message?: string }) {
if (!message) return null
return {message}
}
export function CopyableId({ label, value }: { label: string; value: string }) {
const copy = async () => {
if (!value) return
await navigator.clipboard.writeText(value)
}
return (
{label}
{value || '—'}
{value ? (
) : null}
)
}
export function QuickLinkCard({
to,
title,
desc,
tag,
}: {
to: string
title: string
desc: string
tag?: string
}) {
return (
{tag ? {tag} : null}
{title}
{desc}
前往 →
)
}
export function StatCard({
label,
value,
hint,
tone = 'default',
}: {
label: string
value: ReactNode
hint?: string
tone?: 'default' | 'brand' | 'sky'
}) {
const bg = tone === 'brand' ? 'bg-brand-soft' : tone === 'sky' ? 'bg-glow' : 'bg-surface'
return (
{label}
{value}
{hint ?
{hint}
: null}
)
}