From 85eeaf160ea3156c075805527eb1c8828d14397c Mon Sep 17 00:00:00 2001 From: Oladeji Date: Sat, 25 Jan 2025 16:41:04 +0100 Subject: [PATCH 1/9] handlelogout fixed --- src/app/home/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/home/page.tsx b/src/app/home/page.tsx index 4522c69..6651614 100644 --- a/src/app/home/page.tsx +++ b/src/app/home/page.tsx @@ -54,7 +54,7 @@ const Home = () => { className="sm:hidden text-[30px] cursor-pointer" onClick={()=> setOpenMenu(!openMenu)} /> - + {/* contsainer div */}
From c35ddc914f3fa0baaaad862ae0a3c6bba65a47b2 Mon Sep 17 00:00:00 2001 From: Glo333 Date: Sat, 25 Jan 2025 19:38:55 +0100 Subject: [PATCH 2/9] changed urls --- src/app/utils/api.ts | 2 +- src/app/utils/axiosInstance.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/utils/api.ts b/src/app/utils/api.ts index 12d04f3..38e233f 100644 --- a/src/app/utils/api.ts +++ b/src/app/utils/api.ts @@ -3,7 +3,7 @@ import axios from 'axios'; const api = axios.create({ - baseURL: 'https://career-cop.azurewebsites.net', // Set this to your FastAPI backend URL + baseURL: 'https://fast-backend-n.onrender.com', // Set this to your FastAPI backend URL }); export const registerUser = async (username: string, email: string, password: string) => { diff --git a/src/app/utils/axiosInstance.ts b/src/app/utils/axiosInstance.ts index 7a460e7..e9a21b0 100644 --- a/src/app/utils/axiosInstance.ts +++ b/src/app/utils/axiosInstance.ts @@ -3,7 +3,7 @@ import axios from 'axios'; const axiosInstance = axios.create({ - baseURL: 'https://career-cop.azurewebsites.net', // Replace with your FastAPI server URL + baseURL: 'https://fast-backend-n.onrender.com', // Replace with your FastAPI server URL headers: { 'Content-Type': 'application/json', }, From b829d788cf2fe775403edbfb8e6f2b472bfaea42 Mon Sep 17 00:00:00 2001 From: Glo333 Date: Sat, 25 Jan 2025 19:52:01 +0100 Subject: [PATCH 3/9] corrected home --- src/app/home/page.tsx | 269 ++++++++++++++++++------------------------ 1 file changed, 115 insertions(+), 154 deletions(-) diff --git a/src/app/home/page.tsx b/src/app/home/page.tsx index 6651614..7fb8524 100644 --- a/src/app/home/page.tsx +++ b/src/app/home/page.tsx @@ -1,8 +1,7 @@ "use client"; -// import Modal from "../components/Modal"; + import { useState, useEffect } from "react"; -import { FaUserGroup } from "react-icons/fa6"; -import { FaLinkedin } from "react-icons/fa"; +import { FaUserGroup, FaLinkedin } from "react-icons/fa"; import { CiChat1 } from "react-icons/ci"; import { BsFileEarmarkText } from "react-icons/bs"; import { GiHamburgerMenu } from "react-icons/gi"; @@ -11,195 +10,157 @@ import Screen1 from "@/app/components/screens/Screen1"; import Screen2 from "@/app/components/screens/Screen2"; import Screen3 from "@/app/components/screens/Screen3"; import Screen4 from "@/app/components/screens/Screen4"; -// import Signup from "@/app/Signup/page" + +// Enum for screen types to avoid magic strings +enum Screen { + Screen1 = "screen1", + Screen2 = "screen2", + Screen3 = "screen3", + Screen4 = "screen4", +} const Home = () => { - const [currentPage, setCurrentPage] = useState< - "screen1" | "screen2" | "screen3" | "screen4" - >("screen1"); + const [currentPage, setCurrentPage] = useState(Screen.Screen1); const [openMenu, setOpenMenu] = useState(false); - - // const handleHamburgerMenu = () => { - // setOpenMenu(!openMenu); - // }; - const handleScreenChange = ( - screen: "screen1" | "screen2" | "screen3" | "screen4" - ): void => { - setCurrentPage(screen); - setOpenMenu(!openMenu); - }; - const router = useRouter(); + + // Check if the user is logged in (client-side only) useEffect(() => { - // Check if user is logged in by verifying token presence - const token = localStorage.getItem("access_token"); - if (!token) { - router.push("/login"); // Redirect to login if no token is found + if (typeof window !== "undefined") { + const token = localStorage.getItem("access_token"); + if (!token) { + router.push("/login"); + } } }, [router]); + // Handle screen change and toggle menu + const handleScreenChange = (screen: Screen): void => { + setCurrentPage(screen); + setOpenMenu(false); // Close the menu after selection + }; + + // Handle logout const handleLogOut = () => { - router.push('/'); - } + localStorage.removeItem("access_token"); + router.push("/"); + }; + + // Menu items data for reusability + const menuItems = [ + { + icon: , + label: "Interview Prep", + screen: Screen.Screen1, + }, + { + icon: , + label: "LinkedIn Profile Review", + screen: Screen.Screen2, + }, + { + icon: , + label: "CV/Resume Review", + screen: Screen.Screen3, + }, + { + icon: , + label: "Career Advisor Chat", + screen: Screen.Screen4, + }, + ]; return ( <> -
-
+ {/* Header */} +
+

deji___
school

+ {/* Hamburger Menu (Mobile Only) */} setOpenMenu(!openMenu)} + onClick={() => setOpenMenu(!openMenu)} + aria-label="Toggle menu" /> - + {/* Logout Button (Desktop Only) */} +
- {/* contsainer div */} -
- {/* left side mobile modal */} - {openMenu && ( -
-
-

- choose a service below -

-
-
-
- -

{ - handleScreenChange("screen1"); - }} - className={`capitalize ${ - currentPage === "screen1" ? "font-serif" : "font-normal" - } `} - > - interview prep -

-
-
- -

{ - handleScreenChange("screen2"); - }} - className={`capitalize ${ - currentPage === "screen1" ? "font-serif" : "font-normal" - } `} - > - linkedIn profile review -

-
-
- -

{ - handleScreenChange("screen3"); - }} - className={`capitalize ${ - currentPage === "screen1" ? "font-serif" : "font-normal" - } `} - > - cv/resume review -

-
-
- +
+ + {/* Main Content */} +
+ {/* Mobile Menu */} + {openMenu && ( +
+
+

Choose a service below

+
+
+ {menuItems.map((item) => ( +
handleScreenChange(item.screen)} + > + {item.icon}

{ - handleScreenChange("screen4"); - }} className={`capitalize ${ - currentPage === "screen1" ? "font-serif" : "font-normal" - } `} + currentPage === item.screen ? "font-serif" : "font-normal" + }`} > - career advisor chat + {item.label}

-
+ ))}
- )} - {/* left side big screen modal */} -
-
-

- choose a service below -

-
-
-
- -

{ - handleScreenChange("screen1"); - }} - className={`capitalize cursor-pointer ${ - currentPage === "screen1" ? "font-serif" : "font-normal" - }`} - > - interview prep -

-
-
- -

{ - handleScreenChange("screen2"); - }} - className={`capitalize cursor-pointer ${ - currentPage === "screen2" ? "font-serif" : "font-normal" - }`} - > - linkedIn profile review -

-
-
- -

{ - handleScreenChange("screen3"); - }} - className={`capitalize cursor-pointer ${ - currentPage === "screen3" ? "font-serif" : "font-normal" - }`} - > - cv/resume review -

-
-
- +
+ )} + + {/* Desktop Menu */} +
+
+

Choose a service below

+
+
+ {menuItems.map((item) => ( +
handleScreenChange(item.screen)} + > + {item.icon}

{ - handleScreenChange("screen4"); - }} - className={`capitalize cursor-pointer ${ - currentPage === "screen4" ? "font-serif" : "font-normal" + className={`capitalize ${ + currentPage === item.screen ? "font-serif" : "font-normal" }`} > - career advisor chat + {item.label}

-
+ ))}
- {/* right side */} +
- {/* screen 1 */} - {currentPage === "screen1" && } - {/* screen 2 */} - {currentPage === "screen2" && } - {/* screen 3 */} - {currentPage === "screen3" && } - {/* screen 4 */} - {currentPage === "screen4" && } + {/* Screen Content */} +
+ {currentPage === Screen.Screen1 && } + {currentPage === Screen.Screen2 && } + {currentPage === Screen.Screen3 && } + {currentPage === Screen.Screen4 && }
); }; -export default Home; +export default Home; \ No newline at end of file From 8c78fcdeccde91e792a24bc0fe0e50413cbd51ff Mon Sep 17 00:00:00 2001 From: Glo333 Date: Sat, 25 Jan 2025 19:59:06 +0100 Subject: [PATCH 4/9] made fixes --- src/app/home/page.tsx | 216 +++++++++++++++++++----------------------- 1 file changed, 98 insertions(+), 118 deletions(-) diff --git a/src/app/home/page.tsx b/src/app/home/page.tsx index 7fb8524..43c6508 100644 --- a/src/app/home/page.tsx +++ b/src/app/home/page.tsx @@ -1,165 +1,145 @@ "use client"; import { useState, useEffect } from "react"; -import { FaUserGroup, FaLinkedin } from "react-icons/fa"; +import { useRouter } from "next/navigation"; +import { FaUserGroup, FaLinkedin } from "react-icons/fa6"; import { CiChat1 } from "react-icons/ci"; import { BsFileEarmarkText } from "react-icons/bs"; import { GiHamburgerMenu } from "react-icons/gi"; -import { useRouter } from "next/navigation"; import Screen1 from "@/app/components/screens/Screen1"; import Screen2 from "@/app/components/screens/Screen2"; import Screen3 from "@/app/components/screens/Screen3"; import Screen4 from "@/app/components/screens/Screen4"; -// Enum for screen types to avoid magic strings -enum Screen { - Screen1 = "screen1", - Screen2 = "screen2", - Screen3 = "screen3", - Screen4 = "screen4", +type ScreenType = "screen1" | "screen2" | "screen3" | "screen4"; + +interface MenuItem { + id: ScreenType; + label: string; + icon: JSX.Element; + component: JSX.Element; } const Home = () => { - const [currentPage, setCurrentPage] = useState(Screen.Screen1); - const [openMenu, setOpenMenu] = useState(false); + const [currentPage, setCurrentPage] = useState("screen1"); + const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const router = useRouter(); - // Check if the user is logged in (client-side only) - useEffect(() => { - if (typeof window !== "undefined") { - const token = localStorage.getItem("access_token"); - if (!token) { - router.push("/login"); - } - } - }, [router]); - - // Handle screen change and toggle menu - const handleScreenChange = (screen: Screen): void => { - setCurrentPage(screen); - setOpenMenu(false); // Close the menu after selection - }; - - // Handle logout - const handleLogOut = () => { - localStorage.removeItem("access_token"); - router.push("/"); - }; - - // Menu items data for reusability - const menuItems = [ + const menuItems: MenuItem[] = [ { - icon: , + id: "screen1", label: "Interview Prep", - screen: Screen.Screen1, + icon: , + component: }, { - icon: , + id: "screen2", label: "LinkedIn Profile Review", - screen: Screen.Screen2, + icon: , + component: }, { - icon: , + id: "screen3", label: "CV/Resume Review", - screen: Screen.Screen3, + icon: , + component: }, { - icon: , + id: "screen4", label: "Career Advisor Chat", - screen: Screen.Screen4, - }, + icon: , + component: + } ]; + useEffect(() => { + try { + const token = localStorage.getItem("access_token"); + if (!token) { + router.push("/login"); + } + } catch (error) { + console.error("Error checking authentication:", error); + router.push("/login"); + } + }, [router]); + + const handleLogout = () => { + try { + localStorage.removeItem("access_token"); + router.push("/login"); + } catch (error) { + console.error("Error during logout:", error); + } + }; + + const handleScreenChange = (screen: ScreenType) => { + setCurrentPage(screen); + setIsMobileMenuOpen(false); + }; + + const MenuContent = ({ isMobile = false }: { isMobile?: boolean }) => ( +
+

Choose a service below

+
+
+ {menuItems.map((item) => ( + + ))} +
+
+ ); + return ( - <> +
{/* Header */} -
-
-

- deji___
- school -

- {/* Hamburger Menu (Mobile Only) */} - setOpenMenu(!openMenu)} - aria-label="Toggle menu" - /> - {/* Logout Button (Desktop Only) */} +
+

+ deji___
school +

+
+ setIsMobileMenuOpen(!isMobileMenuOpen)} + />
-
+ {/* Main Content */} -
+
{/* Mobile Menu */} - {openMenu && ( -
-
-

Choose a service below

-
-
- {menuItems.map((item) => ( -
handleScreenChange(item.screen)} - > - {item.icon} -

- {item.label} -

-
- ))} -
-
-
- )} - + {isMobileMenuOpen && } + {/* Desktop Menu */}
-
-

Choose a service below

-
-
- {menuItems.map((item) => ( -
handleScreenChange(item.screen)} - > - {item.icon} -

- {item.label} -

-
- ))} -
-
+
- {/* Screen Content */} -
- {currentPage === Screen.Screen1 && } - {currentPage === Screen.Screen2 && } - {currentPage === Screen.Screen3 && } - {currentPage === Screen.Screen4 && } + {/* Content Area */} +
+ {menuItems.find(item => item.id === currentPage)?.component}
-
- +
+
); }; From f446288b45564cf3b9195ea36eee582bb5af02c7 Mon Sep 17 00:00:00 2001 From: Glo333 Date: Sat, 25 Jan 2025 20:10:15 +0100 Subject: [PATCH 5/9] made fixes --- src/app/components/screens/Screen1.tsx | 362 ++++++++++++++----------- 1 file changed, 206 insertions(+), 156 deletions(-) diff --git a/src/app/components/screens/Screen1.tsx b/src/app/components/screens/Screen1.tsx index eb78c4e..2d11690 100644 --- a/src/app/components/screens/Screen1.tsx +++ b/src/app/components/screens/Screen1.tsx @@ -2,205 +2,255 @@ import React, { useState } from "react"; import { RxDownload } from "react-icons/rx"; import { FaRegPenToSquare } from "react-icons/fa6"; import axiosInstance from "@/app/utils/axiosInstance"; -// import { useRouter } from "next/navigation"; import ReactMarkdown from "react-markdown"; +interface InterviewPrepData { + job_title: string; + job_description: string; + interview_date: string | null; + resume: string; +} + +interface InterviewResponse { + questions_answers_value: string; +} + +const MAX_FILE_SIZE = 1024 * 1024; // 1MB const Screen1 = () => { + const [formData, setFormData] = useState({ + job_title: "", + job_description: "", + interview_date: null, + resume: "" + }); const [file, setFile] = useState(null); - const [jobTitle, setJobTitle] = useState(""); - const [jobDescription, setJobDescription] = useState(""); - const [interviewDate, setInterviewDate] = useState(null); - const [resume, setResume] = useState(""); - const [questionsAnswers, setQuestionsAnswers] = useState(null); + const [questionsAnswers, setQuestionsAnswers] = useState(null); const [loading, setLoading] = useState(false); - const [error, setError] = useState(""); - // const router = useRouter(); + const [error, setError] = useState<{[key: string]: string}>({}); + + const handleInputChange = ( + e: React.ChangeEvent + ) => { + const { name, value } = e.target; + setFormData(prev => ({ ...prev, [name]: value })); + setError(prev => ({ ...prev, [name]: "" })); + }; + + const validateForm = (): boolean => { + const newErrors: {[key: string]: string} = {}; + + if (!formData.job_title.trim()) { + newErrors.job_title = "Job title is required"; + } + if (!formData.job_description.trim()) { + newErrors.job_description = "Job description is required"; + } + if (!formData.resume.trim() && !file) { + newErrors.resume = "Either resume text or file is required"; + } - // interface InterviewPrep{ - // job_title:string, - // job_description: string, - // interview_date: Date - // } + setError(newErrors); + return Object.keys(newErrors).length === 0; + }; - // const handleSubmit = async (e: React.MouseEvent) => { e.preventDefault(); - // console.log("clicked"); + + if (!validateForm()) return; + setLoading(true); + setError({}); + try { - const response = await axiosInstance.post("/interview-prep", { - job_title: jobTitle, - job_description: jobDescription, - interview_date: interviewDate, - resume: resume, - }); - console.log(response); + const response = await axiosInstance.post( + "/interview-prep/", + formData, + { + headers: { + 'Content-Type': 'application/json' + }, + timeout: 30000 // 30 second timeout + } + ); + setQuestionsAnswers(response.data.questions_answers_value); - console.log(setQuestionsAnswers(response.data.questions_answers_value)); - - setLoading(false); - // console.log(questionsAnswers); - } catch (err) { - console.error("Error during interview preparation:", err); - setError("Failed to generate interview preparation data."); - // router.push("/login") + } catch (err: any) { + const errorMessage = err.response?.data?.detail || + "Failed to generate interview preparation data."; + setError({ submit: errorMessage }); + + if (err.response?.status === 401) { + // Handle unauthorized error - could redirect to login + console.error("Authentication error - token may have expired"); + } } finally { setLoading(false); } }; - // file uploading const handleFileChange = (e: React.ChangeEvent) => { - setFile(e.target.files?.[0] || null); - }; - - const handleFile = async (e: React.FormEvent) => { - e.preventDefault(); - - if (file) { - // Only proceed if file is not null - const formData = new FormData(); - formData.append("file", file); - - try { - const response = await fetch("/api/upload", { - method: "POST", - body: formData, - }); - - if (response.ok) { - console.log("File uploaded successfully"); - } else { - console.error("File upload failed"); - } - } catch (error) { - console.error("Error uploading file:", error); - } + const selectedFile = e.target.files?.[0]; + + if (!selectedFile) return; + + if (selectedFile.size > MAX_FILE_SIZE) { + setError({ file: "File size must be less than 1MB" }); + return; } + + if (!["application/pdf", "application/msword", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document" + ].includes(selectedFile.type)) { + setError({ file: "Only PDF and DOCX files are allowed" }); + return; + } + + setFile(selectedFile); + setError(prev => ({ ...prev, file: "", resume: "" })); }; + return ( - <> -
-

Interview Prep

-

- Interview Prep helps you get comprehensive access to possible - questions and their answers in preparation for your next job - interview. -

-
-

- New Interview Prep -

-
-
- +
+

Interview Prep

+

+ Interview Prep helps you get comprehensive access to possible questions + and their answers in preparation for your next job interview. +

+ + +

New Interview Prep

+
+ + {/* Input Fields */} +
+
+ setJobTitle(e.target.value)} - value={jobTitle} + id="job_title" + name="job_title" type="text" + value={formData.job_title} + onChange={handleInputChange} placeholder="Enter Job Title" - className={`border ${ - error ? "border-red-600" : "border" - } text-[18px] py-2 px-3 rounded-[10px] w-[100%] `} + className={`border ${error.job_title ? 'border-red-600' : 'border-gray-300'} + text-lg py-2 px-3 rounded-lg w-full`} /> - {error &&

please input field

} + {error.job_title && ( +

{error.job_title}

+ )}
+
- + - {error &&

please input field

} + className={`border ${error.job_description ? 'border-red-600' : 'border-gray-300'} + text-lg py-2 px-3 rounded-lg sm:h-48`} + /> + {error.job_description && ( +

{error.job_description}

+ )}
+
- + setInterviewDate(e.target.value)} - className="border py-2 px-3 rounded-[10px] w-[100%]" - required + value={formData.interview_date || ""} + onChange={handleInputChange} + className="border border-gray-300 py-2 px-3 rounded-lg w-full" />
- {/* file uploading */} -
-
- -
-
- - -

PDF, DOCX

-

(max, 1mb)

+ + {/* File Upload */} +
+
+
+ +
+ +
+ + +

PDF, DOCX (max. 1MB)

+ {error.file && ( +

{error.file}

+ )} + {file && ( +

+ Selected: {file.name} +

+ )} +
-
+ + {/* Resume Text Area */} +

Or paste CV/Resume Text

-
- +
+ + name="resume" + value={formData.resume} + onChange={handleInputChange} + placeholder="Paste CV/Resume" + className="border outline-none px-12 sm:px-16 py-4 rounded-lg w-full sm:h-64" + /> + {error.resume && ( +

{error.resume}

+ )}
- - - {/* ressume answer */} - {questionsAnswers && ( -
-

- Interview Questions and Answers -

- - {questionsAnswers} - - - {/*

- {JSON.stringify(questionsAnswers, null, 2)} -

*/} -
+
+ + {error.submit && ( +

{error.submit}

)} -
- + + + + + {/* Results Section */} + {questionsAnswers && ( +
+

+ Interview Questions and Answers +

+ + {questionsAnswers} + +
+ )} +
); }; -export default Screen1; +export default Screen1; \ No newline at end of file From a3d24219a0d6a2f8d63ee7ebefed49c81ceaa3a2 Mon Sep 17 00:00:00 2001 From: Glo333 Date: Sat, 25 Jan 2025 20:14:25 +0100 Subject: [PATCH 6/9] made fixes --- src/app/components/screens/Screen1.tsx | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/app/components/screens/Screen1.tsx b/src/app/components/screens/Screen1.tsx index 2d11690..3229dab 100644 --- a/src/app/components/screens/Screen1.tsx +++ b/src/app/components/screens/Screen1.tsx @@ -3,6 +3,7 @@ import { RxDownload } from "react-icons/rx"; import { FaRegPenToSquare } from "react-icons/fa6"; import axiosInstance from "@/app/utils/axiosInstance"; import ReactMarkdown from "react-markdown"; +import { AxiosError } from "axios"; interface InterviewPrepData { job_title: string; @@ -15,6 +16,11 @@ interface InterviewResponse { questions_answers_value: string; } +interface ApiError { + detail?: string; + message?: string; +} + const MAX_FILE_SIZE = 1024 * 1024; // 1MB const Screen1 = () => { @@ -54,7 +60,7 @@ const Screen1 = () => { return Object.keys(newErrors).length === 0; }; - const handleSubmit = async (e: React.MouseEvent) => { + const handleSubmit = async (e: React.MouseEvent) => { e.preventDefault(); if (!validateForm()) return; @@ -70,18 +76,20 @@ const Screen1 = () => { headers: { 'Content-Type': 'application/json' }, - timeout: 30000 // 30 second timeout + timeout: 30000 } ); setQuestionsAnswers(response.data.questions_answers_value); - } catch (err: any) { - const errorMessage = err.response?.data?.detail || + } catch (err) { + const error = err as AxiosError; + const errorMessage = error.response?.data?.detail || + error.response?.data?.message || "Failed to generate interview preparation data."; + setError({ submit: errorMessage }); - if (err.response?.status === 401) { - // Handle unauthorized error - could redirect to login + if (error.response?.status === 401) { console.error("Authentication error - token may have expired"); } } finally { @@ -122,7 +130,6 @@ const Screen1 = () => {

New Interview Prep


- {/* Input Fields */}
@@ -169,7 +176,6 @@ const Screen1 = () => { />
- {/* File Upload */}
@@ -204,7 +210,6 @@ const Screen1 = () => {
- {/* Resume Text Area */}

Or paste CV/Resume Text

@@ -238,7 +243,6 @@ const Screen1 = () => { - {/* Results Section */} {questionsAnswers && (

From ccb29b4cd4f60c09cb13f198d4fb79e6940ff547 Mon Sep 17 00:00:00 2001 From: Glo333 Date: Sat, 25 Jan 2025 20:20:35 +0100 Subject: [PATCH 7/9] debugging relating to axios --- src/app/utils/axiosInstance.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/utils/axiosInstance.ts b/src/app/utils/axiosInstance.ts index e9a21b0..1818237 100644 --- a/src/app/utils/axiosInstance.ts +++ b/src/app/utils/axiosInstance.ts @@ -15,7 +15,8 @@ axiosInstance.interceptors.request.use( // Retrieve the token from localStorage using the correct key const token = localStorage.getItem('access_token'); if (token) { - config.headers.Authorization = `Bearer ${token}`; + config.headers['Authorization'] = `Bearer ${token}`; + console.log('Token being sent:', token); } return config; }, From c75de40d0ae9e849e24f4a99785aa73b62de1ad4 Mon Sep 17 00:00:00 2001 From: Oladeji Date: Fri, 31 Jan 2025 10:43:00 +0100 Subject: [PATCH 8/9] added new themes --- package-lock.json | 11 +++++++ package.json | 1 + src/app/components/lightDarkModes.tsx | 45 +++++++++++++++++++++++++++ src/app/components/themeProvider.tsx | 11 +++++++ src/app/globals.css | 6 +++- src/app/layout.tsx | 31 +++++++----------- src/app/page.tsx | 25 +++++++++++---- 7 files changed, 103 insertions(+), 27 deletions(-) create mode 100644 src/app/components/lightDarkModes.tsx create mode 100644 src/app/components/themeProvider.tsx diff --git a/package-lock.json b/package-lock.json index b9027f4..47fdfb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "axios": "^1.7.7", "next": "15.0.1", + "next-themes": "^0.4.4", "react": "^18.3.1", "react-dom": "^18.3.1", "react-icons": "^5.3.0", @@ -4659,6 +4660,16 @@ } } }, + "node_modules/next-themes": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.4.tgz", + "integrity": "sha512-LDQ2qIOJF0VnuVrrMSMLrWGjRMkq+0mpgl6e0juCLqdJ+oo8Q84JRWT6Wh11VDQKkMMe+dVzDKLWs5n87T+PkQ==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", diff --git a/package.json b/package.json index 8a96021..7bb7c21 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "axios": "^1.7.7", "next": "15.0.1", + "next-themes": "^0.4.4", "react": "^18.3.1", "react-dom": "^18.3.1", "react-icons": "^5.3.0", diff --git a/src/app/components/lightDarkModes.tsx b/src/app/components/lightDarkModes.tsx new file mode 100644 index 0000000..e59961e --- /dev/null +++ b/src/app/components/lightDarkModes.tsx @@ -0,0 +1,45 @@ +'use client' +import React, { useState, useEffect } from "react"; +import { TiWeatherSunny } from "react-icons/ti"; +import { useTheme } from "next-themes"; + + +const lightDarkModes = () => { + // const[mode, setMode] = useState(false) + const[options, setOptions] = useState(false) + const {theme, setTheme} = useTheme() + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); + // console.log(setTheme); + console.log("Current Theme:", theme); + + +// switch btwn light and dark mode + const toggleModes = () => { + setOptions(!options) + } + + return ( + <> +
+ + {options && ( +
+

{setTheme('light'); setOptions(false)}}>light

+

{setTheme('dark'); setOptions(false)}}>dark

+

{setTheme('system'); setOptions(false)}}>system

+
)} + +
+ + + ) +} + +export default lightDarkModes \ No newline at end of file diff --git a/src/app/components/themeProvider.tsx b/src/app/components/themeProvider.tsx new file mode 100644 index 0000000..6a1ffe4 --- /dev/null +++ b/src/app/components/themeProvider.tsx @@ -0,0 +1,11 @@ +"use client" + +import * as React from "react" +import { ThemeProvider as NextThemesProvider } from "next-themes" + +export function ThemeProvider({ + children, + ...props +}: React.ComponentProps) { + return {children} +} diff --git a/src/app/globals.css b/src/app/globals.css index 59b5888..f74248e 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -2,10 +2,14 @@ @tailwind components; @tailwind utilities; + +html { + @apply bg-white text-black dark:bg-black dark:text-white; +} + :root { --background: #ffffff; --foreground: #171717; - color-scheme: light; } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 27307f3..91981b8 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,8 +1,8 @@ +// 'use client' import type { Metadata } from "next"; import localFont from "next/font/local"; import "./globals.css"; -// import { Html, Head, Main, NextScript } from "next/document"; -// import type { Viewport } from 'next' +import { ThemeProvider } from "./components/themeProvider"; const geistSans = localFont({ src: "./fonts/GeistVF.woff", @@ -20,34 +20,25 @@ export const metadata: Metadata = { description: "Generated by create next app", }; - - export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { + return ( - {/* */} - {/* Theme color for light and dark modes */} - {/* */} - {/* */} - {/* */} - {children} + + {children} + ); } - diff --git a/src/app/page.tsx b/src/app/page.tsx index f41bd4c..1f7ea3f 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,8 +1,10 @@ "use client"; -// import React, { useState } from "react"; import Link from "next/link"; import SignUp from "@/app/components/SignUp"; -import Modal from '@/app/components/Modal' +import Modal from "@/app/components/Modal"; +import Modes from "@/app/components/lightDarkModes" + + // import useState from 'react' const page = () => { @@ -10,6 +12,7 @@ const page = () => { <>
+

deji___
school @@ -29,16 +32,26 @@ const page = () => {

{/* mobile screen sign up component */} -
+
- +
- sign up - Log In + + sign up + + + Log In +
From 6790e91f3437ec86c1fbeb6b9b0e3a129c4fa66c Mon Sep 17 00:00:00 2001 From: Oladeji Date: Fri, 31 Jan 2025 10:56:41 +0100 Subject: [PATCH 9/9] BUG FIX --- src/app/components/lightDarkModes.tsx | 1 + src/app/layout.tsx | 2 +- src/app/page.tsx | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/components/lightDarkModes.tsx b/src/app/components/lightDarkModes.tsx index e59961e..cf5bec8 100644 --- a/src/app/components/lightDarkModes.tsx +++ b/src/app/components/lightDarkModes.tsx @@ -13,6 +13,7 @@ const lightDarkModes = () => { useEffect(() => { setMounted(true); }, []); + if (!mounted) return null; // console.log(setTheme); console.log("Current Theme:", theme); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 91981b8..bb77cdc 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -2,7 +2,7 @@ import type { Metadata } from "next"; import localFont from "next/font/local"; import "./globals.css"; -import { ThemeProvider } from "./components/themeProvider"; +import { ThemeProvider } from "./components/ThemeProvider"; const geistSans = localFont({ src: "./fonts/GeistVF.woff", diff --git a/src/app/page.tsx b/src/app/page.tsx index 1f7ea3f..93821dd 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -2,7 +2,7 @@ import Link from "next/link"; import SignUp from "@/app/components/SignUp"; import Modal from "@/app/components/Modal"; -import Modes from "@/app/components/lightDarkModes" +import Modes from "@/app/components/LightDarkModes" // import useState from 'react'