Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/venv
.env
.env.bak
node_modules

17 changes: 12 additions & 5 deletions frontend/src/assets/icon.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
/* ---------- deep accent (#8B5E34) --------------------------------------- */

export const HomeIcon = (
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke={DEEP}>
<svg className="w-5 h-5 text-[#E9967A]" viewBox="0 0 24 24" fill="none" stroke={PRIMARY}>
<path
strokeLinecap="round"
strokeLinejoin="round"
Expand Down Expand Up @@ -133,15 +133,15 @@
);

export const EditIcon = (
<svg className="w-6 h-6" viewBox="0 0 24 24" fill="none" stroke={PRIMARY}>
<svg className="w-6 h-6 text-[#E9967A]" viewBox="0 0 24 24" fill="none" stroke={PRIMARY}>
{strokeIcon(
"M10.779 17.779 4.36 19.918 6.5 13.5m4.279 4.279 8.364-8.643a3.027 3.027 0 0 0-2.14-5.165 3.03 3.03 0 0 0-2.14.886L6.5 13.5m4.279 4.279L6.499 13.5m2.14 2.14 6.213-6.504M12.75 7.04 17 11.28"
)}
</svg>
);

export const SmallEditIcon = (
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke={PRIMARY}>
<svg className="w-4 h-4 text-[#E9967A]" viewBox="0 0 24 24" fill="none" stroke={PRIMARY}>
{strokeIcon(
"M10.779 17.779 4.36 19.918 6.5 13.5m4.279 4.279 8.364-8.643a3.027 3.027 0 0 0-2.14-5.165 3.03 3.03 0 0 0-2.14.886L6.5 13.5m4.279 4.279L6.499 13.5m2.14 2.14 6.213-6.504M12.75 7.04 17 11.28"
)}
Expand Down Expand Up @@ -175,7 +175,7 @@
/* Calendar and navigation icons ------------------------------------------- */
export const CalendarIcon = (
<svg
className="w-4 h-4 text-yellow-600 dark:text-white"
className="w-6 h-6 text-yellow-600 dark:text-white"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
Expand Down Expand Up @@ -380,7 +380,7 @@
</svg>
);

export function confirmToast(message, onConfirm) {

Check warning on line 383 in frontend/src/assets/icon.jsx

View workflow job for this annotation

GitHub Actions / test-frontend

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
toast.custom(
(t) => (
<div className="flex flex-col gap-4 p-4 bg-white rounded-xl shadow-lg border border-neutral-200">
Expand Down Expand Up @@ -411,38 +411,38 @@
}


export const closeIcon = (

Check warning on line 414 in frontend/src/assets/icon.jsx

View workflow job for this annotation

GitHub Actions / test-frontend

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
<svg className="w-28 h-28 text-red-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18 17.94 6M18 18 6.06 6"/>
</svg>
)

export const tickIcon = (

Check warning on line 420 in frontend/src/assets/icon.jsx

View workflow job for this annotation

GitHub Actions / test-frontend

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
<svg className="w-40 h-40 text-green-600 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 11.917 9.724 16.5 19 7.5"/>
</svg>
);

export const creditcardIcon1 = (

Check warning on line 426 in frontend/src/assets/icon.jsx

View workflow job for this annotation

GitHub Actions / test-frontend

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
<svg className="w-8 h-8 text-yellow-700 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
<path fillRule="evenodd" d="M4 5a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2H4Zm0 6h16v6H4v-6Z" clipRule="evenodd"/>
<path fillRule="evenodd" d="M5 14a1 1 0 0 1 1-1h2a1 1 0 1 1 0 2H6a1 1 0 0 1-1-1Zm5 0a1 1 0 0 1 1-1h5a1 1 0 1 1 0 2h-5a1 1 0 0 1-1-1Z" clipRule="evenodd"/>
</svg>
);

export const anglerightIcon = (

Check warning on line 433 in frontend/src/assets/icon.jsx

View workflow job for this annotation

GitHub Actions / test-frontend

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
<svg className="w-5 h-5 text-gray-700 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="m9 5 7 7-7 7"/>
</svg>
);

export const angledownIcon = (

Check warning on line 439 in frontend/src/assets/icon.jsx

View workflow job for this annotation

GitHub Actions / test-frontend

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
<svg className="w-5 h-5 text-gray-700 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="m19 9-7 7-7-7"/>
</svg>
);

export const cartIcon = (

Check warning on line 445 in frontend/src/assets/icon.jsx

View workflow job for this annotation

GitHub Actions / test-frontend

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
<svg className="w-8 h-8 text-yellow-700 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
<path fillRule="evenodd" d="M4 4a1 1 0 0 1 1-1h1.5a1 1 0 0 1 .979.796L7.939 6H19a1 1 0 0 1 .979 1.204l-1.25 6a1 1 0 0 1-.979.796H9.605l.208 1H17a3 3 0 1 1-2.83 2h-2.34a3 3 0 1 1-4.009-1.76L5.686 5H5a1 1 0 0 1-1-1Z" clipRule="evenodd"/>
</svg>
Expand All @@ -460,20 +460,27 @@
</svg>
);

export const filterIcon = (

Check warning on line 463 in frontend/src/assets/icon.jsx

View workflow job for this annotation

GitHub Actions / test-frontend

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
<svg className="w-5 h-6 text-gray-600 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
<path d="M5.05 3C3.291 3 2.352 5.024 3.51 6.317l5.422 6.059v4.874c0 .472.227.917.613 1.2l3.069 2.25c1.01.742 2.454.036 2.454-1.2v-7.124l5.422-6.059C21.647 5.024 20.708 3 18.95 3H5.05Z"/>
</svg>
);

export const trashIcon = (

Check warning on line 469 in frontend/src/assets/icon.jsx

View workflow job for this annotation

GitHub Actions / test-frontend

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
<svg className="w-6 h-6 text-yellow-600 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
<path fillRule="evenodd" d="M8.586 2.586A2 2 0 0 1 10 2h4a2 2 0 0 1 2 2v2h3a1 1 0 1 1 0 2v12a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V8a1 1 0 0 1 0-2h3V4a2 2 0 0 1 .586-1.414ZM10 6h4V4h-4v2Zm1 4a1 1 0 1 0-2 0v8a1 1 0 1 0 2 0v-8Zm4 0a1 1 0 1 0-2 0v8a1 1 0 1 0 2 0v-8Z" clipRule="evenodd"/>
</svg>
);

export const pfpIcon = (

Check warning on line 475 in frontend/src/assets/icon.jsx

View workflow job for this annotation

GitHub Actions / test-frontend

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
<svg className="w-5 h-5 text-orange-300 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
<path fillRule="evenodd" d="M12 4a4 4 0 1 0 0 8 4 4 0 0 0 0-8Zm-2 9a4 4 0 0 0-4 4v1a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2v-1a4 4 0 0 0-4-4h-4Z" clipRule="evenodd"/>
</svg>
);
);

export const MessageIcon = (<svg className="w-7 h-7 text-yellow-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M16 10.5h.01m-4.01 0h.01M8 10.5h.01M5 5h14a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1h-6.6a1 1 0 0 0-.69.275l-2.866 2.723A.5.5 0 0 1 8 18.635V17a1 1 0 0 0-1-1H5a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1Z"/>
</svg>


)
39 changes: 20 additions & 19 deletions frontend/src/components/landing/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect } from "react";
import logo from "../../assets/logo.png";
import { useNavigate } from "react-router-dom";
import { logout } from "../../services/authService";

import { MessageIcon } from "../../assets/icon";
const Header = (props) => {
const navigate = useNavigate();
const { setIsLoggedIn } = props; // Destructure the specific prop
Expand All @@ -29,16 +29,16 @@ const Header = (props) => {
</div>

{/* Center - Navigation */}
<div className="flex justify-center w-1/3 gap-6 font-semibold">
<div className="flex justify-center w-1/3 gap-8 font-semibold">
<button
className="py-2 w-20 rounded-full bg-transparent hover:bg-gray-400/20 transition-all duration-200"
className="py-2 px-4 w-20 rounded-full bg-transparent hover:bg-gray-400/20 transition-all duration-200"
onClick={() => navigate("/")}
>
Home
</button>
{props.isLoggedIn && (
<button
className="py-2 w-20 rounded-full bg-transparent hover:bg-gray-400/20 transition-all duration-200"
className="py-2 px-4 w-20 rounded-full bg-transparent hover:bg-gray-400/20 transition-all duration-200"
onClick={() =>
navigate(`/tutor/profile/${localStorage.getItem("userId")}`)
}
Expand All @@ -47,36 +47,37 @@ const Header = (props) => {
</button>
)}
<button
className="py-2 w-20 rounded-full bg-transparent hover:bg-gray-400/20 transition-all duration-200"
className="py-2 px-4 w-24 rounded-full bg-transparent hover:bg-gray-400/20 transition-all duration-200"
onClick={() => navigate("/meetings/create")}
>
Schedule
</button>
<button
className="py-2 w-20 rounded-full bg-transparent hover:bg-gray-400/20 transition-all duration-200"
onClick={() => navigate("/meetings")}
>
Calendar
</button>
<button className="py-2 w-20 rounded-full bg-transparent hover:bg-gray-400/20 transition-all duration-200"
<button className="py-2 px-4 w-20 rounded-full bg-transparent hover:bg-gray-400/20 transition-all duration-200"
onClick={() =>
navigate('/listing')
}>
Tutors
</button>
<button className="py-2 w-20 rounded-full bg-transparent hover:bg-gray-400/20 transition-all duration-200"
onClick={() =>
navigate(`/dashboard/${localStorage.getItem("userId")}`)
}>
<button className="py-2 px-4 w-24 rounded-full bg-transparent hover:bg-gray-400/20 transition-all duration-200"
onClick={() =>
navigate(`/dashboard/${localStorage.getItem("userId")}`)
}>
Meetings
</button>
<button className="py-2 w-20 rounded-full bg-transparent hover:bg-gray-400/20 transition-all duration-200">
{/* <button className="py-2 w-20 rounded-full bg-transparent hover:bg-gray-400/20 transition-all duration-200">
Contact
</button>
</button> */}
</div>

{/* Right - Log In/Log Out */}
<div className="flex justify-end w-1/3">
<div className="flex justify-end w-1/3 gap-4">
<button
// onClick={() => setIsModalOpen(true)}
className="py-1 px-4 rounded-full bg-gray-300/30 hover:bg-gray-400/40 transition-all duration-200"
aria-label="Messenger"
>
{MessageIcon}
</button>
{props.isLoggedIn ? (
<button
className="py-1 px-4 text-sm font-semibold rounded-full bg-yellow-800 text-white hover:bg-yellow-900 transition-color duration-200"
Expand Down
20 changes: 8 additions & 12 deletions frontend/src/components/tutor_profile/edit/BasicInforEdit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { useState } from "react";
import { useNavigate } from "react-router-dom";
import {
EmailIcon,
StarIcon,
EditIcon,
CameraIcon,
CalendarIcon,
Expand Down Expand Up @@ -77,7 +76,7 @@ const BasicInforEdit = ({ data, showToast }) => {
<button
type="button"
onClick={() => navigate(`/meetings/tutor/${id}`)}
className="p-2 rounded-full bg-transparent hover:bg-yellow-100 focus:outline-none focus:ring-1 focus:ring-yellow-600"
className="p-2 rounded-full bg-transparent hover:bg-gray-400/20 transition-all duration-200"
aria-label="Open tutor calendar"
>
{CalendarIcon}
Expand Down Expand Up @@ -117,25 +116,22 @@ const BasicInforEdit = ({ data, showToast }) => {
{EmailIcon}
<div>{tutorEmail}</div>
</div>
<div className="flex gap-1">
{/* <div className="flex gap-1">
Rated {5}
{StarIcon}by 5 students
</div>
</div> */}
</div>
</div>
<div className="flex justify-between">
<div className="flex flex-wrap w-3/5 gap-3 mt-3">
<div className='flex justify-between items-end mt-5'>
<div className='flex flex-wrap gap-3 max-w-4xl'>
{tutorTeachingStyle?.map((style, index) => (
<div
key={index}
className="px-4 py-1 bg-gray-200 rounded-xl text-center hover:bg-gray-300"
>
<div key={index} className='px-4 py-2 bg-gray-100 rounded-full text-sm font-medium text-gray-700 hover:bg-gray-200 transition-colors'>
{style}
</div>
))}
</div>
<div className="text-3xl font-bold pb-2 text-yellow-800">
{tutorHourlyRate}$/h
<div className='text-4xl font-bold text-yellow-800 whitespace-nowrap ml-6'>
${tutorHourlyRate}/h
</div>
</div>
</div>
Expand Down
87 changes: 58 additions & 29 deletions frontend/src/components/tutor_profile/watch/BasicInfor.jsx
Original file line number Diff line number Diff line change
@@ -1,72 +1,101 @@
import { EmailIcon, StarIcon } from '../../../assets/icon'
import avatars from '../AvatarList'
import covers from '../CoverList'
import { EmailIcon, CalendarIcon } from "../../../assets/icon";
import { useNavigate, useParams } from "react-router-dom";
import avatars from "../AvatarList";
import covers from "../CoverList";

const BasicInfor = ({ data }) => {
const { username, email, bio, teaching_style, hourly_rate, avatar, cover } = data
const { id } = useParams();
const navigate = useNavigate(); // Make sure this is useNavigate, NOT useNavigation
const { username, email, bio, teaching_style, hourly_rate, avatar, cover } =
data;

const parseTeachingStyle = (styleData) => {
if (Array.isArray(styleData)) {
return styleData;
}
if (typeof styleData === 'string') {
if (typeof styleData === "string") {
try {
return JSON.parse(styleData);
} catch (error) {
console.warn('Failed to parse teaching_style:', error);
console.warn("Failed to parse teaching_style:", error);
return [];
}
}
return [];
};

const teachingStyle = parseTeachingStyle(teaching_style)
// const teachingStyle = teaching_style
const fetchAvatar = avatars.find((ava) => ava.id === avatar)
const fetchCover = covers.find((cov) => cov.id === cover)
const teachingStyle = parseTeachingStyle(teaching_style);
const fetchAvatar = avatars.find((ava) => ava.id === avatar);
const fetchCover = covers.find((cov) => cov.id === cover);

return (
<div>
<div className="h-52 bg-gray-200 rounded-tl-xl rounded-tr-xl relative"
style={{ backgroundImage: `url(${fetchCover.src})`, backgroundSize: 'cover', backgroundPosition: 'center' }}>
<div
className="h-52 bg-gray-200 rounded-tl-xl rounded-tr-xl relative"
style={{
backgroundImage: `url(${fetchCover?.src})`,
backgroundSize: "cover",
backgroundPosition: "center",
}}
>
{/* Calendar Button - positioned exactly like in BasicInforEdit */}
<div className="absolute top-56 right-8">
<button
type="button"
onClick={() => navigate(`/meetings/tutor/${id}`)}
className="p-2 rounded-full bg-transparent hover:bg-yellow-100 focus:outline-none focus:ring-1 focus:ring-yellow-600"
aria-label="Open tutor calendar"
>
{CalendarIcon}
</button>
</div>

{/* Profile Section */}
<div className="relative">
{/* Avatar */}
<div className="absolute top-24 left-12 z-10">
<div className="w-40 h-40 bg-gray-300 rounded-full border-4 border-white relative">
<img src={fetchAvatar.src} alt="Avatar" className="w-full h-full object-cover rounded-full" />
<img
src={fetchAvatar?.src}
alt="Avatar"
className="w-full h-full object-cover rounded-full"
/>
</div>
</div>
</div>
</div>
<div className='flex flex-col px-10 py-10 mt-4 gap-1'>
<div className='flex justify-between'>
<div className='flex flex-col gap-4'>
<div className='text-4xl text-yellow-800 font-bold'>{username}</div>
<div className='ml-1'>{bio}</div>
<div className="flex flex-col px-10 py-10 mt-4 gap-1">
<div className="flex justify-between">
<div className="flex flex-col gap-4">
<div className="text-4xl text-yellow-800 font-bold">{username}</div>
<div className="ml-1">{bio}</div>
</div>
<div className='flex flex-col gap-4 items-end mt-3'>
<div className='flex gap-3'>
<div className="flex flex-col gap-4 items-end mt-3">
<div className="flex gap-3">
{EmailIcon}
<div>{email}</div>
</div>
<div className='flex gap-1'>Rated {5}{StarIcon}by 5 students</div>
{/* <div className="flex gap-1">
Rated {5}
{StarIcon}by 5 students
</div> */}
</div>
</div>
<div className='flex justify-between'>
<div className='flex gap-3 mt-3'>
<div className='flex justify-between items-end mt-5'>
<div className='flex flex-wrap gap-3 max-w-4xl'>
{teachingStyle?.map((style, index) => (
<div key={index} className='px-4 py-1 bg-gray-200 rounded-xl text-center hover:bg-gray-300'>
<div key={index} className='px-4 py-2 bg-gray-100 rounded-full text-sm font-medium text-gray-700 hover:bg-gray-200 transition-colors'>
{style}
</div>
))}
</div>
<div className='text-3xl font-bold pb-2 text-yellow-800'>{hourly_rate}$/h</div>
<div className='text-4xl font-bold text-yellow-800 whitespace-nowrap ml-6'>
${hourly_rate}/h
</div>
</div>
</div>
</div>
);
};

)
}

export default BasicInfor
export default BasicInfor;
4 changes: 2 additions & 2 deletions frontend/src/pages/auth/LogIn.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState } from "react";
import { login } from "../../services/authService";
import toastpic from "../../assets/landingpic.jpg";
import { useNavigate } from "react-router-dom";
import { toast, Toaster } from "react-hot-toast";
import { toast } from "react-hot-toast";
import { EyeCloseIcon, EyeOpenIcon } from "../../assets/icon";

const LogIn = (props) => {
Expand Down Expand Up @@ -44,7 +44,7 @@ const LogIn = (props) => {

return (
<div className="flex items-center justify-center h-screen bg-yellow-50 -mt-6">
<Toaster position="top-center" reverseOrder={false} />
{/* <Toaster position="top-center" reverseOrder={false} /> */}
<div className="relative w-full max-w-md">
{/* Image Block */}
<div
Expand Down
9 changes: 3 additions & 6 deletions frontend/src/pages/auth/SignUp.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState } from "react";
import { useState } from "react";
import { register } from "../../services/authService";
import { useNavigate } from "react-router-dom";
import { toast } from "react-hot-toast";
import toast from "react-hot-toast";
import { EyeCloseIcon, EyeOpenIcon } from "../../assets/icon";

const SignUp = () => {
Expand All @@ -10,9 +10,6 @@ const SignUp = () => {
const [confirmPassword, setConfirmPassword] = useState("");

const [showPassword, setShowPassword] = useState(false);
const renderEyeIcon = (isOpen) => {
return React.cloneElement(isOpen ? EyeOpenIcon : EyeCloseIcon);
};

const [formData, setFormData] = useState({
username: "",
Expand Down Expand Up @@ -185,7 +182,7 @@ const SignUp = () => {
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700 focus:outline-none"
>
{renderEyeIcon(showPassword)}
{showPassword ? EyeOpenIcon : EyeCloseIcon}
</button>
</div>
</div>
Expand Down
Loading