chore: refactor code for type safety

This commit is contained in:
shafin-r
2025-07-27 03:19:25 +06:00
parent 0ea199c0fe
commit 3ef526ec1a
10 changed files with 135 additions and 155 deletions

View File

@ -1,24 +1,29 @@
"use client";
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, ReactNode } from "react";
import { useRouter } from "next/navigation";
import Image from "next/image";
import Link from "next/link";
import Header from "@/components/Header";
import SlidingGallery from "@/components/SlidingGallery";
import BackgroundWrapper from "@/components/BackgroundWrapper";
import DestructibleAlert from "@/components/DestructibleAlert";
import { ChevronRight } from "lucide-react"; // Using Lucide React for icons
import styles from "@/css/Home.module.css";
import facebookStyles from "@/css/SlidingGallery.module.css";
import { API_URL } from "@/lib/auth";
import { Avatar, AvatarFallback } from "@radix-ui/react-avatar";
import { Avatar } from "@/components/ui/avatar";
import { getLinkedViews } from "@/lib/gallery-views";
import { getTopThree } from "@/lib/leaderboard";
interface LinkedView {
id: string;
content: ReactNode;
}
const page = () => {
const profileImg = "/images/static/avatar.jpg";
const router = useRouter();
const [boardData, setBoardData] = useState([]);
const [boardError, setBoardError] = useState(null);
const [linkedViews, setLinkedViews] = useState<LinkedView[]>();
const performanceData = [
{ label: "Mock Test", progress: 20 },
@ -31,38 +36,6 @@ const page = () => {
{ label: "Chemistry", progress: 57 },
];
const facebookViews = [
{
id: "1",
content: (
<Link
href="https://www.facebook.com/share/g/15jdqESvWV/?mibextid=wwXIfr"
className="w-full h-full block text-inherit box-border"
>
<div className="w-full h-full p-6 flex text-black bg-blue-50 rounded-4xl border-[0.5px] border-[#113768]/30">
<div className="">
<h3 className="text-2xl text-[#113768] font-black">
Meet, Share, and Learn!
</h3>
<p className="font-bold text-sm text-[#113768] ">
Join Facebook Community
</p>
</div>
<div className={facebookStyles.logoView}>
<Image
src="/images/static/facebook-logo.png"
alt="Facebook Logo"
width={150}
height={150}
/>
</div>
</div>
</Link>
),
},
];
// Fetch function for leaderboard data
useEffect(() => {
let isMounted = true;
async function fetchBoardData() {
@ -77,32 +50,21 @@ const page = () => {
if (isMounted) setBoardError(error.message || "An error occurred");
}
}
fetchBoardData();
const fetchedLinkedViews = getLinkedViews();
setLinkedViews(fetchedLinkedViews);
return () => {
isMounted = false;
};
}, []);
const getTopThree = (boardData) => {
if (!boardData || boardData.length === 0) return [];
return boardData
.slice()
.sort((a, b) => b.points - a.points)
.slice(0, 3)
.map((player, index) => ({
...player,
rank: index + 1,
height: index === 0 ? 250 : index === 1 ? 200 : 170,
}));
};
return (
<BackgroundWrapper>
<div className={styles.container}>
<Header displayTabTitle={null} displayUser image={profileImg} />
<Header displayUser />
<div className={styles.scrollContainer}>
<div className={styles.contentWrapper}>
<SlidingGallery views={facebookViews} height="23vh" />
<SlidingGallery views={linkedViews} height="23vh" />
<div className={styles.mainContent}>
{/* Categories Section */}
<div>

View File

@ -5,13 +5,7 @@ import Link from "next/link";
import { ReactNode } from "react";
import { usePathname } from "next/navigation";
import clsx from "clsx";
import {
House,
LayoutGrid,
Bookmark,
CircleUser,
Settings,
} from "lucide-react";
import { House, LayoutGrid, Bookmark, Settings } from "lucide-react";
const tabs = [
{ name: "Home", href: "/home", component: <House size={30} /> },

View File

@ -4,39 +4,17 @@ import BackgroundWrapper from "@/components/BackgroundWrapper";
import Header from "@/components/Header";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { API_URL, getToken } from "@/lib/auth";
import { BoardData, getLeaderboard, getUserData } from "@/lib/leaderboard";
import { UserData } from "@/types/auth";
import Image from "next/image";
import React, { useEffect, useState } from "react";
const page = () => {
const LeaderboardPage = () => {
const [boardError, setBoardError] = useState<string | null>(null);
const [boardData, setBoardData] = useState([]);
const [userData, setUserData] = useState(null);
const [boardData, setBoardData] = useState<BoardData[]>([]);
const [userData, setUserData] = useState<UserData>();
const [loading, setLoading] = useState(true);
async function fetchBoardData() {
try {
const boardResponse = await fetch(`${API_URL}/leaderboard`, {
method: "GET",
});
if (!boardResponse.ok) {
throw new Error("Failed to fetch leaderboard data");
}
const fetchedBoardData = await boardResponse.json();
if (Array.isArray(fetchedBoardData) && fetchedBoardData.length > 0) {
setBoardData(fetchedBoardData);
} else {
setBoardError("No leaderboard data available.");
setBoardData([]);
}
} catch (error) {
console.error(error);
setBoardError("Something went wrong. Please try again.");
setBoardData([]);
}
}
useEffect(() => {
async function fetchUser() {
try {
@ -55,7 +33,31 @@ const page = () => {
setUserData(fetchedUserData);
} catch (error) {
console.error(error);
setUserData(null);
setUserData(undefined);
}
}
async function fetchBoardData() {
try {
const boardResponse = await fetch(`${API_URL}/leaderboard`, {
method: "GET",
});
if (!boardResponse.ok) {
throw new Error("Failed to fetch leaderboard data");
}
const fetchedBoardData = await boardResponse.json();
if (Array.isArray(fetchedBoardData) && fetchedBoardData.length > 0) {
setBoardData(fetchedBoardData);
} else {
setBoardError("No leaderboard data available.");
setBoardData([]);
}
} catch (error) {
console.error(error);
setBoardError("Something went wrong. Please try again.");
setBoardData([]);
}
}
@ -63,7 +65,7 @@ const page = () => {
fetchBoardData();
}, []);
const getTopThree = (boardData) => {
const getTopThree = (boardData: BoardData[]) => {
if (!boardData || !Array.isArray(boardData)) return [];
const sortedData = boardData
.filter((player) => player?.points !== undefined) // Ensure `points` exists
@ -78,24 +80,10 @@ const page = () => {
return [topThree[1], topThree[0], topThree[2]].filter(Boolean); // Handle missing players
};
const getLeaderboard = (boardData) => {
return boardData.slice().sort((a, b) => b.points - a.points);
};
const getUserData = (boardData, name) => {
if (!boardData || !Array.isArray(boardData)) return [];
const sortedData = boardData
.filter((player) => player?.name && player?.points !== undefined)
.sort((a, b) => b.points - a.points);
const result = sortedData.find((player) => player.name === name);
return result ? [{ ...result, rank: sortedData.indexOf(result) + 1 }] : [];
};
return (
<BackgroundWrapper>
<section>
<Header displaySubject={"Leaderboard"} displayTabTitle={null} />
<Header displayTabTitle={"Leaderboard"} />
<section className="flex flex-col mx-10 pt-10 space-y-4">
<section className="flex justify-evenly items-end">
{getTopThree(boardData).map((student, idx) =>
@ -124,7 +112,7 @@ const page = () => {
<div className="w-full border-[0.5px] border-[#c5dbf8] bg-[#c5dbf8]"></div>
<section className="border-[1px] border-[#c0dafc] w-full rounded-3xl p-6 space-y-4 mb-20">
<section>
{getUserData(boardData, userData?.name).map((user, idx) => (
{getUserData(boardData, userData!.name).map((user, idx) => (
<div
key={idx}
className="flex bg-[#113768] rounded-[8] py-2 px-4 justify-between items-center"
@ -179,4 +167,4 @@ const page = () => {
);
};
export default page;
export default LeaderboardPage;

View File

@ -6,15 +6,7 @@ import { getToken, API_URL } from "@/lib/auth";
import { ChevronLeft, Edit2, Lock, Save } from "lucide-react";
import { useRouter } from "next/navigation";
import React, { useEffect, useState } from "react";
interface UserData {
name: string;
institution: string;
sscRoll: string;
hscRoll: string;
email: string;
phone: string;
}
import { UserData } from "@/types/auth";
const ProfilePage = () => {
const router = useRouter();

View File

@ -1,9 +1,7 @@
import type { Metadata } from "next";
import { Montserrat } from "next/font/google";
import "./globals.css";
import { AuthProvider } from "@/context/AuthContext";
import { TimerProvider } from "@/context/TimerContext";
import { ExamProvider } from "@/context/ExamContext";
import { Providers } from "./providers";
const montserrat = Montserrat({

View File

@ -3,30 +3,10 @@
import Link from "next/link";
import { useRouter } from "next/navigation";
import Image from "next/image";
import { useState, useEffect } from "react";
import BackgroundWrapper from "@/components/BackgroundWrapper";
export default function Home() {
const router = useRouter();
const [windowDimensions, setWindowDimensions] = useState({
width: 0,
height: 0,
});
useEffect(() => {
function handleResize() {
setWindowDimensions({
width: window.innerWidth,
height: window.innerHeight,
});
}
// Set initial dimensions
handleResize();
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return (
<BackgroundWrapper>

View File

@ -1,33 +1,27 @@
import React, { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
import Image from "next/image";
import { ChevronLeft, Layers } from "lucide-react";
import { useTimer } from "@/context/TimerContext";
import styles from "@/css/Header.module.css";
import { useExam } from "@/context/ExamContext";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { useModal } from "@/context/ModalContext";
import { API_URL, getToken } from "@/lib/auth";
import { UserData } from "@/types/auth";
const API_URL = "https://examjam-api.pptx704.com";
// You'll need to implement getToken for Next.js - could use cookies, localStorage, etc.
const getToken = async () => {
if (typeof window === "undefined") {
return null;
}
// Extract authToken from cookies
const match = document.cookie.match(/(?:^|;\s*)authToken=([^;]*)/);
return match ? decodeURIComponent(match[1]) : null;
};
interface HeaderProps {
displayUser?: boolean;
displaySubject?: string;
displayTabTitle: string;
examDuration?: string;
}
const Header = ({
image,
displayUser,
displaySubject,
displayTabTitle,
examDuration,
}) => {
}: HeaderProps) => {
const router = useRouter();
const { open } = useModal();
const { clearExam } = useExam();
@ -35,7 +29,7 @@ const Header = ({
examDuration ? parseInt(examDuration) * 60 : 0
);
const { timeRemaining, stopTimer } = useTimer();
const [userData, setUserData] = useState(null);
const [userData, setUserData] = useState<UserData>();
useEffect(() => {
if (!examDuration) return;

View File

@ -1,4 +1,5 @@
// lib/resultViews.tsx
// lib/gallery-views.tsx
import { Link } from "lucide-react";
import Image from "next/image";
interface ExamResults {
@ -97,3 +98,34 @@ export const getResultViews = (examResults: ExamResults | null) => [
),
},
];
export const getLinkedViews = () => [
{
id: "1",
content: (
<Link
href="https://www.facebook.com/share/g/15jdqESvWV/?mibextid=wwXIfr"
className="w-full h-full block text-inherit box-border"
>
<div className="w-full h-full p-6 flex text-black bg-blue-50 rounded-4xl border-[0.5px] border-[#113768]/30">
<div className="">
<h3 className="text-2xl text-[#113768] font-black">
Meet, Share, and Learn!
</h3>
<p className="font-bold text-sm text-[#113768] ">
Join Facebook Community
</p>
</div>
<div className="flex justify-center items-center shrink-0">
<Image
src="/images/static/facebook-logo.png"
alt="Facebook Logo"
width={150}
height={150}
/>
</div>
</div>
</Link>
),
},
];

32
lib/leaderboard.ts Normal file
View File

@ -0,0 +1,32 @@
export interface BoardData {
id: string;
name: string;
points: number;
}
export const getTopThree = (boardData: BoardData[]) => {
if (!boardData || boardData.length === 0) return [];
return boardData
.slice()
.sort((a, b) => b.points - a.points)
.slice(0, 3)
.map((player, index) => ({
...player,
rank: index + 1,
height: index === 0 ? 250 : index === 1 ? 200 : 170,
}));
};
export const getLeaderboard = (boardData: BoardData[]) => {
return boardData.slice().sort((a, b) => b.points - a.points);
};
export const getUserData = (boardData: BoardData[], name: string) => {
if (!boardData || !Array.isArray(boardData)) return [];
const sortedData = boardData
.filter((player) => player?.name && player?.points !== undefined)
.sort((a, b) => b.points - a.points);
const result = sortedData.find((player) => player.name === name);
return result ? [{ ...result, rank: sortedData.indexOf(result) + 1 }] : [];
};

8
types/auth.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
export interface UserData {
name: string;
institution: string;
sscRoll: string;
hscRoll: string;
email: string;
phone: string;
}