erp-construccion-frontend-v2/web/src/components/common/LoadingSpinner.tsx
Adrian Flores Cortes 765a639004 refactor(frontend): Extract common components and types
- Create src/types/ with organized type files:
  - api.types.ts, auth.types.ts, construction.types.ts
  - estimates.types.ts, hse.types.ts, common.types.ts

- Create src/utils/ with utilities:
  - formatters.ts (formatCurrency, formatDate, etc.)
  - validators.ts (isValidEmail, isValidRFC, etc.)
  - constants.ts (status options, color maps)

- Create src/components/common/ with reusable components:
  - DataTable, Modal, ConfirmDialog
  - FormField (TextInput, SelectField, TextareaField)
  - StatusBadge, SearchInput, PageHeader
  - ActionButtons, LoadingSpinner, EmptyState

- Refactor pages to use common components:
  - FraccionamientosPage: 385 -> 285 lines
  - PresupuestosPage: 412 -> 297 lines
  - IncidentesPage: 741 -> 253 lines

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 09:02:57 -06:00

46 lines
964 B
TypeScript

/**
* LoadingSpinner - Reusable loading indicator component
*/
import clsx from 'clsx';
interface LoadingSpinnerProps {
size?: 'sm' | 'md' | 'lg';
className?: string;
}
const sizeClasses = {
sm: 'w-4 h-4',
md: 'w-8 h-8',
lg: 'w-12 h-12',
};
export function LoadingSpinner({ size = 'md', className }: LoadingSpinnerProps) {
return (
<div
className={clsx(
'animate-spin rounded-full border-2 border-gray-300 border-t-blue-600',
sizeClasses[size],
className
)}
role="status"
aria-label="Cargando"
>
<span className="sr-only">Cargando...</span>
</div>
);
}
interface LoadingOverlayProps {
message?: string;
}
export function LoadingOverlay({ message = 'Cargando...' }: LoadingOverlayProps) {
return (
<div className="flex flex-col items-center justify-center p-8">
<LoadingSpinner size="lg" />
<p className="mt-4 text-gray-500">{message}</p>
</div>
);
}