From 837cd9ffbb2bcddf383fc9b879ae8bd4723466df Mon Sep 17 00:00:00 2001 From: Bahugunajii Date: Wed, 13 Dec 2023 15:05:18 +0530 Subject: [PATCH 01/12] fetch userId from token --- api/reporting/reports.ts | 6 ++---- app/reports/page.tsx | 22 +++++++++++----------- app/reports/reports_list.tsx | 7 ++++--- app/types.ts | 4 +++- 4 files changed, 20 insertions(+), 19 deletions(-) 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/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..7bdd558 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 ; diff --git a/app/types.ts b/app/types.ts index 11693f3..b1ead85 100644 --- a/app/types.ts +++ b/app/types.ts @@ -87,4 +87,6 @@ export interface LiveClasses { sessionOccurrence: SessionOccurrence; sessionDetail: Session; } - +export interface ReportsListProps { + userId: string; +} From d24bfbcbf43761b766bbbc9ce7a18cd91ee2590b Mon Sep 17 00:00:00 2001 From: Bahugunajii Date: Thu, 21 Dec 2023 14:02:20 +0530 Subject: [PATCH 02/12] refresh the token if expired --- api/afdb/userName.ts | 23 +++++++++++++++++++++++ app/page.tsx | 26 ++++++++++++++------------ components/TopBar.tsx | 2 +- services/AuthContext.tsx | 6 ++++-- services/url.ts | 5 +++-- services/validation.ts | 30 ++++++++++++++++++++++++------ utils/quizUtils.ts | 3 +-- 7 files changed, 70 insertions(+), 25 deletions(-) create mode 100644 api/afdb/userName.ts diff --git a/api/afdb/userName.ts b/api/afdb/userName.ts new file mode 100644 index 0000000..e5c057d --- /dev/null +++ b/api/afdb/userName.ts @@ -0,0 +1,23 @@ +"use server" + +import axios from 'axios'; +import getAxiosConfig from '../axiosConfig'; + +export async function getUserName(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), + }); + + const firstName = response.data[0].user.first_name; + const lastName = response.data[0].user.last_name; + return `${firstName} ${lastName}`; + } catch (error) { + console.error('Error fetching student data:', error); + throw error; + } +} diff --git a/app/page.tsx b/app/page.tsx index c5d79bf..2015bd6 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -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 ? userId : '') .then((quizLink) => { return ( @@ -99,20 +99,22 @@ 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(); + } }, []); return ( diff --git a/components/TopBar.tsx b/components/TopBar.tsx index 8adc286..031972d 100644 --- a/components/TopBar.tsx +++ b/components/TopBar.tsx @@ -13,7 +13,7 @@ const TopBar = () => { }; const pathname = usePathname(); - const routeName = routeNames[pathname] ||

Welcome,
Shagun Panday

; + const routeName = routeNames[pathname] ||

Welcome,
{userName}

; return (
diff --git a/services/AuthContext.tsx b/services/AuthContext.tsx index 9607de3..5061565 100644 --- a/services/AuthContext.tsx +++ b/services/AuthContext.tsx @@ -5,6 +5,7 @@ import { verifyToken } from '@/services/validation'; import { useRouter } from 'next/navigation'; import { AuthContextProps } from '../app/types'; import { api } from '@/services/url'; +import { getUserName } from '@/api/afdb/userName'; const AuthContext = createContext(undefined); @@ -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 studentName = await getUserName(result.data.id); + setUserName(studentName || '') } 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); 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..b1a3b43 100644 --- a/services/validation.ts +++ b/services/validation.ts @@ -1,24 +1,42 @@ import axios from 'axios'; -import { getCookie } from 'cookies-next'; +import { getCookie, setCookie } from 'cookies-next'; import { api } from './url'; 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}`, + Authorization: `Bearer ${accessToken}`, }, }); + return { isValid: true, data: response.data }; - } catch (error) { + if (error && refreshToken) { + try { + const refreshResponse = await axios.post(refreshUrl, {}, { + headers: { + Authorization: `Bearer ${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}`; }; From 4c0de5cfbe6aaee78053c5e15cf50f113e3aec77 Mon Sep 17 00:00:00 2001 From: Bahugunajii Date: Thu, 21 Dec 2023 15:12:39 +0530 Subject: [PATCH 03/12] added loggedIn to dependency variable --- app/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/page.tsx b/app/page.tsx index 2015bd6..ccfc002 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -115,7 +115,7 @@ export default function Home() { if (loggedIn) { fetchData(); } - }, []); + }, [loggedIn]); return ( <> From fd3025461f2ebe06207df5652cd646f2f5e2e147 Mon Sep 17 00:00:00 2001 From: Bahugunajii Date: Thu, 21 Dec 2023 15:19:19 +0530 Subject: [PATCH 04/12] minor changes --- app/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/page.tsx b/app/page.tsx index ccfc002..9f9c34c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -82,7 +82,7 @@ export default function Home() { ); } } else if (data.sessionDetail.platform === 'quiz') { - generateQuizLink(data.sessionDetail.platform_link, userId ? userId : '') + generateQuizLink(data.sessionDetail.platform_link, userId || '') .then((quizLink) => { return ( From 344a500febe165569558057e1f2a501012d3b78c Mon Sep 17 00:00:00 2001 From: Bahugunajii Date: Thu, 21 Dec 2023 15:38:14 +0530 Subject: [PATCH 05/12] handle token expiry error --- services/validation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/validation.ts b/services/validation.ts index b1a3b43..3f23ba6 100644 --- a/services/validation.ts +++ b/services/validation.ts @@ -20,8 +20,8 @@ export async function verifyToken() { }); return { isValid: true, data: response.data }; - } catch (error) { - if (error && refreshToken) { + } catch (error: any) { + if (error.response.data.detail === "Signature has expired" && refreshToken) { try { const refreshResponse = await axios.post(refreshUrl, {}, { headers: { From bd0a4f67ddc69fdaef48ca53e4dc7c2305260a71 Mon Sep 17 00:00:00 2001 From: Bahugunajii Date: Thu, 21 Dec 2023 15:56:42 +0530 Subject: [PATCH 06/12] code optimizations --- services/validation.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/services/validation.ts b/services/validation.ts index 3f23ba6..a8bcbff 100644 --- a/services/validation.ts +++ b/services/validation.ts @@ -1,6 +1,7 @@ import axios from 'axios'; import { getCookie, setCookie } from 'cookies-next'; import { api } from './url'; +import getAxiosConfig from '@/api/axiosConfig'; export async function verifyToken() { const accessToken = getCookie('access_token'); @@ -14,19 +15,15 @@ export async function verifyToken() { try { const response = await axios.get(url, { - headers: { - Authorization: `Bearer ${accessToken}`, - }, + ...getAxiosConfig(accessToken), }); return { isValid: true, data: response.data }; } catch (error: any) { if (error.response.data.detail === "Signature has expired" && refreshToken) { try { - const refreshResponse = await axios.post(refreshUrl, {}, { - headers: { - Authorization: `Bearer ${refreshToken}`, - }, + const refreshResponse = await axios.post(refreshUrl, { + ...getAxiosConfig(refreshToken), }); setCookie('access_token', refreshResponse.data.access_token); From 1f32f23b4c0b63f77aff8419cda91940bcf5ea15 Mon Sep 17 00:00:00 2001 From: Bahugunajii Date: Thu, 21 Dec 2023 16:27:45 +0530 Subject: [PATCH 07/12] suggested changes --- api/afdb/userName.ts | 2 +- app/page.tsx | 2 +- app/reports/reports_list.tsx | 26 +++++++++++++++++--------- services/AuthContext.tsx | 2 +- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/api/afdb/userName.ts b/api/afdb/userName.ts index e5c057d..50d91fc 100644 --- a/api/afdb/userName.ts +++ b/api/afdb/userName.ts @@ -5,7 +5,7 @@ import getAxiosConfig from '../axiosConfig'; export async function getUserName(studentId: string): Promise { const url = process.env.AF_DB_SERVICE_URL; - const bearerToken = process.env.AF_DB_SERVICE_BEARER_TOKEN || ''; + const bearerToken = process.env.AF_DB_SERVICE_BEARER_TOKEN!; try { const response = await axios.get(`${url}/student`, { diff --git a/app/page.tsx b/app/page.tsx index 9f9c34c..3b547b5 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -82,7 +82,7 @@ export default function Home() { ); } } else if (data.sessionDetail.platform === 'quiz') { - generateQuizLink(data.sessionDetail.platform_link, userId || '') + generateQuizLink(data.sessionDetail.platform_link, userId!) .then((quizLink) => { return ( diff --git a/app/reports/reports_list.tsx b/app/reports/reports_list.tsx index 7bdd558..88ff0c8 100644 --- a/app/reports/reports_list.tsx +++ b/app/reports/reports_list.tsx @@ -27,15 +27,23 @@ export default function ReportsList({ userId }: ReportsListProps) { return (
- {responseData.reports.map((report: Report, index: number) => ( - -
-
-

{report.test_name}

-

Rank: {report.rank}

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

{report.test_name}

+

Rank: {report.rank}

+
+ + ))} + + ) : ( +
+ Sorry! There are no reports avaiable +
+ )}
); } diff --git a/services/AuthContext.tsx b/services/AuthContext.tsx index 5061565..a77737d 100644 --- a/services/AuthContext.tsx +++ b/services/AuthContext.tsx @@ -31,7 +31,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { setLoggedIn(true); setUserId(result.data.id); const studentName = await getUserName(result.data.id); - setUserName(studentName || '') + setUserName(studentName) } else { setLoggedIn(false); setUserId(null); From dc22ad8f33b155bfd8be7f04b43efd61be26f662 Mon Sep 17 00:00:00 2001 From: Bahugunajii Date: Thu, 21 Dec 2023 18:56:32 +0530 Subject: [PATCH 08/12] suggested changes --- services/validation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/validation.ts b/services/validation.ts index a8bcbff..66d6ebd 100644 --- a/services/validation.ts +++ b/services/validation.ts @@ -22,7 +22,7 @@ export async function verifyToken() { } catch (error: any) { if (error.response.data.detail === "Signature has expired" && refreshToken) { try { - const refreshResponse = await axios.post(refreshUrl, { + const refreshResponse = await axios.post(refreshUrl, {}, { ...getAxiosConfig(refreshToken), }); From c70c13a8364288165e9c2b151ec350928d61ffe2 Mon Sep 17 00:00:00 2001 From: Bahugunajii Date: Thu, 21 Dec 2023 20:50:30 +0530 Subject: [PATCH 09/12] suggested changes --- api/afdb/userName.ts | 7 ++++++- components/TopBar.tsx | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/api/afdb/userName.ts b/api/afdb/userName.ts index 50d91fc..3c6694a 100644 --- a/api/afdb/userName.ts +++ b/api/afdb/userName.ts @@ -3,7 +3,7 @@ import axios from 'axios'; import getAxiosConfig from '../axiosConfig'; -export async function getUserName(studentId: string): Promise { +export const getUserName = async (studentId: string): Promise => { const url = process.env.AF_DB_SERVICE_URL; const bearerToken = process.env.AF_DB_SERVICE_BEARER_TOKEN!; @@ -13,6 +13,11 @@ export async function getUserName(studentId: string): Promise { ...getAxiosConfig(bearerToken), }); + if (response.data.length === 0) { + console.warn(`No user found for student ID: ${studentId}`); + return null; + } + const firstName = response.data[0].user.first_name; const lastName = response.data[0].user.last_name; return `${firstName} ${lastName}`; diff --git a/components/TopBar.tsx b/components/TopBar.tsx index 031972d..246941c 100644 --- a/components/TopBar.tsx +++ b/components/TopBar.tsx @@ -13,7 +13,7 @@ const TopBar = () => { }; const pathname = usePathname(); - const routeName = routeNames[pathname] ||

Welcome,
{userName}

; + const routeName = routeNames[pathname] ||

Welcome
{userName}

; return (
From 0a51ae2bdf7033c59936fc48cd9082fb79c106b7 Mon Sep 17 00:00:00 2001 From: Bahugunajii Date: Thu, 21 Dec 2023 21:23:29 +0530 Subject: [PATCH 10/12] added suggested changes --- app/page.tsx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 3b547b5..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 { @@ -156,7 +156,7 @@ export default function Home() { ))}
) : (

- Good Job! There are no more pending live classes today. + There are no more live classes today. You can relax!

)}
@@ -188,15 +188,10 @@ export default function Home() { ))} ) : (

- Good Job! There are no more pending tests today. + There are no more tests today. You can relax!

)} -
- - Click on the link to see all upcoming Tests - -
) ) : ( From 0f2d8f7fbeb73dbbc4769b67a67e113c282d944c Mon Sep 17 00:00:00 2001 From: Bahugunajii Date: Thu, 21 Dec 2023 21:30:23 +0530 Subject: [PATCH 11/12] added suggested changes --- app/reports/reports_list.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/reports/reports_list.tsx b/app/reports/reports_list.tsx index 88ff0c8..368e324 100644 --- a/app/reports/reports_list.tsx +++ b/app/reports/reports_list.tsx @@ -27,7 +27,7 @@ export default function ReportsList({ userId }: ReportsListProps) { return (
- {responseData.reports ? ( + {responseData.reports.length > 0 ? ( <> {responseData.reports.map((report: Report, index: number) => ( @@ -40,8 +40,8 @@ export default function ReportsList({ userId }: ReportsListProps) { ))} ) : ( -
- Sorry! There are no reports avaiable +
+ Sorry! There are no reports avaiable.
)}
From 57c19459c3d04c873dcf5ebaa2da6c115c5fdb37 Mon Sep 17 00:00:00 2001 From: Bahugunajii Date: Fri, 22 Dec 2023 12:37:22 +0530 Subject: [PATCH 12/12] suggested changes --- api/afdb/userName.ts | 7 +++---- app/types.ts | 6 ++++++ services/AuthContext.tsx | 10 ++++++---- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/api/afdb/userName.ts b/api/afdb/userName.ts index 3c6694a..2d15c41 100644 --- a/api/afdb/userName.ts +++ b/api/afdb/userName.ts @@ -2,8 +2,9 @@ import axios from 'axios'; import getAxiosConfig from '../axiosConfig'; +import { User } from '@/app/types'; -export const getUserName = async (studentId: string): Promise => { +export const getUserName = async (studentId: string): Promise => { const url = process.env.AF_DB_SERVICE_URL; const bearerToken = process.env.AF_DB_SERVICE_BEARER_TOKEN!; @@ -18,9 +19,7 @@ export const getUserName = async (studentId: string): Promise => return null; } - const firstName = response.data[0].user.first_name; - const lastName = response.data[0].user.last_name; - return `${firstName} ${lastName}`; + return response.data[0].user; } catch (error) { console.error('Error fetching student data:', error); throw error; diff --git a/app/types.ts b/app/types.ts index 2f9c06b..5fdbd25 100644 --- a/app/types.ts +++ b/app/types.ts @@ -99,3 +99,9 @@ export interface ReportsListProps { export interface AxiosAdditionalHeaders { [key: string]: string; } + +export interface User { + id: number; + first_name: string; + last_name: string; +} diff --git a/services/AuthContext.tsx b/services/AuthContext.tsx index a77737d..fe30ea7 100644 --- a/services/AuthContext.tsx +++ b/services/AuthContext.tsx @@ -3,7 +3,7 @@ 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'; @@ -21,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() { @@ -30,8 +30,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { if (result.isValid) { setLoggedIn(true); setUserId(result.data.id); - const studentName = await getUserName(result.data.id); - setUserName(studentName) + const userData = await getUserName(result.data.id); + setUser(userData); } else { setLoggedIn(false); setUserId(null); @@ -47,6 +47,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { checkToken(); }, []); + const userName = user ? `${user.first_name} ${user.last_name}` : ''; + return ( {children}