Widget komponenti
This commit is contained in:
parent
0f12b2e030
commit
391428d874
2 changed files with 1703 additions and 0 deletions
90
ui/src/components/ui/Widget/Widget.tsx
Normal file
90
ui/src/components/ui/Widget/Widget.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
1613
ui/src/components/ui/Widget/iconList.ts
Normal file
1613
ui/src/components/ui/Widget/iconList.ts
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue