Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

102 changes: 102 additions & 0 deletions src/app/catalogue/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"use client";

import { useSearchParams, useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import axios, { AxiosError } from "axios";
import Cryptr from "cryptr";

interface Paper {
_id: string;
exam: string;
finalUrl: string;
slot: string;
subject: string;
year: string;
}

const cryptr = new Cryptr(process.env.NEXT_PUBLIC_CRYPTO_SECRET ?? "default_crypto_secret");

const Catalogue = () => {
const router = useRouter();
const searchParams = useSearchParams();
const subject = searchParams.get('subject');
const [papers, setPapers] = useState<Paper[]>([]);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState<boolean>(false);

useEffect(() => {
if (subject) {
const fetchPapers = async () => {
setLoading(true);

try {
const papersResponse = await axios.get("http://localhost:3000/api/papers", {
params: { subject },
});

const { res: encryptedPapersResponse } = papersResponse.data;
const decryptedPapersResponse = cryptr.decrypt(encryptedPapersResponse);
// console.log("Decrypted Papers Response:", decryptedPapersResponse);

const papersData: Paper[] = JSON.parse(decryptedPapersResponse).papers;
setPapers(papersData);
} catch (error) {

if (axios.isAxiosError(error)) {
const axiosError = error as AxiosError<{ message?: string }>;
const errorMessage = axiosError.response?.data?.message || "Error fetching papers";
setError(errorMessage);

} else {
setError("Error fetching papers");
}

} finally {
setLoading(false);
}
};

fetchPapers();
}
}, [subject]);

return (
<div className="min-h-screen bg-gray-50 p-8">

<button onClick={() => router.push('/')} className="mb-4 px-4 py-2 bg-blue-500 text-white rounded-md">
Back to Search
</button>

<h1 className="text-2xl font-bold mb-4">Papers for {subject}</h1>
{error && <p className="text-red-500">{error}</p>}

{loading ? (
<p>Loading papers...</p>
) : (
papers.length > 0 ? (
<div className="grid grid-cols-1 gap-4">

{papers.map((paper) => (
<div key={paper._id} className="border rounded-md p-4 shadow-md bg-white">

<h3 className="text-xl font-bold">Exam: {paper.exam}</h3>
<p>Slot: {paper.slot}</p>
<p>Subject: {paper.subject}</p>
<p>Year: {paper.year}</p>

<a href={paper.finalUrl} target="_blank" rel="noopener noreferrer" className="text-blue-500 hover:underline">
View Paper
</a>

</div>
))}
</div>
) : (
<p>No papers available for this subject.</p>
)
)}
</div>
);
};

export default Catalogue;
77 changes: 61 additions & 16 deletions src/app/components/searchbar.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,79 @@
"use client";

import { useState } from "react";
import axios from "axios";
import { Search } from "lucide-react";
import Cryptr from "cryptr";
import { useRouter } from "next/navigation";

const cryptr = new Cryptr(
process.env.NEXT_PUBLIC_CRYPTO_SECRET ?? "default_crypto_secret"
);

const SearchBar = () => {
const router = useRouter();
const [searchText, setSearchText] = useState("");
const [suggestions, setSuggestions] = useState<string[]>([]);
const [error, setError] = useState<string | null>(null);

const handleSearchChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const text = e.target.value;
setSearchText(text);

if (text.length > 1) {
try {
const searchResponse = await axios.get("http://localhost:3000/api/search", {
params: { text },
});

const { res: encryptedSearchResponse } = searchResponse.data;
const decryptedSearchResponse = cryptr.decrypt(encryptedSearchResponse);
// console.log("Decrypted Search Response:", decryptedSearchResponse);

const { subjects } = JSON.parse(decryptedSearchResponse);
const suggestionList = subjects.map((subjectObj: { subject: string }) => subjectObj.subject);
setSuggestions(suggestionList);
} catch (error) {
setError("Error fetching suggestions");
}
} else {
setSuggestions([]);
}
};

const handleSearch = (event: React.FormEvent) => {
event.preventDefault();
console.log("Searching for:", searchText);
const handleSelectSuggestion = async (suggestion: string) => {
setSearchText(suggestion);
setSuggestions([]);
router.push(`/catalogue?subject=${encodeURIComponent(suggestion)}`);
};

return (
<div className="flex min-h-screen items-center justify-center bg-gray-50">
<form onSubmit={handleSearch} className="w-full max-w-md">
<div className="flex min-h-screen items-center justify-center bg-gray-50 flex-col">
<form className="w-full max-w-md">
<div className="relative">
<input
type="text"
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
placeholder="Search..."
className="w-full rounded-md border border-gray-300 px-4 py-2 pr-10 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
<button
type="submit"
className="absolute inset-y-0 right-0 flex items-center pr-3"
>

<input type="text" value={searchText} onChange={handleSearchChange}
placeholder="Search..." className="w-full rounded-md border border-gray-300 px-4 py-2 pr-10 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500"/>

<button type="submit" className="absolute inset-y-0 right-0 flex items-center pr-3">
<Search className="h-5 w-5 text-gray-400" />
</button>

</div>
{suggestions.length > 0 && (
<ul className="absolute z-10 w-full bg-white border border-gray-300 rounded-md mt-2">
{suggestions.map((suggestion, index) => (

<li key={index} onClick={() => handleSelectSuggestion(suggestion)} className="cursor-pointer p-2 hover:bg-gray-100">
{suggestion}
</li>

))}
</ul>
)}
</form>

{error && <p className="mt-4 text-red">{error}</p>}
</div>
);
};
Expand Down