diff --git a/api/afdb/userName.ts b/api/afdb/userName.ts new file mode 100644 index 0000000..2d15c41 --- /dev/null +++ b/api/afdb/userName.ts @@ -0,0 +1,27 @@ +"use server" + +import axios from 'axios'; +import getAxiosConfig from '../axiosConfig'; +import { User } from '@/app/types'; + +export const getUserName = async (studentId: string): Promise => { + const url = process.env.AF_DB_SERVICE_URL; + const bearerToken = process.env.AF_DB_SERVICE_BEARER_TOKEN!; + + try { + const response = await axios.get(`${url}/student`, { + params: { student_id: studentId }, + ...getAxiosConfig(bearerToken), + }); + + if (response.data.length === 0) { + console.warn(`No user found for student ID: ${studentId}`); + return null; + } + + return response.data[0].user; + } catch (error) { + console.error('Error fetching student data:', error); + throw error; + } +} diff --git a/api/reporting/reports.ts b/api/reporting/reports.ts index fcdfeeb..20d02fe 100644 --- a/api/reporting/reports.ts +++ b/api/reporting/reports.ts @@ -3,11 +3,9 @@ import { api } from "@/services/url"; import axios from "axios"; -export async function getReports() { +export async function getReports(userId: string) { const apiKey = process.env.AF_REPORTS_DB_API_KEY; - // Temporary till we implement tokens in portal - const studentId = process.env.STUDENT_ID; - const url = `${api.reports.baseUrl}${api.reports.student_reports}${studentId}?format=json`; + const url = `${api.reports.baseUrl}${api.reports.student_reports}${userId}?format=json`; try { const responseData = await axios.get(url, { diff --git a/app/page.tsx b/app/page.tsx index 273f576..02b44b1 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -18,7 +18,7 @@ export default function Home() { const [isLoading, setIsLoading] = useState(true); const [quizzes, setQuizzes] = useState([]); const commonTextClass = "text-gray-700 text-sm md:text-base mx-6 md:mx-8"; - const infoMessageClass = "flex items-center justify-center text-center h-72 mx-4"; + const infoMessageClass = "flex items-center justify-center text-center h-72 mx-4 pb-40"; const fetchSessionOccurrencesAndDetails = async () => { try { @@ -82,7 +82,7 @@ export default function Home() { ); } } else if (data.sessionDetail.platform === 'quiz') { - generateQuizLink(data.sessionDetail.platform_link) + generateQuizLink(data.sessionDetail.platform_link, userId!) .then((quizLink) => { return ( @@ -99,105 +99,102 @@ export default function Home() { return null; } - useEffect(() => { - async function fetchData() { - setIsLoading(true); + const fetchData = async () => { + setIsLoading(true); - try { - await fetchSessionOccurrencesAndDetails(); - } catch (error) { - console.log("Error:", error) - } finally { - setIsLoading(false); - } + try { + await fetchSessionOccurrencesAndDetails(); + } catch (error) { + console.log("Error:", error); + } finally { + setIsLoading(false); } + }; - fetchData(); - }, []); + useEffect(() => { + if (loggedIn) { + fetchData(); + } + }, [loggedIn]); return ( <> - {/* {loggedIn ? ( */} - {isLoading ? ( -
- - -
- ) : ( -
- -
-

Live Classes

- {liveClasses.length > 0 ? ( -
- {liveClasses.map((data, index) => ( -
-
-

- {formatSessionTime(data.sessionOccurrence.start_time)} -

-

- {formatSessionTime(data.sessionOccurrence.end_time)} -

-
-
-
-
- Subject: {data.sessionDetail.meta_data.subject ?? "Science"} -
- Batch: {data.sessionDetail.meta_data.batch ?? "Master Batch"} + {loggedIn ? ( + isLoading ? ( +
+ + +
+ ) : ( +
+ +
+

Live Classes

+ {liveClasses.length > 0 ? ( +
+ {liveClasses.map((data, index) => ( +
+
+

+ {formatSessionTime(data.sessionOccurrence.start_time)} +

+

+ {formatSessionTime(data.sessionOccurrence.end_time)} +

+
+
+
+
+ Subject: {data.sessionDetail.meta_data.subject ?? "Science"} +
+ Batch: {data.sessionDetail.meta_data.batch ?? "Master Batch"} +
+ {renderButton(data)}
- {renderButton(data)}
-
- ))} -
) : ( -

- Good Job! There are no more pending live classes today. -

- )} -
-
-

Tests

- {quizzes.length > 0 ? ( -
- {quizzes.map((data, index) => ( -
-
-

- {formatSessionTime(data.sessionOccurrence.start_time)} -

-

- {formatSessionTime(data.sessionOccurrence.end_time)} -

-
-
-
-
- Subject: {data.sessionDetail.meta_data.stream} -
- Type: {data.sessionDetail.meta_data.test_type} + ))} +
) : ( +

+ There are no more live classes today. You can relax! +

+ )} +
+
+

Tests

+ {quizzes.length > 0 ? ( +
+ {quizzes.map((data, index) => ( +
+
+

+ {formatSessionTime(data.sessionOccurrence.start_time)} +

+

+ {formatSessionTime(data.sessionOccurrence.end_time)} +

+
+
+
+
+ Subject: {data.sessionDetail.meta_data.stream} +
+ Type: {data.sessionDetail.meta_data.test_type} +
+ {renderButton(data)}
- {renderButton(data)}
-
- ))} -
) : ( -

- Good Job! There are no more pending tests today. -

- )} -
-
- - Click on the link to see all upcoming Tests - -
- -
)} - {/* ) : ( + ))} + ) : ( +

+ There are no more tests today. You can relax! +

+ )} + + + ) + ) : (
@@ -205,7 +202,7 @@ export default function Home() {
- )} */} + )} ); } diff --git a/app/reports/page.tsx b/app/reports/page.tsx index 1390937..f49d032 100644 --- a/app/reports/page.tsx +++ b/app/reports/page.tsx @@ -8,25 +8,25 @@ import TopBar from "@/components/TopBar"; import { useAuth } from "../../services/AuthContext"; export default function ReportsPage() { - const { loggedIn } = useAuth(); + const { loggedIn, userId } = useAuth(); return ( <> - {/* {loggedIn ? ( */} -
- - - - - -
- {/* ) : ( + {loggedIn && userId ? ( +
+ + + + + +
+ ) : (
- )} */} + )} ); } diff --git a/app/reports/reports_list.tsx b/app/reports/reports_list.tsx index fa688f1..368e324 100644 --- a/app/reports/reports_list.tsx +++ b/app/reports/reports_list.tsx @@ -3,14 +3,15 @@ import { Report } from "../types"; import { useState, useEffect } from "react"; import Loading from "../loading"; import { getReports } from "@/api/reporting/reports"; +import { ReportsListProps } from "../types"; -export default function ReportsList() { +export default function ReportsList({ userId }: ReportsListProps) { const [responseData, setResponseData] = useState<{ reports: Report[] } | null>(null); useEffect(() => { async function fetchReportsData() { try { - const data = await getReports(); + const data = await getReports(userId); setResponseData(data); } catch (error) { throw error; @@ -18,7 +19,7 @@ export default function ReportsList() { } fetchReportsData(); - }, []); + }, [userId]); if (!responseData) { return ; @@ -26,15 +27,23 @@ export default function ReportsList() { return (
- {responseData.reports.map((report: Report, index: number) => ( - -
-
-

{report.test_name}

-

Rank: {report.rank}

-
- - ))} + {responseData.reports.length > 0 ? ( + <> + {responseData.reports.map((report: Report, index: number) => ( + +
+
+

{report.test_name}

+

Rank: {report.rank}

+
+ + ))} + + ) : ( +
+ Sorry! There are no reports avaiable. +
+ )}
); } diff --git a/app/types.ts b/app/types.ts index 693add8..5fdbd25 100644 --- a/app/types.ts +++ b/app/types.ts @@ -92,7 +92,16 @@ export interface LiveClasses { sessionOccurrence: SessionOccurrence; sessionDetail: Session; } +export interface ReportsListProps { + userId: string; +} export interface AxiosAdditionalHeaders { [key: string]: string; } + +export interface User { + id: number; + first_name: string; + last_name: string; +} diff --git a/components/TopBar.tsx b/components/TopBar.tsx index e9e6a6d..a72c5b5 100644 --- a/components/TopBar.tsx +++ b/components/TopBar.tsx @@ -18,7 +18,7 @@ const TopBar = () => { }; const pathname = usePathname(); - const routeName = routeNames[pathname] ||

Welcome,
Shagun Panday

; + const routeName = routeNames[pathname] ||

Welcome
{userName}

; const toggleDropdown = () => { setIsDropdownOpen(!isDropdownOpen); diff --git a/services/AuthContext.tsx b/services/AuthContext.tsx index 9607de3..fe30ea7 100644 --- a/services/AuthContext.tsx +++ b/services/AuthContext.tsx @@ -3,8 +3,9 @@ import React, { createContext, useContext, useState, useEffect } from 'react'; import { verifyToken } from '@/services/validation'; import { useRouter } from 'next/navigation'; -import { AuthContextProps } from '../app/types'; +import { AuthContextProps, User } from '../app/types'; import { api } from '@/services/url'; +import { getUserName } from '@/api/afdb/userName'; const AuthContext = createContext(undefined); @@ -20,7 +21,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const router = useRouter(); const [loggedIn, setLoggedIn] = useState(false); const [userId, setUserId] = useState(null); - const [userName, setUserName] = useState(null); + const [user, setUser] = useState(null); useEffect(() => { async function checkToken() { @@ -29,11 +30,12 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { if (result.isValid) { setLoggedIn(true); setUserId(result.data.id); - setUserName(result.data.data.name) + const userData = await getUserName(result.data.id); + setUser(userData); } else { setLoggedIn(false); setUserId(null); - // router.push(`${api.portal.frontend.baseUrl}`); + router.push(`${api.portal.frontend.baseUrl}`); } } catch (error) { console.error('Error verifying token:', error); @@ -45,6 +47,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { checkToken(); }, []); + const userName = user ? `${user.first_name} ${user.last_name}` : ''; + return ( {children} diff --git a/services/url.ts b/services/url.ts index 1b058cc..f8fd635 100644 --- a/services/url.ts +++ b/services/url.ts @@ -5,11 +5,12 @@ export const api = { }, backend: { baseUrl: process.env.NEXT_PUBLIC_AF_PORTAL_BACKEND_URL || '', - verify: '/verify', + verify: '/auth/verify', + refreshToken: '/auth/refresh-token' } }, reports: { baseUrl: process.env.AF_REPORTS_URL || '', - student_reports: '/student_reports/', + student_reports: '/reports/student_reports/', } } diff --git a/services/validation.ts b/services/validation.ts index df6ee5d..66d6ebd 100644 --- a/services/validation.ts +++ b/services/validation.ts @@ -1,24 +1,39 @@ import axios from 'axios'; -import { getCookie } from 'cookies-next'; +import { getCookie, setCookie } from 'cookies-next'; import { api } from './url'; +import getAxiosConfig from '@/api/axiosConfig'; export async function verifyToken() { - const token = getCookie('access_token'); + const accessToken = getCookie('access_token'); + const refreshToken = getCookie('refresh_token'); const url = `${api.portal.backend.baseUrl}${api.portal.backend.verify}`; + const refreshUrl = `${api.portal.backend.baseUrl}${api.portal.backend.refreshToken}`; - if (!token) { - return { isValid: false, message: 'Token not found' }; + if (!accessToken) { + return { isValid: false, message: 'Access token not found' }; } try { const response = await axios.get(url, { - headers: { - Authorization: `Bearer ${token}`, - }, + ...getAxiosConfig(accessToken), }); + return { isValid: true, data: response.data }; - - } catch (error) { + } catch (error: any) { + if (error.response.data.detail === "Signature has expired" && refreshToken) { + try { + const refreshResponse = await axios.post(refreshUrl, {}, { + ...getAxiosConfig(refreshToken), + }); + + setCookie('access_token', refreshResponse.data.access_token); + + return { isValid: true }; + } catch (refreshError) { + return { isValid: false, message: 'Error refreshing token', refreshError }; + } + } + return { isValid: false, message: error }; } } diff --git a/utils/quizUtils.ts b/utils/quizUtils.ts index c28542e..cbdcf60 100644 --- a/utils/quizUtils.ts +++ b/utils/quizUtils.ts @@ -2,8 +2,7 @@ const baseUrl = process.env.AF_QUIZ_URL; const apiKey = process.env.AF_QUIZ_API_KEY; -const userId = process.env.AF_QUIZ_USER_ID; -export const generateQuizLink = async (platformLink: string) => { +export const generateQuizLink = async (platformLink: string, userId: string) => { return `${baseUrl}${platformLink}?apiKey=${apiKey}&userId=${userId}`; };