Fixed the training content view

This commit is contained in:
Carlos Mesquita
2024-08-05 10:41:11 +01:00
parent 0f47a8af70
commit 309dfba583
11 changed files with 906 additions and 284 deletions

643
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -25,6 +25,7 @@
"@types/node": "18.13.0",
"@types/react": "18.0.27",
"@types/react-dom": "18.0.10",
"@use-gesture/react": "^10.3.1",
"axios": "^1.3.5",
"bcrypt": "^5.1.1",
"chart.js": "^4.2.1",
@@ -67,14 +68,12 @@
"react-phone-number-input": "^3.3.6",
"react-player": "^2.12.0",
"react-select": "^5.7.5",
"react-slick": "^0.30.2",
"react-string-replace": "^1.1.0",
"react-toastify": "^9.1.2",
"react-tooltip": "^5.27.1",
"react-xarrows": "^2.0.2",
"read-excel-file": "^5.7.1",
"short-unique-id": "5.0.2",
"slick-carousel": "^1.8.1",
"stripe": "^13.10.0",
"swr": "^2.1.3",
"tailwind-scrollbar-hide": "^1.1.7",
@@ -94,7 +93,6 @@
"@types/qrcode": "^1.5.5",
"@types/react-csv": "^1.1.10",
"@types/react-datepicker": "^4.15.1",
"@types/react-slick": "^0.23.13",
"@types/uuid": "^9.0.1",
"@types/wavesurfer.js": "^6.0.6",
"@wixc3/react-board": "^2.2.0",

View File

@@ -2,7 +2,7 @@ import React, { useState, ReactNode, useRef, useEffect } from 'react';
import { animated, useSpring } from '@react-spring/web';
interface DropdownProps {
title: string;
title: ReactNode;
open?: boolean;
className?: string;
contentWrapperClassName?: string;

View File

@@ -0,0 +1,168 @@
import React, { useRef, useEffect, useState, useCallback, ReactNode } from 'react';
import { useSpring, animated } from '@react-spring/web';
import { useDrag } from '@use-gesture/react';
import clsx from 'clsx';
interface InfiniteCarouselProps {
children: React.ReactNode;
height: string;
speed?: number;
gap?: number;
overlay?: ReactNode;
overlayFunc?: (index: number) => void;
overlayClassName?: string;
}
const InfiniteCarousel: React.FC<InfiniteCarouselProps> = ({
children,
height,
speed = 20000,
gap = 16,
overlay = undefined,
overlayFunc = undefined,
overlayClassName = ""
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const [containerWidth, setContainerWidth] = useState<number>(0);
const itemCount = React.Children.count(children);
const [isDragging, setIsDragging] = useState<boolean>(false);
const [itemWidth, setItemWidth] = useState<number>(0);
const [isInfinite, setIsInfinite] = useState<boolean>(true);
const dragStartX = useRef<number>(0);
useEffect(() => {
const handleResize = () => {
if (containerRef.current) {
const containerWidth = containerRef.current.clientWidth;
setContainerWidth(containerWidth);
const firstChild = containerRef.current.firstElementChild?.firstElementChild as HTMLElement;
if (firstChild) {
const childWidth = firstChild.offsetWidth;
setItemWidth(childWidth);
const totalContentWidth = (childWidth + gap) * itemCount - gap;
setIsInfinite(totalContentWidth > containerWidth);
}
}
};
handleResize();
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, [gap, itemCount]);
const totalWidth = (itemWidth + gap) * itemCount;
const [{ x }, api] = useSpring(() => ({
from: { x: 0 },
to: { x: -totalWidth },
config: { duration: speed },
loop: true,
}));
const startAnimation = useCallback(() => {
if (isInfinite) {
api.start({
from: { x: x.get() },
to: { x: x.get() - totalWidth },
config: { duration: speed },
loop: true,
});
} else {
api.stop();
api.start({ x: 0, immediate: true });
}
}, [api, x, totalWidth, speed, isInfinite]);
useEffect(() => {
if (containerWidth > 0 && !isDragging) {
startAnimation();
}
}, [containerWidth, isDragging, startAnimation]);
const bind = useDrag(({ down, movement: [mx], first }) => {
if (!isInfinite) return;
if (first) {
setIsDragging(true);
api.stop();
dragStartX.current = x.get();
}
if (down) {
let newX = dragStartX.current + mx;
newX = ((newX % totalWidth) + totalWidth) % totalWidth;
if (newX > 0) newX -= totalWidth;
api.start({ x: newX, immediate: true });
} else {
setIsDragging(false);
startAnimation();
}
}, {
filterTaps: true,
from: () => [x.get(), 0],
});
return (
<div
className="overflow-hidden relative select-none"
style={{ height, touchAction: 'pan-y' }}
ref={containerRef}
{...(isInfinite ? bind() : {})}
>
<animated.div
className="flex"
style={{
display: 'flex',
willChange: 'transform',
transform: isInfinite
? x.to((x) => `translate3d(${x}px, 0, 0)`)
: 'none',
gap: `${gap}px`,
width: 'fit-content',
}}
>
{React.Children.map(children, (child, i) => (
<div
key={i}
className="flex-shrink-0 relative"
>
{overlay !== undefined && overlayFunc !== undefined && (
<div className={clsx('absolute', overlayClassName)} onClick={() => overlayFunc(i)}>
{overlay}
</div>
)}
<div
className="select-none"
style={{ pointerEvents: 'none' }}
>
{child}
</div>
</div>
))}
{isInfinite && React.Children.map(children, (child, i) => (
<div
key={`clone-${i}`}
className="flex-shrink-0 relative"
>
{overlay !== undefined && overlayFunc !== undefined && (
<div className={clsx('absolute', overlayClassName)} onClick={() => overlayFunc(i)}>
{overlay}
</div>
)}
<div
className="select-none"
style={{ pointerEvents: 'none' }}
>
{child}
</div>
</div>
))}
</animated.div>
</div>
);
};
export default InfiniteCarousel;

View File

@@ -175,6 +175,9 @@ export default function Sidebar({path, navDisabled = false, focusMode = false, u
{checkAccess(user, getTypesOfUser(["agent"]), "viewRecords") && (
<Nav disabled={disableNavigation} Icon={BsClockHistory} label="Record" path={path} keyPath="/record" isMinimized={true} />
)}
{checkAccess(user, getTypesOfUser(["agent"]), "viewRecords") && (
<Nav disabled={disableNavigation} Icon={CiDumbbell} label="Training" path={path} keyPath="/training" isMinimized={true} />
)}
{checkAccess(user, getTypesOfUser(["student"])) && (
<Nav disabled={disableNavigation} Icon={BsShieldFill} label="Settings" path={path} keyPath="/settings" isMinimized={true} />
)}

View File

@@ -68,6 +68,9 @@ const aggregateScoresByModule = (stats: Stat[]): { module: Module; total: number
};
interface StatsGridItemProps {
width?: string | undefined;
height?: string | undefined;
examNumber?: number | undefined;
stats: Stat[];
timestamp: string | number;
user: User,
@@ -100,7 +103,10 @@ const StatsGridItem: React.FC<StatsGridItemProps> = ({
setSelectedModules,
setInactivity,
setTimeSpent,
renderPdfIcon
renderPdfIcon,
width = undefined,
height = undefined,
examNumber = undefined
}) => {
const router = useRouter();
const correct = stats.reduce((accumulator, current) => accumulator + current.score.correct, 0);
@@ -190,6 +196,8 @@ const StatsGridItem: React.FC<StatsGridItemProps> = ({
</span>
{renderPdfIcon(session, textColor, textColor)}
</div>
{examNumber === undefined ? (
<>
{aiUsage >= 50 && user.type !== "student" && (
<div className={clsx(
"ml-auto border px-1 rounded w-fit mr-1",
@@ -201,6 +209,12 @@ const StatsGridItem: React.FC<StatsGridItemProps> = ({
<span className="text-xs">AI Usage</span>
</div>
)}
</>
) : (
<div className='flex justify-end'>
<span className="font-semibold bg-gray-200 text-gray-800 px-2.5 py-0.5 rounded-full mt-0.5">{examNumber}</span>
</div>
)}
</div>
</div>
@@ -232,7 +246,10 @@ const StatsGridItem: React.FC<StatsGridItemProps> = ({
correct / total < 0.3 && "hover:border-mti-rose",
typeof selectedTrainingExams !== "undefined" && typeof timestamp === "string" && selectedTrainingExams.includes(timestamp) && "border-2 border-slate-600",
)}
onClick={selectExam}
style={{
...(width !== undefined && { width }),
...(height !== undefined && { height }),
}}
data-tip="This exam is still being evaluated..."
role="button">
{content}
@@ -246,6 +263,10 @@ const StatsGridItem: React.FC<StatsGridItemProps> = ({
correct / total < 0.3 && "hover:border-mti-rose",
)}
data-tip="Your screen size is too small to view previous exams."
style={{
...(width !== undefined && { width }),
...(height !== undefined && { height }),
}}
role="button">
{content}
</div>

View File

@@ -76,7 +76,7 @@ const TrainingScore: React.FC<TrainingScoreProps> = ({
</div>
</div>
{gridView && (
<div className="flex flex-col items-center justify-center gap-2">
<div className="flex flex-col items-center justify-center gap-2 -lg:hidden">
<div className="flex w-14 h-14 bg-[#F5F5F5] items-center justify-center rounded-xl border border-[#DBDBDB]">
<GiLightBulb color={"#FFCC00"} size={28} />
</div>

View File

@@ -24,6 +24,12 @@ import { usePDFDownload } from "@/hooks/usePDFDownload";
import useAssignments from '@/hooks/useAssignments';
import useUsers from '@/hooks/useUsers';
import Dropdown from "@/components/Dropdown";
import InfiniteCarousel from '@/components/InfiniteCarousel';
import { LuExternalLink } from "react-icons/lu";
import { uniqBy } from 'lodash';
import { getExamById } from '@/utils/exams';
import { convertToUserSolutions } from '@/utils/stats';
import { sortByModule } from '@/utils/moduleUtils';
export const getServerSideProps = withIronSessionSsr(({ req, res }) => {
const user = req.session.user;
@@ -68,12 +74,9 @@ const TrainingContent: React.FC<{ user: User }> = ({ user }) => {
const { assignments } = useAssignments({});
const { users } = useUsers();
const router = useRouter();
const { id } = router.query;
useEffect(() => {
const fetchTrainingContent = async () => {
if (!id || typeof id !== 'string') return;
@@ -118,6 +121,32 @@ const TrainingContent: React.FC<{ user: User }> = ({ user }) => {
setCurrentTipIndex((prevIndex) => (prevIndex - 1));
};
const goToExam = (examNumber: number) => {
const stats = trainingContent?.exams[examNumber].stats!;
const examPromises = uniqBy(stats, "exam").map((stat) => {
return getExamById(stat.module, stat.exam);
});
const { timeSpent, inactivity } = stats[0];
Promise.all(examPromises).then((exams) => {
if (exams.every((x) => !!x)) {
if (!!timeSpent) setTimeSpent(timeSpent);
if (!!inactivity) setInactivity(inactivity);
setUserSolutions(convertToUserSolutions(stats));
setShowSolutions(true);
setExams(exams.map((x) => x!).sort(sortByModule));
setSelectedModules(
exams
.map((x) => x!)
.sort(sortByModule)
.map((x) => x!.module),
);
router.push("/exercises");
}
});
}
return (
<>
<Head>
@@ -137,13 +166,25 @@ const TrainingContent: React.FC<{ user: User }> = ({ user }) => {
<span className="loading loading-infinity w-32 bg-mti-green-light" />
</div>
) : (trainingContent && (
<div className="flex flex-col gap-10">
<div className="flex h-screen flex-col gap-4">
<div className='flex flex-row h-[15%] gap-4'>
{/*<Carousel itemsPerFrame={4} itemsPerScroll={4}>*/}
<div className="flex flex-col gap-8">
<div className="flex flex-row items-center">
<span className="bg-gray-200 text-gray-800 px-3 py-0.5 rounded-full font-semibold text-lg mr-2">{trainingContent.exams.length}</span>
<span>Exams Selected</span>
</div>
<div className='h-[15vh] mb-4'>
<InfiniteCarousel height="150px"
overlay={
<LuExternalLink size={20} />
}
overlayFunc={goToExam}
overlayClassName='bottom-6 right-5 cursor-pointer'
>
{trainingContent.exams.map((exam, examIndex) => (
<StatsGridItem
key={`exam-${examIndex}`}
width='350px'
height='150px'
examNumber={examIndex + 1}
stats={exam.stats || []}
timestamp={exam.date}
user={user}
@@ -158,11 +199,11 @@ const TrainingContent: React.FC<{ user: User }> = ({ user }) => {
renderPdfIcon={renderPdfIcon}
/>
))}
{/* </Carousel> */}
</InfiniteCarousel>
</div>
<div className='flex flex-col h-[75%]' style={{ maxHeight: '85%' }}>
<div className='flex flex-row gap-10 -md:flex-col'>
<div className="rounded-3xl p-6 w-1/2 shadow-training-inset -md:w-full max-h-full">
<div className='flex flex-col'>
<div className='flex flex-row gap-10 -md:flex-col h-full'>
<div className="flex flex-col rounded-3xl p-6 w-1/2 shadow-training-inset -md:w-full max-h-full">
<div className="flex flex-row items-center mb-6 gap-1">
<MdOutlinePlaylistAddCheckCircle color={"#40A1EA"} size={26} />
<h2 className={`text-xl font-semibold text-[#40A1EA]`}>General Evaluation</h2>
@@ -183,11 +224,14 @@ const TrainingContent: React.FC<{ user: User }> = ({ user }) => {
</svg>
<h3 className="text-xl font-semibold text-[#40A1EA]">Performance Breakdown by Exam:</h3>
</div>
<ul>
<ul className='overflow-auto scrollbar-hide flex-grow'>
{trainingContent.exams.flatMap((exam, index) => (
<li key={index} className="flex flex-col mb-2 bg-[#22E1B30F] p-4 rounded-xl border">
<div className="flex flex-row font-semibold border-b-2 border-[#D9D9D929] text-[#22E1B3] mb-2">
<span className="border-r-2 border-[#D9D9D929] pr-2">Exam {index + 1}</span>
<div className='flex items-center border-r-2 border-[#D9D9D929] pr-2'>
<span className='mr-1'>Exam</span>
<span className="font-semibold bg-gray-200 text-gray-800 px-2 rounded-full text-sm">{index + 1}</span>
</div>
<span className="pl-2">{exam.score}%</span>
</div>
<div className="flex flex-row items-center gap-2">
@@ -198,39 +242,8 @@ const TrainingContent: React.FC<{ user: User }> = ({ user }) => {
))}
</ul>
</div>
<div className="rounded-3xl p-6 w-1/2 shadow-training-inset -md:w-full">
<div className="flex flex-row items-center mb-4 gap-1">
<MdOutlineSelfImprovement color={"#40A1EA"} size={24} />
<h2 className={`text-xl font-semibold text-[#40A1EA]`}>Subjects that Need Improvement</h2>
</div>
<div className="bg-[#FBFBFB] border rounded-xl p-4 max-h-[500px] overflow-y-auto scrollbar-hide">
<div className="flex flex-col rounded-3xl p-6 w-1/2 shadow-training-inset -md:w-full">
<div className='flex flex-col'>
<div className="flex flex-row items-center gap-1 mb-4">
<div className="flex items-center justify-center w-[48px] h-[48px]">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_112_445" maskUnits="userSpaceOnUse" x="0" y="0" width="24" height="24">
<rect width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_112_445)">
<path d="M6 17H11V15H6V17ZM16 17H18V15H16V17ZM6 13H11V11H6V13ZM16 13H18V7H16V13ZM6 9H11V7H6V9ZM4 21C3.45 21 2.97917 20.8042 2.5875 20.4125C2.19583 20.0208 2 19.55 2 19V5C2 4.45 2.19583 3.97917 2.5875 3.5875C2.97917 3.19583 3.45 3 4 3H20C20.55 3 21.0208 3.19583 21.4125 3.5875C21.8042 3.97917 22 4.45 22 5V19C22 19.55 21.8042 20.0208 21.4125 20.4125C21.0208 20.8042 20.55 21 20 21H4ZM4 19H20V5H4V19Z" fill="#1C1B1F" />
</g>
</svg>
</div>
<h3 className="text-lg font-semibold">Detailed Breakdown</h3>
</div>
<ul className="space-y-4 pb-2">
{trainingContent.exams.map((exam, index) => (
<li key={index} className="border rounded-lg bg-white">
<Dropdown title={`Exam ${index + 1}`}>
<span>{exam.detailed_summary}</span>
</Dropdown>
</li>
))}
</ul>
</div>
</div>
<div className="w-full h-px bg-[#D9D9D929] my-6"></div>
<div className="flex flex-row items-center mb-4 gap-1">
<AiOutlineFileSearch color="#40A1EA" size={24} />
<h3 className="text-xl font-semibold text-[#40A1EA]">Identified Weak Areas</h3>
@@ -238,7 +251,7 @@ const TrainingContent: React.FC<{ user: User }> = ({ user }) => {
<Tab.Group>
<div className="flex flex-col gap-4">
<Tab.List>
<div className="flex flex-row gap-6">
<div className="flex flex-row gap-6 overflow-x-auto pb-1 training-scrollbar">
{trainingContent.weak_areas.map((x, index) => (
<Tab
key={index}
@@ -268,10 +281,47 @@ const TrainingContent: React.FC<{ user: User }> = ({ user }) => {
</div>
</Tab.Group>
</div>
<div className="w-full h-px bg-[#D9D9D929] my-6"></div>
<div className="flex flex-row items-center mb-4 gap-1">
<MdOutlineSelfImprovement color={"#40A1EA"} size={24} />
<h2 className={`text-xl font-semibold text-[#40A1EA]`}>Subjects that Need Improvement</h2>
</div>
<div className="flex flex-grow bg-[#FBFBFB] border rounded-xl p-4">
<div className='flex flex-col'>
<div className="flex flex-row items-center gap-1 mb-4">
<div className="flex items-center justify-center w-[48px] h-[48px]">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_112_445" maskUnits="userSpaceOnUse" x="0" y="0" width="24" height="24">
<rect width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_112_445)">
<path d="M6 17H11V15H6V17ZM16 17H18V15H16V17ZM6 13H11V11H6V13ZM16 13H18V7H16V13ZM6 9H11V7H6V9ZM4 21C3.45 21 2.97917 20.8042 2.5875 20.4125C2.19583 20.0208 2 19.55 2 19V5C2 4.45 2.19583 3.97917 2.5875 3.5875C2.97917 3.19583 3.45 3 4 3H20C20.55 3 21.0208 3.19583 21.4125 3.5875C21.8042 3.97917 22 4.45 22 5V19C22 19.55 21.8042 20.0208 21.4125 20.4125C21.0208 20.8042 20.55 21 20 21H4ZM4 19H20V5H4V19Z" fill="#1C1B1F" />
</g>
</svg>
</div>
<h3 className="text-lg font-semibold">Detailed Breakdown</h3>
</div>
<ul className="flex flex-col flex-grow space-y-4 pb-2 max-h-[350px] overflow-y-auto scrollbar-hide">
{trainingContent.exams.map((exam, index) => (
<li key={index} className="border rounded-lg bg-white">
<Dropdown title={
<div className='flex flex-row items-center'>
<span className="mr-1">Exam</span>
<span className="font-semibold bg-gray-200 text-gray-800 px-2 rounded-full text-sm mt-0.5">{index + 1}</span>
</div>
} open={index == 0}>
<span>{exam.detailed_summary}</span>
</Dropdown>
</li>
))}
</ul>
</div>
</div>
</div>
<div className="flex">
</div>
</div>
<div className="flex -md:hidden">
<div className="rounded-3xl p-6 shadow-training-inset w-full">
<div className="flex flex-col p-10">
<Exercise key={currentTipIndex} {...trainingTips[currentTipIndex]} />
@@ -294,6 +344,7 @@ const TrainingContent: React.FC<{ user: User }> = ({ user }) => {
</Button>
</div>
</div>
</div>
</div>
))}

View File

@@ -389,7 +389,7 @@ const Training: React.FC<{ user: User }> = ({ user }) => {
</div>
)}
{groupedByTrainingContent && Object.keys(groupedByTrainingContent).length > 0 && (
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 w-full gap-4 xl:gap-6">
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-2 2xl:grid-cols-3 w-full gap-4 xl:gap-6">
{Object.keys(filterTrainingContentByDate(groupedByTrainingContent))
.sort((a, b) => parseInt(b) - parseInt(a))
.map(trainingContentContainer)}

View File

@@ -4,14 +4,35 @@
@layer utilities {
.scrollbar-hide {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none;
/* IE and Edge */
scrollbar-width: none;
/* Firefox */
}
.scrollbar-hide::-webkit-scrollbar {
display: none; /* Chrome, Safari and Opera */
display: none;
/* Chrome, Safari and Opera */
}
}
.training-scrollbar::-webkit-scrollbar {
@apply w-1.5;
}
.training-scrollbar::-webkit-scrollbar-track {
@apply bg-transparent;
}
.training-scrollbar::-webkit-scrollbar-thumb {
@apply bg-gray-400 hover:bg-gray-500 rounded-full transition-colors opacity-50 hover:opacity-75;
}
.training-scrollbar {
scrollbar-width: thin;
scrollbar-color: rgba(156, 163, 175, 0.5) transparent;
}
:root {
--max-width: 1100px;
--border-radius: 12px;

View File

@@ -711,6 +711,13 @@
node-fetch "2.6.7"
tslib "^2.1.0"
"@firebase/util@^1.9.7":
version "1.9.7"
resolved "https://registry.npmjs.org/@firebase/util/-/util-1.9.7.tgz"
integrity sha512-fBVNH/8bRbYjqlbIhZ+lBtdAAS4WqZumx03K06/u7fJSpz1TGjEMm1ImvKD47w+xaFKIP2ori6z8BrbakRfjJA==
dependencies:
tslib "^2.1.0"
"@firebase/util@1.9.3":
version "1.9.3"
resolved "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz"
@@ -718,13 +725,6 @@
dependencies:
tslib "^2.1.0"
"@firebase/util@^1.9.7":
version "1.9.7"
resolved "https://registry.yarnpkg.com/@firebase/util/-/util-1.9.7.tgz#c03b0ae065b3bba22800da0bd5314ef030848038"
integrity sha512-fBVNH/8bRbYjqlbIhZ+lBtdAAS4WqZumx03K06/u7fJSpz1TGjEMm1ImvKD47w+xaFKIP2ori6z8BrbakRfjJA==
dependencies:
tslib "^2.1.0"
"@firebase/webchannel-wrapper@0.9.0":
version "0.9.0"
resolved "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.9.0.tgz"
@@ -1591,13 +1591,6 @@
dependencies:
"@types/react" "*"
"@types/react-slick@^0.23.13":
version "0.23.13"
resolved "https://registry.npmjs.org/@types/react-slick/-/react-slick-0.23.13.tgz"
integrity sha512-bNZfDhe/L8t5OQzIyhrRhBr/61pfBcWaYJoq6UDqFtv5LMwfg4NsVDD2J8N01JqdAdxLjOt66OZEp6PX+dGs/A==
dependencies:
"@types/react" "*"
"@types/react-transition-group@^4.4.0", "@types/react-transition-group@^4.4.1":
version "4.4.5"
resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz"
@@ -1691,6 +1684,18 @@
"@typescript-eslint/types" "5.51.0"
eslint-visitor-keys "^3.3.0"
"@use-gesture/core@10.3.1":
version "10.3.1"
resolved "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz"
integrity sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==
"@use-gesture/react@^10.3.1":
version "10.3.1"
resolved "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz"
integrity sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==
dependencies:
"@use-gesture/core" "10.3.1"
"@wixc3/board-core@^2.2.0":
version "2.2.0"
resolved "https://registry.npmjs.org/@wixc3/board-core/-/board-core-2.2.0.tgz"
@@ -2165,7 +2170,7 @@ chownr@^2.0.0:
resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz"
integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.0, classnames@^2.5.1:
classnames@^2.2.6, classnames@^2.3.0, classnames@^2.5.1:
version "2.5.1"
resolved "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz"
integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
@@ -2666,11 +2671,6 @@ enhanced-resolve@^5.10.0:
graceful-fs "^4.2.4"
tapable "^2.2.0"
enquire.js@^2.1.6:
version "2.1.6"
resolved "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz"
integrity sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw==
ent@^2.2.0:
version "2.2.1"
resolved "https://registry.npmjs.org/ent/-/ent-2.2.1.tgz"
@@ -4135,13 +4135,6 @@ json-stable-stringify-without-jsonify@^1.0.1:
resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz"
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
json2mq@^0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz"
integrity sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==
dependencies:
string-convert "^0.2.0"
json5@^1.0.1:
version "1.0.2"
resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz"
@@ -4319,11 +4312,6 @@ lodash.clonedeep@^4.5.0:
resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz"
integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==
lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz"
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
lodash.includes@^4.3.0:
version "4.3.0"
resolved "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz"
@@ -5355,17 +5343,6 @@ react-select@^5.7.5:
react-transition-group "^4.3.0"
use-isomorphic-layout-effect "^1.1.2"
react-slick@^0.30.2:
version "0.30.2"
resolved "https://registry.npmjs.org/react-slick/-/react-slick-0.30.2.tgz"
integrity sha512-XvQJi7mRHuiU3b9irsqS9SGIgftIfdV5/tNcURTb5LdIokRA5kIIx3l4rlq2XYHfxcSntXapoRg/GxaVOM1yfg==
dependencies:
classnames "^2.2.5"
enquire.js "^2.1.6"
json2mq "^0.2.0"
lodash.debounce "^4.0.8"
resize-observer-polyfill "^1.5.0"
react-string-replace@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/react-string-replace/-/react-string-replace-1.1.0.tgz"
@@ -5506,11 +5483,6 @@ requizzle@^0.2.3:
dependencies:
lodash "^4.17.21"
resize-observer-polyfill@^1.5.0:
version "1.5.1"
resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz"
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz"
@@ -5720,11 +5692,6 @@ slash@^4.0.0:
resolved "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz"
integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==
slick-carousel@^1.8.1:
version "1.8.1"
resolved "https://registry.npmjs.org/slick-carousel/-/slick-carousel-1.8.1.tgz"
integrity sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==
source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
@@ -5773,11 +5740,6 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
string-convert@^0.2.0:
version "0.2.1"
resolved "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz"
integrity sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
@@ -6369,8 +6331,7 @@ wordwrap@^1.0.0:
resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
name wrap-ansi-cjs
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==