Skip to content

Commit

Permalink
Merge pull request #6 from avantifellows/feature/home-page
Browse files Browse the repository at this point in the history
Content Library
  • Loading branch information
Bahugunajii authored Nov 8, 2023
2 parents f091d7b + a141cab commit 5385fe6
Show file tree
Hide file tree
Showing 9 changed files with 400 additions and 3 deletions.
4 changes: 3 additions & 1 deletion app/AuthContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const router = useRouter();
const [loggedIn, setLoggedIn] = useState(false);
const [userId, setUserId] = useState<string | null>(null);
const [userName, setUserName] = useState<string | null>(null);

useEffect(() => {
async function checkToken() {
Expand All @@ -28,6 +29,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
if (result.isValid) {
setLoggedIn(true);
setUserId(result.data.id);
setUserName(result.data.data.name)
} else {
setLoggedIn(false);
setUserId(null);
Expand All @@ -44,7 +46,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
}, []);

return (
<AuthContext.Provider value={{ loggedIn, userId }}>
<AuthContext.Provider value={{ loggedIn, userId, userName }}>
{children}
</AuthContext.Provider>
);
Expand Down
124 changes: 124 additions & 0 deletions app/library/contentList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import axios from 'axios';
import { Subject, Grade, Chapter, Resource, Topic } from '../types'

const url = process.env.NEXT_PUBLIC_AF_DB_SERVICE_URL;
const bearerToken = process.env.NEXT_PUBLIC_AF_DB_SERVICE_BEARER_TOKEN;

export const getSubjects = async (subjectName: string): Promise<Subject[]> => {
try {
const response = await axios.get(`${url}/subject`, {
params: { name: subjectName },
headers: {
'Authorization': `Bearer ${bearerToken}`
}
});
return response.data;
} catch (error) {
console.error("Error in getSubjects:", error);
throw error;
}
};

export const getGrades = async (number: number): Promise<Grade[]> => {
try {
const response = await axios.get(`${url}/grade`, {
params: { number: number },
headers: {
'Authorization': `Bearer ${bearerToken}`
}
});
return response.data;
} catch (error) {
console.error("Error in getSubjects:", error);
throw error;
}
};

export const getChapters = async (subjectId?: number, gradeId?: number, limit?: number, offset?: number, id?: number): Promise<Chapter[]> => {
try {
const response = await axios.get(`${url}/chapter`, {
params: { id: id, subject_id: subjectId, grade_id: gradeId, limit, offset },
headers: {
'Authorization': `Bearer ${bearerToken}`
}
});
return response.data;
} catch (error) {
console.error("Error in getChapters:", error);
throw error;
}
};

export const getTopics = async (chapterIds: number[], limit: number, offset: number): Promise<Topic[]> => {
const topicPromises = chapterIds.map(async (chapterId) => {
try {
const response = await axios.get(`${url}/topic`, {
params: { chapter_id: chapterId, limit, offset },
headers: {
'Authorization': `Bearer ${bearerToken}`
}
});
return response.data || [];
} catch (error) {
console.error("Error in getTopics for chapterId", chapterId, ":", error);
return [];
}
});

const topicResponses = await Promise.all(topicPromises);
return topicResponses.flat();
};

export const getSource = async (sourceId: number) => {
try {
const response = await axios.get(`${url}/source`, {
params: { id: sourceId },
headers: {
'Authorization': `Bearer ${bearerToken}`
}
});

if (response.data) {
return response.data;
}
} catch (error) {
console.error("Error in getSource for sourceId", sourceId, ":", error);
}
return null;
};

export const getResourcesWithSource = async (topicIds: number[]): Promise<Resource[]> => {
const resourcePromises = topicIds.map(async (topicId) => {
try {
const response = await axios.get(`${url}/resource`, {
params: { topic_id: topicId },
headers: {
'Authorization': `Bearer ${bearerToken}`
}
});

if (response.data) {
const chapterResources: Resource[] = response.data;

const sourcePromises = chapterResources.map(async (resource) => {
if (resource.source_id) {
const sourceData = await getSource(resource.source_id);
if (sourceData) {
resource.link = sourceData[0].link;
}
}
return resource;
});

const resourcesWithSource = await Promise.all(sourcePromises);
return resourcesWithSource;
}
} catch (error) {
console.error("Error in getResources for topicId", topicId, ":", error);
}
return [];
});

const resourceResponses = await Promise.all(resourcePromises);
return resourceResponses.flat();
};
219 changes: 219 additions & 0 deletions app/library/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
"use client"

import React, { useState } from 'react';
import BottomNavigationBar from '@/components/BottomNavigationBar';
import Loading from '../loading';
import TopBar from '@/components/TopBar';
import PrimaryButton from '@/components/Button';
import { getSubjects, getChapters, getResourcesWithSource, getTopics, getGrades } from './contentList';
import { Chapter, Resource, Topic } from '../types';
import { useEffect } from 'react';
import Link from 'next/link';
import ExpandIcon from "../../assets/expand.png";
import CollapseIcon from "../../assets/collapse.png";
import PlayIcon from "../../assets/play.png"
import Image from 'next/image';

const Page = () => {
const [activeTab, setActiveTab] = useState('Physics');
const [chapters, setChapters] = useState<Chapter[]>([]);
const [topics, setTopics] = useState<Topic[]>([]);
const [resources, setResources] = useState<Resource[]>([]);
const [expandedChapters, setExpandedChapters] = useState<Record<number, boolean>>({});
const [page, setPage] = useState(1);
const [selectedGrade, setSelectedGrade] = useState(9);
const [selectedChapter, setSelectedChapter] = useState<number | null>(null);
const [chapterList, setChapterList] = useState<Chapter[]>([]);
const limit = 4;
const gradeOptions = [9, 10, 11, 12];

const handleTabClick = async (tabName: string) => {
setActiveTab(tabName);
if (activeTab != tabName) {
setPage(1);
setSelectedChapter(null)
}

try {
const actualTabName = tabName.toLowerCase();
const subjectData = await getSubjects(actualTabName);
const gradeData = await getGrades(selectedGrade);
if (subjectData.length > 0) {
const subjectId = subjectData[0].id;
const gradeId = gradeData[0].id;
const offset = (page - 1) * limit;

const finalOffset = offset >= 0 ? offset : 0;
await fetchChapters(subjectId, gradeId);
const chapterData = selectedChapter
? await getChapters(subjectId, gradeId, limit, finalOffset, selectedChapter)
: await getChapters(subjectId, gradeId, limit, finalOffset);

if (chapterData.length > 0) {
setChapters(chapterData);
const chapterIds = chapterData.map((chapter) => chapter.id);
const topicData = await getTopics(chapterIds, limit, 0);
setTopics(topicData);
const topicIds = topicData.map((topic) => topic.id);
const resourceData = await getResourcesWithSource(topicIds);
setResources(resourceData);
}
else {
setPage(page - 1);
}
} else {
console.log("Bad request")
}
} catch (error) {
console.error('Error fetching data:', error);
}
};

useEffect(() => {
handleTabClick(activeTab);
}, [selectedGrade, page, selectedChapter]);

const toggleChapterExpansion = (chapterId: number) => {
setExpandedChapters((prevExpanded) => ({
...prevExpanded,
[chapterId]: !prevExpanded[chapterId],
}));
};

const handleNextPage = () => {
const nextPage = page + 1;
setPage(nextPage);
handleTabClick(activeTab);
};

const handlePreviousPage = () => {
const previousPage = page - 1;
if (previousPage >= 1) {
setPage(previousPage);
handleTabClick(activeTab);
}
};

const handleGradeChange = (grade: number) => {
setSelectedGrade(grade);
setSelectedChapter(null)
};

const fetchChapters = async (subjectId: number, gradeId: number) => {
const chapterData = await getChapters(subjectId, gradeId);
setChapterList(chapterData);
};

return (
<>
<TopBar />
<div className="bg-heading text-primary h-20 flex flex-col">
<h1 className="font-semibold ml-4 text-xl pt-6">NEET Course <br /></h1>
<span className="text-xs ml-4 font-normal">New Delhi</span>
</div>
<div className="flex flex-row mt-4 mb-4 justify-between">
<PrimaryButton
onClick={() => handleTabClick('Physics')}
className={activeTab === 'Physics' ? 'bg-heading text-primary' : 'bg-white text-slate-600'}
>
Physics
</PrimaryButton>
<PrimaryButton
onClick={() => handleTabClick('Chemistry')}
className={activeTab === 'Chemistry' ? 'bg-heading text-primary' : 'bg-white text-slate-600'}
>
Chemistry
</PrimaryButton>
<PrimaryButton
onClick={() => handleTabClick('Maths')}
className={activeTab === 'Maths' ? 'bg-heading text-primary' : 'bg-white text-slate-600'}
>
Maths
</PrimaryButton>
<PrimaryButton
onClick={() => handleTabClick('Biology')}
className={activeTab === 'Biology' ? 'bg-heading text-primary' : 'bg-white text-slate-600'}
>
Biology
</PrimaryButton>
</div>
<div className="bg-heading h-20 flex justify-between items-center px-4">
<select
onChange={(e) => handleGradeChange(+e.target.value)}
value={selectedGrade}
className="w-32 h-8 rounded-lg text-center"
>
{gradeOptions.map((grade) => (
<option key={grade} value={grade} className="text-sm">
Grade {grade}
</option>
))}
</select>
<select
onChange={(e) => setSelectedChapter(+e.target.value)}
value={selectedChapter || ''}
className="w-32 h-8 rounded-lg text-center"
>
<option value="" className="text-sm">Chapter: All</option>
{chapterList.map((chapter) => (
<option key={chapter.id} value={chapter.id} className="text-sm">
{chapter.name}
</option>
))}
</select>
</div>
{resources.length > 0 ? (
<div className="mt-4 mb-40">
{chapters.map((chapter) => (
<div key={chapter.id} className="mx-5">
<div
className="text-md font-semibold mt-2 bg-primary text-white cursor-pointer px-4 py-4 mb-4 flex flex-row justify-between items-center"
onClick={() => toggleChapterExpansion(chapter.id)}
>
<div className="w-52">{chapter.name}</div>
<div className="w-8 flex justify-center">
{expandedChapters[chapter.id] ? (
<Image src={ExpandIcon} alt="Expand" />
) : (
<Image src={CollapseIcon} alt="Collapse" />
)}
</div>
</div>
{expandedChapters[chapter.id] && (
<ul>
{topics
.filter((topic) => topic.chapter_id === chapter.id)
.map((topic) => (
<div key={topic.id} className="bg-card rounded-lg shadow-lg shadow-slate-400 p-4 mx-2 mt-2 my-8 text-black font-semibold">
<h3>{topic.name}</h3>
<ul className="text-primary m-2 font-normal">
{resources
.filter((resource) => resource.topic_id === topic.id)
.map((resource) => (
<li key={resource.id} className="py-2">
<Link href={resource.link} target="_blank" rel="noopener noreferrer" className="flex flex-row">
<Image src={PlayIcon} alt="Play" className="w-10 h-10 mr-2" /> {resource.name}
</Link>
</li>
))}
</ul>
</div>
))}
</ul>
)}
</div>
))}
<div className="flex justify-between rounded-lg mt-8 px-4">
<PrimaryButton onClick={handlePreviousPage} className="bg-heading text-primary">Previous</PrimaryButton>
<PrimaryButton onClick={handleNextPage} className="bg-heading text-primary">Next</PrimaryButton>
</div>
<BottomNavigationBar />
</div>
) : (
<Loading />
)}
</>
);
};

export default Page;
Loading

0 comments on commit 5385fe6

Please sign in to comment.