Widget komponenti

This commit is contained in:
Sedat ÖZTÜRK 2025-09-12 08:53:05 +03:00
parent 0f12b2e030
commit 391428d874
2 changed files with 1703 additions and 0 deletions

View file

@ -0,0 +1,90 @@
import { useEffect, useState } from 'react'
import classNames from 'classnames'
import { iconList } from './iconList'
export type colorType =
| 'blue'
| 'green'
| 'purple'
| 'gray'
| 'red'
| 'yellow'
| 'pink'
| 'indigo'
| 'teal'
| 'orange'
interface WidgetProps {
title: string
value: string | number
valueClassName?: string
color: colorType
icon: (typeof iconList)[number]
subtitle?: string
onClick?: () => void
className?: string
}
export default function Widget({
title,
value,
valueClassName = 'text-3xl',
color,
icon,
subtitle,
onClick,
className,
}: WidgetProps) {
const [IconComponent, setIconComponent] = useState<React.ComponentType<{
className?: string
}> | null>(null)
useEffect(() => {
let isMounted = true
import('react-icons/fa').then((icons) => {
if (isMounted && icon in icons) {
setIconComponent(() => (icons as any)[icon])
}
})
return () => {
isMounted = false
}
}, [icon])
const colorMap: Record<string, { bg: string; text: string }> = {
blue: { bg: 'from-blue-100 to-blue-200', text: 'text-blue-600' },
green: { bg: 'from-green-100 to-green-200', text: 'text-green-600' },
purple: { bg: 'from-purple-100 to-purple-200', text: 'text-purple-600' },
gray: { bg: 'from-gray-100 to-gray-200', text: 'text-gray-600' },
red: { bg: 'from-red-100 to-red-200', text: 'text-red-600' },
yellow: { bg: 'from-yellow-100 to-yellow-200', text: 'text-yellow-600' },
pink: { bg: 'from-pink-100 to-pink-200', text: 'text-pink-600' },
indigo: { bg: 'from-indigo-100 to-indigo-200', text: 'text-indigo-600' },
teal: { bg: 'from-teal-100 to-teal-200', text: 'text-teal-600' },
orange: { bg: 'from-orange-100 to-orange-200', text: 'text-orange-600' },
}
return (
<div
onClick={onClick}
className={classNames(
'bg-white rounded-lg shadow-sm border border-gray-200 p-4 flex flex-col justify-between',
onClick && 'cursor-pointer hover:bg-gray-50',
className,
)}
>
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-semibold text-gray-600 uppercase tracking-wide">{title}</p>
<p className={`${valueClassName} font-bold mt-1 ${colorMap[color].text}`}>{value}</p>
{subtitle && <p className="text-sm text-gray-500 mt-1">{subtitle}</p>}
</div>
<div
className={`w-12 h-12 bg-gradient-to-br ${colorMap[color].bg} rounded-xl flex items-center justify-center shadow-sm`}
>
{IconComponent ? <IconComponent className={`w-6 h-6 ${colorMap[color].text}`} /> : null}
</div>
</div>
</div>
)
}

File diff suppressed because it is too large Load diff