Skip to content

Commit 8ea7274

Browse files
authored
Merge pull request #148 from HackYourFutureProjects/FE/dashboard-fix
Fe/dashboard fix
2 parents 241f899 + 8526186 commit 8ea7274

16 files changed

Lines changed: 3752 additions & 74 deletions

File tree

‎client/src/assets/logo.svg‎

Lines changed: 3252 additions & 0 deletions
Loading
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { useEffect, useState } from "react";
2+
3+
type MyLessonsCardProps = {
4+
count: number;
5+
};
6+
7+
export const MyLessonsCard = ({ count }: MyLessonsCardProps) => {
8+
const [animatedCount, setAnimatedCount] = useState(0);
9+
const [isVisible, setIsVisible] = useState(false);
10+
11+
useEffect(() => {
12+
const timer = setTimeout(() => setIsVisible(true), 0);
13+
return () => clearTimeout(timer);
14+
}, []);
15+
16+
useEffect(() => {
17+
const duration = 1500;
18+
const steps = 60;
19+
const increment = count / steps;
20+
let currentCount = 0;
21+
22+
const timer = setInterval(() => {
23+
currentCount += increment;
24+
if (currentCount >= count) {
25+
setAnimatedCount(count);
26+
clearInterval(timer);
27+
} else {
28+
setAnimatedCount(Math.floor(currentCount));
29+
}
30+
}, duration / steps);
31+
32+
return () => clearInterval(timer);
33+
}, [count]);
34+
35+
return (
36+
<div
37+
className={`
38+
relative overflow-hidden
39+
w-[332px] h-[200px]
40+
px-[32px] py-[24px]
41+
flex flex-col items-center justify-center gap-[16px]
42+
rounded-[20px]
43+
bg-dark-900 border-2 border-blue-500
44+
shadow-[0px_20px_60px_0px_rgba(59,130,246,0.15)]
45+
hover:shadow-[0px_25px_80px_0px_rgba(59,130,246,0.25)]
46+
hover:bg-dark-700
47+
transform transition-all duration-500 ease-out
48+
hover:scale-105 hover:-translate-y-2
49+
cursor-pointer group
50+
${isVisible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8"}
51+
`}
52+
>
53+
<div className="absolute inset-0 rounded-[20px] border-2 border-blue-400 opacity-0 group-hover:opacity-100 transition-opacity duration-500 animate-pulse" />
54+
55+
<div className="relative z-10 w-full flex flex-col items-center gap-[20px]">
56+
<div className="text-[22px] font-bold leading-[100%] tracking-[-0.5px] text-white drop-shadow-sm">
57+
My Total Lessons
58+
</div>
59+
60+
<div className="relative">
61+
<span
62+
className="
63+
font-bold
64+
text-[48px]
65+
leading-[1]
66+
tracking-[-1px]
67+
text-white
68+
text-center
69+
drop-shadow-lg
70+
block
71+
transform transition-all duration-300
72+
group-hover:scale-110 group-hover:text-blue-100
73+
"
74+
>
75+
{animatedCount}
76+
</span>
77+
</div>
78+
79+
<div className="w-full max-w-[200px] h-1 bg-blue-500/20 rounded-full overflow-hidden border border-blue-500/30">
80+
<div
81+
className="h-full bg-gradient-to-r from-blue-500/60 to-blue-400/80 rounded-full transition-all duration-1500 ease-out"
82+
style={{
83+
width: count > 0 ? "100%" : "0%",
84+
transform: `translateX(${count > 0 ? "0%" : "-100%"})`,
85+
}}
86+
/>
87+
</div>
88+
</div>
89+
90+
<div className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-700">
91+
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-blue-400/10 to-transparent transform -skew-x-12 -translate-x-full group-hover:translate-x-full transition-transform duration-1000" />
92+
</div>
93+
</div>
94+
);
95+
};

‎client/src/components/clientsDashboard/MyLessonsSection.tsx‎

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import LessonsTable from "../table/LessonsTable";
21
import { Calendar } from "../calendar/Calendar";
32
import { PageTitle } from "../pageTitle/PageTitle";
4-
import { FindTeachersCard } from "./FindTeachersCard";
3+
import { MyLessonsCard } from "./MyLessonsCard";
4+
import { MyTeachersCard } from "./MyTeachersCard";
5+
import { StudentLessonCard } from "./StudentLessonCard";
56
import { useStudentAppointmentsQuery } from "../../features/appointments/query/useAppointmentsQuery";
67
import { useAuthSessionStore } from "../../store/authSession.store";
78
import { useAppointmentTime } from "../../features/appointments/hooks/useAppointmentTime";
@@ -26,6 +27,17 @@ export const MyLessonsSection = () => {
2627
);
2728

2829
const appointments = data?.appointments || [];
30+
const totalLessonsCount = appointments.length;
31+
32+
const uniqueTeachersCount = useMemo(() => {
33+
const teacherIds = new Set();
34+
appointments.forEach((appointment) => {
35+
if (appointment.teacherId) {
36+
teacherIds.add(appointment.teacherId);
37+
}
38+
});
39+
return teacherIds.size;
40+
}, [appointments]);
2941

3042
const today = new Date().toISOString().split("T")[0];
3143

@@ -36,7 +48,7 @@ export const MyLessonsSection = () => {
3648
}
3749
};
3850

39-
const tableRows = useMemo(() => {
51+
const lessons = useMemo(() => {
4052
const todayAppointments = appointments.filter(
4153
(appointment) =>
4254
appointment.date === today && appointment.status === "approved",
@@ -52,17 +64,18 @@ export const MyLessonsSection = () => {
5264

5365
return {
5466
id: appointment.id,
55-
checked: appointment.status === "approved",
5667
lesson: appointment.lesson,
5768
teacher: appointment.teacherName || appointment.teacherId || "N/A",
5869
price: appointment.price,
70+
date: appointment.date,
71+
time: appointment.time,
5972
isPast,
6073
videoCall: canJoin ? (
6174
<Button
6275
as="button"
6376
onClick={() => openCallTab(videoCallHref)}
6477
variant="link"
65-
className="text-white underline text-[14px] md:text-[16px] hover:text-gray-300"
78+
className="text-blue-400 underline text-[12px] hover:text-blue-300 transition-colors"
6679
>
6780
Join
6881
</Button>
@@ -78,35 +91,39 @@ export const MyLessonsSection = () => {
7891
return (
7992
<div>
8093
<PageTitle title="General" />
81-
<div className="flex flex-col items-center justify-center mt-6 lg:flex-row lg:justify-between gap-10">
82-
<FindTeachersCard />
94+
<div className="mt-6 flex flex-col items-center justify-center gap-[24px] lg:flex-row lg:justify-between lg:gap-[40px]">
95+
<MyLessonsCard count={totalLessonsCount} />
96+
<MyTeachersCard count={uniqueTeachersCount} />
8397
<Calendar />
8498
</div>
99+
85100
<PageTitle title="My lessons today" />
86-
<div className="mt-7">
101+
<div className="mt-[12px]">
87102
{isLoading ? (
88-
<div className="text-white text-center py-8">
89-
Loading appointments...
90-
</div>
103+
<div className="py-8 text-center text-white">Loading lessons...</div>
91104
) : error ? (
92-
<div className="text-white text-center py-8">
93-
Error loading appointments
105+
<div className="py-8 text-center text-white">
106+
Error loading lessons
94107
</div>
95-
) : tableRows.length === 0 ? (
96-
<div className="text-white text-center py-8">
108+
) : lessons.length === 0 ? (
109+
<div className="py-8 text-center text-white">
97110
No approved lessons scheduled for today
98111
</div>
99112
) : (
100-
<LessonsTable
101-
height={290}
102-
columns={[
103-
{ key: "lesson", label: "Lessons", width: "130px" },
104-
{ key: "teacher", label: "Teachers", width: "184px" },
105-
{ key: "price", label: "Price", width: "146px" },
106-
{ key: "videoCall", label: "Video call", width: "146px" },
107-
]}
108-
rows={tableRows}
109-
/>
113+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-[16px]">
114+
{lessons.map((lesson) => (
115+
<StudentLessonCard
116+
key={lesson.id}
117+
lesson={lesson.lesson}
118+
teacher={lesson.teacher}
119+
price={lesson.price}
120+
date={lesson.date}
121+
time={lesson.time}
122+
isPast={lesson.isPast}
123+
videoCall={lesson.videoCall}
124+
/>
125+
))}
126+
</div>
110127
)}
111128
</div>
112129
</div>
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { useEffect, useState } from "react";
2+
3+
type MyTeachersCardProps = {
4+
count: number;
5+
};
6+
7+
export const MyTeachersCard = ({ count }: MyTeachersCardProps) => {
8+
const [animatedCount, setAnimatedCount] = useState(0);
9+
const [isVisible, setIsVisible] = useState(false);
10+
11+
useEffect(() => {
12+
const timer = setTimeout(() => setIsVisible(true), 0);
13+
return () => clearTimeout(timer);
14+
}, []);
15+
16+
useEffect(() => {
17+
const duration = 1500;
18+
const steps = 60;
19+
const increment = count / steps;
20+
let currentCount = 0;
21+
22+
const timer = setInterval(() => {
23+
currentCount += increment;
24+
if (currentCount >= count) {
25+
setAnimatedCount(count);
26+
clearInterval(timer);
27+
} else {
28+
setAnimatedCount(Math.floor(currentCount));
29+
}
30+
}, duration / steps);
31+
32+
return () => clearInterval(timer);
33+
}, [count]);
34+
35+
return (
36+
<div
37+
className={`
38+
relative overflow-hidden
39+
w-[332px] h-[200px]
40+
px-[32px] py-[24px]
41+
flex flex-col items-center justify-center gap-[16px]
42+
rounded-[20px]
43+
bg-dark-900 border-2 border-blue-500
44+
shadow-[0px_20px_60px_0px_rgba(59,130,246,0.15)]
45+
hover:shadow-[0px_25px_80px_0px_rgba(59,130,246,0.25)]
46+
hover:bg-dark-700
47+
transform transition-all duration-500 ease-out
48+
hover:scale-105 hover:-translate-y-2
49+
cursor-pointer group
50+
${isVisible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8"}
51+
`}
52+
>
53+
<div className="absolute inset-0 rounded-[20px] border-2 border-blue-400 opacity-0 group-hover:opacity-100 transition-opacity duration-500 animate-pulse" />
54+
55+
<div className="relative z-10 w-full flex flex-col items-center gap-[20px]">
56+
<div className="text-[22px] font-bold leading-[100%] tracking-[-0.5px] text-white drop-shadow-sm">
57+
My Teachers
58+
</div>
59+
60+
<div className="relative">
61+
<span
62+
className="
63+
font-bold
64+
text-[48px]
65+
leading-[1]
66+
tracking-[-1px]
67+
text-white
68+
text-center
69+
drop-shadow-lg
70+
block
71+
transform transition-all duration-300
72+
group-hover:scale-110 group-hover:text-blue-100
73+
"
74+
>
75+
{animatedCount}
76+
</span>
77+
</div>
78+
79+
<div className="w-full max-w-[200px] h-1 bg-blue-500/20 rounded-full overflow-hidden border border-blue-500/30">
80+
<div
81+
className="h-full bg-gradient-to-r from-blue-500/60 to-blue-400/80 rounded-full transition-all duration-1500 ease-out"
82+
style={{
83+
width: count > 0 ? "100%" : "0%",
84+
transform: `translateX(${count > 0 ? "0%" : "-100%"})`,
85+
}}
86+
/>
87+
</div>
88+
</div>
89+
90+
<div className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-700">
91+
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-blue-400/10 to-transparent transform -skew-x-12 -translate-x-full group-hover:translate-x-full transition-transform duration-1000" />
92+
</div>
93+
</div>
94+
);
95+
};
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import React from "react";
2+
3+
type StudentLessonCardProps = {
4+
lesson: string;
5+
teacher: string;
6+
price: string;
7+
date: string;
8+
time: string;
9+
isPast: boolean;
10+
videoCall: React.ReactNode;
11+
};
12+
13+
export const StudentLessonCard = ({
14+
lesson,
15+
teacher,
16+
price,
17+
date,
18+
time,
19+
isPast,
20+
videoCall,
21+
}: StudentLessonCardProps) => {
22+
const formatDate = (dateStr: string) => {
23+
const date = new Date(dateStr);
24+
return date.toLocaleDateString("en-US", {
25+
weekday: "short",
26+
month: "short",
27+
day: "numeric",
28+
});
29+
};
30+
31+
return (
32+
<div className="bg-dark-900/30 border border-blue-500/20 rounded-[16px] p-[20px] hover:border-blue-500/40 transition-all duration-300 hover:bg-dark-900/50">
33+
<div className="flex flex-col gap-[12px]">
34+
<div className="flex items-start justify-between">
35+
<div className="flex-1">
36+
<h3 className="text-white font-medium text-[16px] mb-[4px]">
37+
{lesson}
38+
</h3>
39+
<p className="text-gray-400 text-[14px]">{teacher}</p>
40+
</div>
41+
<div className="text-right">
42+
<p className="text-blue-400 font-semibold text-[16px]">
43+
{price} euro
44+
</p>
45+
<p className="text-gray-400 text-[12px] mt-[2px]">
46+
{formatDate(date)} • {time}
47+
</p>
48+
</div>
49+
</div>
50+
51+
<div className="flex items-center justify-between pt-[8px] border-t border-gray-700/30">
52+
<div className="flex items-center gap-[8px]">
53+
<div
54+
className={`w-[8px] h-[8px] rounded-full ${
55+
isPast ? "bg-gray-500" : "bg-green-500"
56+
}`}
57+
/>
58+
<span className="text-[12px] text-gray-400">
59+
{isPast ? "Past" : "Active"}
60+
</span>
61+
</div>
62+
63+
<div className="flex items-center">{videoCall}</div>
64+
</div>
65+
</div>
66+
</div>
67+
);
68+
};

‎client/src/components/homeSection/Features/Features.tsx‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { FeatureCard } from "./FeatureCard";
2-
import { Button } from "../../ui/button/Button";
32
import HomeSection1Icon from "../../icons/HomeSection1Icon";
43
import HomeSection1Icon2 from "../../icons/HomeSection1Icon2";
54
import HomeSection1Icon3 from "../../icons/HomeSection1Icon3";
@@ -54,10 +53,6 @@ export const Features = () => {
5453
/>
5554
))}
5655
</div>
57-
58-
<div className="flex justify-center mt-12 sm:mt-16 lg:mt-20">
59-
<Button variant="secondary">More</Button>
60-
</div>
6156
</div>
6257
</section>
6358
);

0 commit comments

Comments
 (0)