diff --git a/frontend/src/components/index.ts b/frontend/src/components/index.ts index 62afa00..e7c6699 100644 --- a/frontend/src/components/index.ts +++ b/frontend/src/components/index.ts @@ -11,6 +11,11 @@ export type { BreadcrumbProps, BreadcrumbItem } from './common/Breadcrumb'; // Reusable UI primitives export { default as Button } from './Button'; +// UI +export { ProgressBar } from './ui/ProgressBar'; +export type { ProgressBarProps, ProgressBarColor, ProgressBarSize } from './ui/ProgressBar'; +export { CircularProgress } from './ui/CircularProgress'; +export type { CircularProgressProps } from './ui/CircularProgress'; export type { ButtonProps, ButtonVariant, ButtonSize } from './Button'; export { default as Card, CardHeader, CardBody, CardFooter } from './Card'; diff --git a/frontend/src/components/ui/CircularProgress/CircularProgress.tsx b/frontend/src/components/ui/CircularProgress/CircularProgress.tsx new file mode 100644 index 0000000..3fcaaa0 --- /dev/null +++ b/frontend/src/components/ui/CircularProgress/CircularProgress.tsx @@ -0,0 +1,83 @@ +import React from 'react'; + +export interface CircularProgressProps { + value: number; + size?: number; + strokeWidth?: number; + label?: string; + className?: string; +} + +export const CircularProgress: React.FC = ({ + value, + size = 80, + strokeWidth = 6, + label, + className = '', +}) => { + const clamped = Math.min(100, Math.max(0, value)); + const radius = (size - strokeWidth) / 2; + const circumference = 2 * Math.PI * radius; + const offset = circumference - (clamped / 100) * circumference; + const center = size / 2; + + return ( +
+ + {label !== undefined && ( + {label} + )} +
+ ); +}; + +export default CircularProgress; diff --git a/frontend/src/components/ui/CircularProgress/index.ts b/frontend/src/components/ui/CircularProgress/index.ts new file mode 100644 index 0000000..4ab317f --- /dev/null +++ b/frontend/src/components/ui/CircularProgress/index.ts @@ -0,0 +1,2 @@ +export { default, CircularProgress } from './CircularProgress'; +export type { CircularProgressProps } from './CircularProgress'; diff --git a/frontend/src/components/ui/ProgressBar/ProgressBar.tsx b/frontend/src/components/ui/ProgressBar/ProgressBar.tsx new file mode 100644 index 0000000..f39eb34 --- /dev/null +++ b/frontend/src/components/ui/ProgressBar/ProgressBar.tsx @@ -0,0 +1,68 @@ +import React from 'react'; + +export type ProgressBarColor = 'blue' | 'green' | 'yellow' | 'red'; +export type ProgressBarSize = 'sm' | 'md' | 'lg'; + +export interface ProgressBarProps { + value: number; + label?: string; + color?: ProgressBarColor; + size?: ProgressBarSize; + className?: string; +} + +const colorStyles: Record = { + blue: 'bg-accent-blue', + green: 'bg-accent-green', + yellow: 'bg-yellow-400', + red: 'bg-accent-red', +}; + +const trackStyles: Record = { + blue: 'bg-accent-blue/20', + green: 'bg-accent-green/20', + yellow: 'bg-yellow-400/20', + red: 'bg-accent-red/20', +}; + +const sizeStyles: Record = { + sm: 'h-1', + md: 'h-2', + lg: 'h-3', +}; + +export const ProgressBar: React.FC = ({ + value, + label, + color = 'blue', + size = 'md', + className = '', +}) => { + const clamped = Math.min(100, Math.max(0, value)); + + return ( +
+ {label !== undefined && ( +
+ {label} + {clamped}% +
+ )} +
+
+
+
+ ); +}; + +export default ProgressBar; diff --git a/frontend/src/components/ui/ProgressBar/index.ts b/frontend/src/components/ui/ProgressBar/index.ts new file mode 100644 index 0000000..999f9d7 --- /dev/null +++ b/frontend/src/components/ui/ProgressBar/index.ts @@ -0,0 +1,2 @@ +export { default, ProgressBar } from './ProgressBar'; +export type { ProgressBarProps, ProgressBarColor, ProgressBarSize } from './ProgressBar';