175 lines
5.2 KiB
TypeScript
175 lines
5.2 KiB
TypeScript
import React from 'react'
|
||
import { Box } from '@chakra-ui/react'
|
||
import { motion } from 'framer-motion'
|
||
import { User, Reaction } from '../../__data__/model'
|
||
import { UserCard } from '../user-card'
|
||
import { StudentCardBack } from './StudentCardBack'
|
||
import { useColorMode } from '@chakra-ui/react'
|
||
|
||
// Компонент маленькой батарейки для отображения в углу карточки
|
||
const BatteryIndicator: React.FC<{
|
||
student: User & { present?: boolean; recentlyPresent?: boolean }
|
||
}> = ({ student }) => {
|
||
const { colorMode } = useColorMode();
|
||
|
||
// Та же логика из StudentCardBack для определения уровня батареи
|
||
const getAttendanceLevel = () => {
|
||
if (student.present) {
|
||
return student.recentlyPresent ? 4 : 5;
|
||
}
|
||
|
||
const id = student.sub || '';
|
||
const idSum = id.split('').reduce((sum, char) => sum + char.charCodeAt(0), 0);
|
||
return Math.min(3, Math.floor((idSum % 100) / 25));
|
||
}
|
||
|
||
const batteryLevel = getAttendanceLevel();
|
||
|
||
// Цвета для разных уровней заряда батареи
|
||
const colors = [
|
||
// Empty (0)
|
||
{ primary: colorMode === "light" ? "#E53E3E" : "#F56565" },
|
||
// Very Low (1)
|
||
{ primary: colorMode === "light" ? "#DD6B20" : "#ED8936" },
|
||
// Low (2)
|
||
{ primary: colorMode === "light" ? "#D69E2E" : "#ECC94B" },
|
||
// Medium (3)
|
||
{ primary: colorMode === "light" ? "#38B2AC" : "#4FD1C5" },
|
||
// Good (4)
|
||
{ primary: colorMode === "light" ? "#3182CE" : "#4299E1" },
|
||
// Excellent (5)
|
||
{ primary: colorMode === "light" ? "#38A169" : "#48BB78" }
|
||
];
|
||
|
||
const color = colors[batteryLevel].primary;
|
||
|
||
// Функция для определения заполненности сегментов
|
||
const getSegmentFill = (segmentIndex: number) => {
|
||
return segmentIndex <= batteryLevel ? color : 'transparent';
|
||
}
|
||
|
||
return (
|
||
<Box
|
||
position="absolute"
|
||
top="8px"
|
||
right="8px"
|
||
width="20px"
|
||
height="10px"
|
||
zIndex="1"
|
||
>
|
||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
{/* Battery outline */}
|
||
<rect
|
||
x="2"
|
||
y="6"
|
||
width="18"
|
||
height="12"
|
||
rx="2"
|
||
stroke={color}
|
||
strokeWidth="1.5"
|
||
fill="transparent"
|
||
/>
|
||
{/* Battery cap */}
|
||
<path
|
||
d="M20 10H22V14H20V10Z"
|
||
fill={color}
|
||
/>
|
||
|
||
{/* Battery segments */}
|
||
<rect x="4" y="8" width="2.3" height="8" rx="1" fill={getSegmentFill(0)} />
|
||
<rect x="7" y="8" width="2.3" height="8" rx="1" fill={getSegmentFill(1)} />
|
||
<rect x="10" y="8" width="2.3" height="8" rx="1" fill={getSegmentFill(2)} />
|
||
<rect x="13" y="8" width="2.3" height="8" rx="1" fill={getSegmentFill(3)} />
|
||
<rect x="16" y="8" width="2.3" height="8" rx="1" fill={getSegmentFill(4)} />
|
||
|
||
{/* Lightning icon if recently joined or fully charged */}
|
||
{(student.recentlyPresent || batteryLevel === 5) && (
|
||
<path
|
||
d="M11.5 7.5L8 12H11L9.5 16.5L13 12H10L11.5 7.5Z"
|
||
fill={color}
|
||
stroke={color}
|
||
strokeWidth="0.3"
|
||
opacity="0.9"
|
||
/>
|
||
)}
|
||
</svg>
|
||
</Box>
|
||
);
|
||
};
|
||
|
||
interface StudentCardProps {
|
||
student: User & { present?: boolean; recentlyPresent?: boolean }
|
||
onAddUser: (user: User) => void
|
||
reaction?: Reaction
|
||
}
|
||
|
||
export const StudentCard: React.FC<StudentCardProps> = ({
|
||
student,
|
||
onAddUser,
|
||
reaction
|
||
}) => {
|
||
return (
|
||
<motion.li
|
||
key={student.sub}
|
||
animate={{
|
||
rotateY: student.present ? 0 : 180,
|
||
boxShadow: student.recentlyPresent
|
||
? ['0 0 0 0 rgba(66, 153, 225, 0)', '0 0 20px 5px rgba(66, 153, 225, 0.7)', '0 0 0 0 rgba(66, 153, 225, 0)']
|
||
: '0 0 0 0 rgba(0, 0, 0, 0)'
|
||
}}
|
||
transition={{
|
||
rotateY: { type: "spring", stiffness: 300, damping: 20 },
|
||
boxShadow: {
|
||
repeat: student.recentlyPresent ? 3 : 0,
|
||
duration: 1.5
|
||
}
|
||
}}
|
||
style={{
|
||
transformStyle: "preserve-3d",
|
||
perspective: "1000px",
|
||
aspectRatio: "1",
|
||
width: "100%",
|
||
display: "block"
|
||
}}
|
||
>
|
||
{/* Container for 3D effect */}
|
||
<Box
|
||
position="relative"
|
||
width="100%"
|
||
height="100%"
|
||
style={{
|
||
transformStyle: "preserve-3d"
|
||
}}
|
||
>
|
||
{/* Front side - visible when present */}
|
||
<Box
|
||
position="absolute"
|
||
top="0"
|
||
left="0"
|
||
width="100%"
|
||
height="100%"
|
||
style={{
|
||
backfaceVisibility: "hidden",
|
||
transform: "rotateY(0deg)",
|
||
zIndex: student.present ? 1 : 0
|
||
}}
|
||
>
|
||
<UserCard
|
||
wrapperAS="div"
|
||
student={student}
|
||
present={student.present}
|
||
recentlyPresent={student.recentlyPresent}
|
||
onAddUser={onAddUser}
|
||
reaction={reaction}
|
||
/>
|
||
|
||
{/* Battery indicator in corner */}
|
||
<BatteryIndicator student={student} />
|
||
</Box>
|
||
|
||
{/* Back side */}
|
||
<StudentCardBack student={student} />
|
||
</Box>
|
||
</motion.li>
|
||
)
|
||
}
|