From 58d4d14a5102703903d40f4d19bb1e3af6bfdcc8 Mon Sep 17 00:00:00 2001 From: Dacca Retro Date: Mon, 18 Aug 2025 15:06:50 +0600 Subject: [PATCH] fix(api): fix api endpoint logic #5 chore(env): obscure api url in env feat(ui): render subjects according to user preparation unit --- .env.example | 1 + .gitignore | 2 +- app/(tabs)/layout.tsx | 2 +- app/(tabs)/leaderboard/page.tsx | 42 +- app/(tabs)/paper/page.tsx | 122 - app/(tabs)/subjects/page.tsx | 228 ++ app/(tabs)/{unit => units}/page.tsx | 21 +- app/exam/pretest/page.tsx | 1 - bun.lockb | Bin 209837 -> 0 bytes components/ProfileManager.tsx | 2 - context/AuthContext.tsx | 2 +- lib/auth.ts | 2 +- package-lock.json | 4001 ++++++++++++++++++++++++--- package.json | 4 +- public/data/questions.json | 0 15 files changed, 3853 insertions(+), 577 deletions(-) create mode 100644 .env.example delete mode 100644 app/(tabs)/paper/page.tsx create mode 100644 app/(tabs)/subjects/page.tsx rename app/(tabs)/{unit => units}/page.tsx (81%) delete mode 100644 bun.lockb create mode 100644 public/data/questions.json diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..bda589d --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +NEXT_PUBLIC_EXAMJAM_API_URL=api_url_here \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9c8a03f..5c6f0f2 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,7 @@ yarn-error.log* .pnpm-debug.log* # env files (can opt-in for committing if needed) -.env* +.env # vercel .vercel diff --git a/app/(tabs)/layout.tsx b/app/(tabs)/layout.tsx index d9e2e39..dffd47c 100644 --- a/app/(tabs)/layout.tsx +++ b/app/(tabs)/layout.tsx @@ -9,7 +9,7 @@ import { House, LayoutGrid, Bookmark, Settings } from "lucide-react"; const tabs = [ { name: "Home", href: "/home", component: }, - { name: "Unit", href: "/unit", component: }, + { name: "Subjects", href: "/subjects", component: }, { name: "Bookmark", href: "/bookmark", component: }, { name: "Settings", href: "/settings", component: }, ]; diff --git a/app/(tabs)/leaderboard/page.tsx b/app/(tabs)/leaderboard/page.tsx index ab7c524..ebfb0e8 100644 --- a/app/(tabs)/leaderboard/page.tsx +++ b/app/(tabs)/leaderboard/page.tsx @@ -8,49 +8,15 @@ import { API_URL, getToken } from "@/lib/auth"; import { BoardData, getLeaderboard } from "@/lib/leaderboard"; import { UserData } from "@/types/auth"; import React, { useEffect, useState } from "react"; +import { useAuth } from "@/context/AuthContext"; const LeaderboardPage = () => { const [boardError, setBoardError] = useState(null); const [boardData, setBoardData] = useState([]); - const [userData, setUserData] = useState({ - name: "", - institution: "", - sscRoll: "", - hscRoll: "", - email: "", - phone: "", - }); + const {user, isLoading} = useAuth() const [loading, setLoading] = useState(true); - useEffect(() => { - async function fetchUser() { - try { - const token = await getToken(); - if (!token) throw new Error("User is not authenticated"); - - const response = await fetch(`${API_URL}/me`, { - method: "get", - headers: { Authorization: `Bearer ${token}` }, - }); - - if (!response.ok) throw new Error("Failed to fetch user data"); - - const fetchedUserData = await response.json(); - setLoading(false); - setUserData(fetchedUserData); - } catch (error) { - console.error(error); - setUserData({ - name: "", - institution: "", - sscRoll: "", - hscRoll: "", - email: "", - phone: "", - }); - } - } async function fetchBoardData() { try { @@ -75,10 +41,8 @@ const LeaderboardPage = () => { setBoardData([]); } } - - fetchUser(); fetchBoardData(); - }, []); + } const getTopThree = (boardData: BoardData[]) => { if (!boardData || !Array.isArray(boardData)) return []; diff --git a/app/(tabs)/paper/page.tsx b/app/(tabs)/paper/page.tsx deleted file mode 100644 index 3e8123b..0000000 --- a/app/(tabs)/paper/page.tsx +++ /dev/null @@ -1,122 +0,0 @@ -"use client"; - -import { useSearchParams } from "next/navigation"; -import { useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; -import Header from "@/components/Header"; -import DestructibleAlert from "@/components/DestructibleAlert"; -import BackgroundWrapper from "@/components/BackgroundWrapper"; -import { API_URL } from "@/lib/auth"; -import { Loader, RefreshCw } from "lucide-react"; - -interface Mock { - id: string; - title: string; - rating: number; -} -export default function PaperScreen() { - const router = useRouter(); - const searchParams = useSearchParams(); - const name = searchParams.get("name") || ""; - - const [questions, setQuestions] = useState(null); - const [errorMsg, setErrorMsg] = useState(null); - const [refreshing, setRefreshing] = useState(false); - const [componentKey, setComponentKey] = useState(0); - - async function fetchMocks() { - try { - const questionResponse = await fetch(`${API_URL}/mocks`, { - method: "GET", - }); - const fetchedQuestionData: Mock[] = await questionResponse.json(); - setQuestions(fetchedQuestionData); - } catch (error) { - setErrorMsg(error instanceof Error ? error.message : "An error occurred"); - } - } - - useEffect(() => { - if (name) { - fetchMocks(); - } - }, [name]); - - const onRefresh = async () => { - setRefreshing(true); - - await fetchMocks(); - setComponentKey((prevKey) => prevKey + 1); - setTimeout(() => { - setRefreshing(false); - }, 1000); - }; - - if (errorMsg) { - return ( - -
-
-
- -
-
- -
-
- - ); - } - - return ( - -
-
-
-
- {questions ? ( - questions.map((mock) => ( -
- -
- )) - ) : ( -
-
-

Loading...

-
- )} -
-
- -
-
-
- {/* */} -
- ); -} diff --git a/app/(tabs)/subjects/page.tsx b/app/(tabs)/subjects/page.tsx new file mode 100644 index 0000000..7104faf --- /dev/null +++ b/app/(tabs)/subjects/page.tsx @@ -0,0 +1,228 @@ +"use client"; + +import { useSearchParams } from "next/navigation"; +import { useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import Header from "@/components/Header"; +import DestructibleAlert from "@/components/DestructibleAlert"; +import BackgroundWrapper from "@/components/BackgroundWrapper"; +import { API_URL } from "@/lib/auth"; +import { Loader, RefreshCw } from "lucide-react"; +import { v4 as uuidv4 } from "uuid"; +import { useAuth } from "@/context/AuthContext"; + +interface Mock { + id: string; + title: string; + rating: number; +} +export default function PaperScreen() { + const router = useRouter(); + const searchParams = useSearchParams(); + const name = searchParams.get("name") || ""; + const {user} = useAuth() + + const [questions, setQuestions] = useState(null); + const [errorMsg, setErrorMsg] = useState(null); + const [refreshing, setRefreshing] = useState(false); + const [componentKey, setComponentKey] = useState(0); + +interface Subject { + subject_id: string; + name: string; + unit: string; +} + +const [subjects, setSubjects] = useState([ + { + subject_id: uuidv4(), + name: "Biology", + unit: "Science" + }, + { + subject_id: uuidv4(), + name: "Chemistry", + unit: "Science" + }, + { + subject_id: uuidv4(), + name: "Physics", + unit: "Science" + }, + { + subject_id: uuidv4(), + name: "Accounting", + unit: "Business" + }, + { + subject_id: uuidv4(), + name: "Finance", + unit: "Business" + }, + { + subject_id: uuidv4(), + name: "Marketing", + unit: "Business" + }, + { + subject_id: uuidv4(), + name: "History", + unit: "Humanities" + }, + { + subject_id: uuidv4(), + name: "Geography", + unit: "Humanities" + }, + { + subject_id: uuidv4(), + name: "Sociology", + unit: "Humanities" + }, +]); + + + + // async function fetchMocks() { + // try { + // const questionResponse = await fetch(`${API_URL}/mocks`, { + // method: "GET", + // }); + // const fetchedQuestionData: Mock[] = await questionResponse.json(); + // setQuestions(fetchedQuestionData); + // } catch (error) { + // setErrorMsg(error instanceof Error ? error.message : "An error occurred"); + // } + // } + + // useEffect(() => { + // if (name) { + // fetchMocks(); + // } + // }, [name]); + + const onRefresh = async () => { + setRefreshing(true); + + setSubjects([{ + subject_id: uuidv4(), + name: "Biology", + unit: "Science" + }, + { + subject_id: uuidv4(), + name: "Chemistry", + unit: "Science" + }, + { + subject_id: uuidv4(), + name: "Physics", + unit: "Science" + }, + { + subject_id: uuidv4(), + name: "Accounting", + unit: "Business Studies" + }, + { + subject_id: uuidv4(), + name: "Finance", + unit: "Business Studies" + }, + { + subject_id: uuidv4(), + name: "Marketing", + unit: "Business Studies" + }, + { + subject_id: uuidv4(), + name: "History", + unit: "Humanities" + }, + { + subject_id: uuidv4(), + name: "Geography", + unit: "Humanities" + }, + { + subject_id: uuidv4(), + name: "Sociology", + unit: "Humanities" + }]) + setComponentKey((prevKey) => prevKey + 1); + setTimeout(() => { + setRefreshing(false); + }, 1000); + }; + + if (errorMsg) { + return ( + +
+
+
+ +
+
+ +
+
+ + ); + } + + return ( + +
+
+
+

{user?.preparation_unit}

+
+ {subjects ? ( + subjects + .filter(subject => subject.unit === user?.preparation_unit) + .map((subject) => ( +
+ +
+ )) + ) : ( +
+
+

Loading...

+
+ )} +
+
+ +
+
+
+ {/* */} +
+ ); +} diff --git a/app/(tabs)/unit/page.tsx b/app/(tabs)/units/page.tsx similarity index 81% rename from app/(tabs)/unit/page.tsx rename to app/(tabs)/units/page.tsx index 47b227e..82dc14c 100644 --- a/app/(tabs)/unit/page.tsx +++ b/app/(tabs)/units/page.tsx @@ -4,11 +4,22 @@ import React from "react"; import { useRouter } from "next/navigation"; import Header from "@/components/Header"; import BackgroundWrapper from "@/components/BackgroundWrapper"; +import { v4 as uuidv4 } from "uuid"; const units = [ { - id: 3, - name: "C Unit (Business Studies)", + id: uuidv4(), + name: "Science", + rating: 8, + }, + { + id: uuidv4(), + name: "Business Studies", + rating: 8, + }, + { + id: uuidv4(), + name: "Humanities", rating: 9, }, ]; @@ -17,7 +28,7 @@ const Unit = () => { const router = useRouter(); const handleUnitPress = (unit: { - id?: number; + id: string; name: string; rating?: number; }) => { @@ -27,10 +38,10 @@ const Unit = () => { return (
-
+
-
+
{units ? ( units.map((unit) => (