Sergei 4b60a92777 Add basic UI components for web application
- Set up Tailwind CSS configuration for styling
- Create Button component with variants (primary, secondary, outline, ghost, danger)
- Create Input component with label, error, and helper text support
- Create Card component with composable subcomponents (Header, Title, Description, Content, Footer)
- Create LoadingSpinner component with size and fullscreen options
- Create ErrorMessage component with retry and dismiss actions
- Add comprehensive test suite using Jest and React Testing Library
- Configure ESLint and Jest for quality assurance

All components follow consistent design patterns from mobile app
and include proper TypeScript types and accessibility features.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-31 18:11:31 -08:00

107 lines
2.2 KiB
TypeScript

import React from 'react';
interface CardProps {
children: React.ReactNode;
className?: string;
padding?: 'none' | 'sm' | 'md' | 'lg';
hover?: boolean;
onClick?: () => void;
}
export function Card({
children,
className = '',
padding = 'md',
hover = false,
onClick,
}: CardProps) {
const baseClasses = 'bg-white rounded-lg border border-gray-200 shadow-sm';
const paddingClasses = {
none: '',
sm: 'p-3',
md: 'p-4',
lg: 'p-6',
};
const hoverClasses = hover
? 'hover:shadow-md transition-shadow cursor-pointer'
: '';
const interactiveClasses = onClick ? 'cursor-pointer' : '';
const classes = `${baseClasses} ${paddingClasses[padding]} ${hoverClasses} ${interactiveClasses} ${className}`;
if (onClick) {
return (
<div className={classes} onClick={onClick} role="button" tabIndex={0}>
{children}
</div>
);
}
return <div className={classes}>{children}</div>;
}
interface CardHeaderProps {
children: React.ReactNode;
className?: string;
}
export function CardHeader({ children, className = '' }: CardHeaderProps) {
return (
<div className={`border-b border-gray-200 pb-3 mb-3 ${className}`}>
{children}
</div>
);
}
interface CardTitleProps {
children: React.ReactNode;
className?: string;
}
export function CardTitle({ children, className = '' }: CardTitleProps) {
return (
<h3 className={`text-lg font-semibold text-textPrimary ${className}`}>
{children}
</h3>
);
}
interface CardDescriptionProps {
children: React.ReactNode;
className?: string;
}
export function CardDescription({
children,
className = '',
}: CardDescriptionProps) {
return (
<p className={`text-sm text-textSecondary mt-1 ${className}`}>{children}</p>
);
}
interface CardContentProps {
children: React.ReactNode;
className?: string;
}
export function CardContent({ children, className = '' }: CardContentProps) {
return <div className={className}>{children}</div>;
}
interface CardFooterProps {
children: React.ReactNode;
className?: string;
}
export function CardFooter({ children, className = '' }: CardFooterProps) {
return (
<div className={`border-t border-gray-200 pt-3 mt-3 ${className}`}>
{children}
</div>
);
}