Frontend:
- apps/web (Vite+TS, TanStack Router/Query, shadcn/ui @cfdm/ui base-nova)
- 10 страниц в routes/_auth/, Recharts через shadcn Chart, lucide-react
- формы на RHF + Zod (FormSheet/FormField)
- удалены Tabler, Chart.js, react-router-dom
Backend (параллельный трек):
- apps/api (Fastify 5 + Drizzle + better-sqlite3)
- packages/db: Drizzle-схема и repositories по сущностям
- packages/shared: Zod-контракты
- роуты с валидацией и единым форматом ошибок { error: { code, message } }
- sync/backup — заглушки 501 (billmanager-адаптеры переносятся отдельно)
- legacy Express оставлен как runtime по умолчанию (RUNTIME=express)
Infra:
- Dockerfile multi-stage под pnpm workspaces
- .dockerignore и docker-compose обновлены под monorepo
Rules:
- удалены нерелевантные правила (rust, cloudflare, server/frontend-conventions)
- project-structure.mdc и AGENTS.md переписаны под monorepo
- frontend-shadcn.mdc, shadcn-ui-production.mdc, sqlite.mdc обновлены
Co-authored-by: Cursor <cursoragent@cursor.com>
20 lines
729 B
TypeScript
20 lines
729 B
TypeScript
import { Button } from '@cfdm/ui/components/button'
|
|
import { Loader2Icon } from 'lucide-react'
|
|
import type { ButtonHTMLAttributes, ReactNode } from 'react'
|
|
|
|
type LoadingButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
loading?: boolean
|
|
variant?: 'default' | 'outline' | 'secondary' | 'ghost' | 'destructive' | 'link'
|
|
size?: 'default' | 'xs' | 'sm' | 'lg' | 'icon' | 'icon-xs' | 'icon-sm' | 'icon-lg'
|
|
children: ReactNode
|
|
}
|
|
|
|
export function LoadingButton({ loading, disabled, children, ...props }: LoadingButtonProps) {
|
|
return (
|
|
<Button disabled={disabled || loading} {...props}>
|
|
{loading ? <Loader2Icon className="animate-spin" data-icon="inline-start" /> : null}
|
|
{children}
|
|
</Button>
|
|
)
|
|
}
|