diff --git a/package-lock.json b/package-lock.json index 4bfdf86..f9f2475 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2894,12 +2894,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.13.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz", - "integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==", + "version": "22.14.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz", + "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==", "license": "MIT", "dependencies": { - "undici-types": "~6.20.0" + "undici-types": "~6.21.0" } }, "node_modules/@types/parse-json": { @@ -2925,9 +2925,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", - "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "version": "18.3.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.6.tgz", + "integrity": "sha512-nf22//wEbKXusP6E9pfOCDwFdHAX4u172eaJI4YkDRQEZiorm6KfYnSC2SWLDMVWUOWPERmJnN0ujeAfTBLvrw==", "license": "MIT", "peerDependencies": { "@types/react": "^18.0.0" @@ -4024,9 +4024,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001707", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz", - "integrity": "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==", + "version": "1.0.30001709", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001709.tgz", + "integrity": "sha512-NgL3vUTnDrPCZ3zTahp4fsugQ4dc7EKTSzwQDPEel6DMoMnfH2jhry9n2Zm8onbSR+f/QtKHFOA+iAQu4kbtWA==", "funding": [ { "type": "opencollective", @@ -4943,9 +4943,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.128", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.128.tgz", - "integrity": "sha512-bo1A4HH/NS522Ws0QNFIzyPcyUUNV/yyy70Ho1xqfGYzPUme2F/xr4tlEOuM6/A538U1vDA7a4XfCd1CKRegKQ==", + "version": "1.5.130", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.130.tgz", + "integrity": "sha512-Ou2u7L9j2XLZbhqzyX0jWDj6gA8D3jIfVzt4rikLf3cGBa0VdReuFimBKS9tQJA4+XpeCxj1NoWlfBXzbMa9IA==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -5255,9 +5255,9 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.37.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", - "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, "license": "MIT", "dependencies": { @@ -5271,7 +5271,7 @@ "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.8", + "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", @@ -6134,14 +6134,14 @@ } }, "node_modules/framer-motion": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.6.2.tgz", - "integrity": "sha512-7LgPRlPs5aG8UxeZiMCMZz8firC53+2+9TnWV22tuSi38D3IFRxHRUqOREKckAkt6ztX+Dn6weLcatQilJTMcg==", + "version": "12.6.3", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.6.3.tgz", + "integrity": "sha512-2hsqknz23aloK85bzMc9nSR2/JP+fValQ459ZTVElFQ0xgwR2YqNjYSuDZdFBPOwVCt4Q9jgyTt6hg6sVOALzw==", "license": "MIT", "peer": true, "dependencies": { - "motion-dom": "^12.6.1", - "motion-utils": "^12.5.0", + "motion-dom": "^12.6.3", + "motion-utils": "^12.6.3", "tslib": "^2.4.0" }, "peerDependencies": { @@ -6677,9 +6677,9 @@ } }, "node_modules/html-entities": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.3.tgz", - "integrity": "sha512-D3AfvN7SjhTgBSA8L1BN4FpPzuEd06uy4lHwSoRWr0lndi9BKaNzPLKGOWZ2ocSGguozr08TTb2jhCLHaemruw==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", "funding": [ { "type": "github", @@ -7856,19 +7856,19 @@ } }, "node_modules/motion-dom": { - "version": "12.6.1", - "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.6.1.tgz", - "integrity": "sha512-8XVsriTUEVOepoIDgE/LDGdg7qaKXWdt+wQA/8z0p8YzJDLYL8gbimZ3YkCLlj7bB2i/4UBD/g+VO7y9ZY0zHQ==", + "version": "12.6.3", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.6.3.tgz", + "integrity": "sha512-gRY08RjcnzgFYLemUZ1lo/e9RkBxR+6d4BRvoeZDSeArG4XQXERSPapKl3LNQRu22Sndjf1h+iavgY0O4NrYqA==", "license": "MIT", "peer": true, "dependencies": { - "motion-utils": "^12.5.0" + "motion-utils": "^12.6.3" } }, "node_modules/motion-utils": { - "version": "12.5.0", - "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.5.0.tgz", - "integrity": "sha512-+hFFzvimn0sBMP9iPxBa9OtRX35ZQ3py0UHnb8U29VD+d8lQ8zH3dTygJWqK7av2v6yhg7scj9iZuvTS0f4+SA==", + "version": "12.6.3", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.6.3.tgz", + "integrity": "sha512-R/b3Ia2VxtTNZ4LTEO5pKYau1OUNHOuUfxuP0WFCTDYdHkeTBR9UtxR1cc8mDmKr8PEhmmfnTKGz3rSMjNRoRg==", "license": "MIT", "peer": true }, @@ -8929,9 +8929,9 @@ } }, "node_modules/react-hook-form": { - "version": "7.54.2", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.2.tgz", - "integrity": "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==", + "version": "7.55.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.55.0.tgz", + "integrity": "sha512-XRnjsH3GVMQz1moZTW53MxfoWN7aDpUg/GpVNc4A3eXRVNdGXfbzJ4vM4aLQ8g6XCUh1nIbx70aaNCl7kxnjog==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -10534,9 +10534,9 @@ } }, "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -10694,9 +10694,9 @@ } }, "node_modules/use-sync-external-store": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", - "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" diff --git a/src/components/lesson/StudentCardBack.tsx b/src/components/lesson/StudentCardBack.tsx index aef849e..f9db155 100644 --- a/src/components/lesson/StudentCardBack.tsx +++ b/src/components/lesson/StudentCardBack.tsx @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next' import { User } from '../../__data__/model' import { AddMissedButton } from './sstyle' import { AddIcon } from '@chakra-ui/icons' +import { UserCard } from '../user-card' interface StudentCardBackProps { student: User & { present?: boolean; recentlyPresent?: boolean } @@ -14,139 +15,33 @@ export const StudentCardBack: React.FC = ({ student, onAdd const { colorMode } = useColorMode() const { t } = useTranslation() - // Determine attendance level based on student data - // We simulate different battery levels based on student presence - const getAttendanceLevel = () => { - // Using a scale of 0-5 for battery levels (6 segments total) - // In a real scenario, you would calculate this based on attendance history - - // If student is present, show higher battery level - if (student.present) { - return student.recentlyPresent ? 4 : 5; // Full or almost full if present - } - - // For absent students, randomly assign a lower battery level (0-3) - // In a real implementation, this would come from attendance history + // Веселые градиентные цвета + const colors = { + gradient: colorMode === 'light' + ? 'linear-gradient(135deg, #F6FFDE, #E3F2C1, #C9DBB2)' + : 'linear-gradient(135deg, #0D1282, #2F58CD, #3795BD)', + text: colorMode === 'light' ? "#2E7D32" : "#81C784" + } + + // Is the student marked as present? + const isPresent = !!student.present; + + // Функция для генерации случайного, но стабильного положения пузырьков для каждого студента + const getBubbleStyle = (index: number) => { + // Используем ID студента для получения стабильных, но уникальных чисел const id = student.sub || ''; - // Use the student ID hash to deterministically assign a battery level - // This creates a pseudo-random but consistent level for each student - const idSum = id.split('').reduce((sum, char) => sum + char.charCodeAt(0), 0); - return Math.min(3, Math.floor((idSum % 100) / 25)); - } - - const batteryLevel = getAttendanceLevel(); - - // Check if student recently joined the class - const isRecentlyJoined = !!student.recentlyPresent; - - // Get color scheme based on battery level - const getBatteryColors = () => { - // Colors for different battery levels - const colorSchemes = [ - // Empty (0) - { - light: { - primary: "#E53E3E", // red.500 - secondary: "#FED7D7", // red.100 - accent: "#C53030", // red.700 - text: "journal.pl.lesson.veryPoorAttendance" - }, - dark: { - primary: "#F56565", // red.400 - secondary: "#9B2C2C", // red.800 - accent: "#FEB2B2", // red.200 - text: "journal.pl.lesson.veryPoorAttendance" - } - }, - // Very Low (1) - { - light: { - primary: "#DD6B20", // orange.500 - secondary: "#FEEBC8", // orange.100 - accent: "#C05621", // orange.700 - text: "journal.pl.lesson.poorAttendance" - }, - dark: { - primary: "#ED8936", // orange.400 - secondary: "#9C4221", // orange.800 - accent: "#FBD38D", // orange.200 - text: "journal.pl.lesson.poorAttendance" - } - }, - // Low (2) - { - light: { - primary: "#D69E2E", // yellow.500 - secondary: "#FEFCBF", // yellow.100 - accent: "#B7791F", // yellow.700 - text: "journal.pl.lesson.lowAttendance" - }, - dark: { - primary: "#ECC94B", // yellow.400 - secondary: "#975A16", // yellow.800 - accent: "#F6E05E", // yellow.200 - text: "journal.pl.lesson.lowAttendance" - } - }, - // Medium (3) - { - light: { - primary: "#38B2AC", // teal.500 - secondary: "#B2F5EA", // teal.100 - accent: "#285E61", // teal.700 - text: "journal.pl.lesson.mediumAttendance" - }, - dark: { - primary: "#4FD1C5", // teal.400 - secondary: "#234E52", // teal.800 - accent: "#81E6D9", // teal.200 - text: "journal.pl.lesson.mediumAttendance" - } - }, - // Good (4) - { - light: { - primary: "#3182CE", // blue.500 - secondary: "#BEE3F8", // blue.100 - accent: "#2C5282", // blue.700 - text: "journal.pl.lesson.goodAttendance" - }, - dark: { - primary: "#4299E1", // blue.400 - secondary: "#2A4365", // blue.800 - accent: "#90CDF4", // blue.200 - text: "journal.pl.lesson.goodAttendance" - } - }, - // Excellent (5) - { - light: { - primary: "#38A169", // green.500 - secondary: "#C6F6D5", // green.100 - accent: "#276749", // green.700 - text: "journal.pl.lesson.excellentAttendance" - }, - dark: { - primary: "#48BB78", // green.400 - secondary: "#22543D", // green.800 - accent: "#9AE6B4", // green.200 - text: "journal.pl.lesson.excellentAttendance" - } - } - ]; + const charSum = id.split('').reduce((sum, char, i) => sum + char.charCodeAt(0) * (i + 1), 0); - const scheme = colorSchemes[batteryLevel]; - return scheme[colorMode === 'light' ? 'light' : 'dark']; - } - - const colors = getBatteryColors(); - - // Function to determine which battery segments to fill based on level - const getSegmentFill = (segmentIndex: number) => { - const isActive = segmentIndex <= batteryLevel; + // Разные значения для разных пузырей + const seed = (charSum + index * 137) % 100; - return isActive ? colors.primary : 'transparent'; - } + return { + left: `${(seed % 80) + 10}%`, + top: `${((seed * 1.5) % 80) + 10}%`, + size: `${(seed % 15) + 10}px`, + duration: `${(seed % 4) + 8}s` + }; + }; return ( = ({ student, onAdd aspectRatio: "1" }} > - onAddUser(student)} aria-label={t('journal.pl.common.add')}> + {/* Add button */} + {!isPresent && ( + onAddUser?.(student)} + aria-label={t('journal.pl.common.add')} + > + )} + + {/* Веселый фон с градиентом */} - { + const style = getBubbleStyle(i); + return ( + + ); + })} + + {/* Content */} + + {/* Gravatar с весёлой анимацией */} - {isRecentlyJoined && ( - + - )} - {/* Battery icon with 6 segments */} - - {/* Battery outline */} - - {/* Battery cap */} - - - {/* Battery segments - from lowest to highest */} - - - - - - - {/* Lightning icon if recently joined or fully charged */} - {(isRecentlyJoined || batteryLevel === 5) && ( - - )} - + - + + {/* Student name */} + {student.name || student.preferred_username} + + {/* Status с эмодзи */} - {isRecentlyJoined - ? t('journal.pl.lesson.recentlyJoined') - : t(colors.text || 'journal.pl.lesson.attendance')} + 🔔 + {t('journal.pl.lesson.notMarked')} - + ) } \ No newline at end of file diff --git a/src/components/user-card/style.ts b/src/components/user-card/style.ts index 49d2219..1bda4a9 100644 --- a/src/components/user-card/style.ts +++ b/src/components/user-card/style.ts @@ -109,7 +109,7 @@ export const Wrapper = styled.div<{ warn?: boolean; width?: string | number; pos props.warn ? css` opacity: 0.7; - filter: grayscale(0.8); + filter: grayscale(0.3); ` : ''} `