Skip to content

Commit

Permalink
Merge pull request #71 from CodeChefVIT/staging
Browse files Browse the repository at this point in the history
Staging
  • Loading branch information
NishantGupt786 authored Nov 22, 2024
2 parents 090674f + 368b1c9 commit 30bf8db
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 35 deletions.
20 changes: 20 additions & 0 deletions src/app/api/course-list/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { NextResponse } from "next/server";
import { connectToDatabase } from "@/lib/mongoose";
import { Course } from "@/db/papers";

export const dynamic = "force-dynamic";

export async function GET() {
try {
await connectToDatabase();
const courses = await Course.find().lean();

return NextResponse.json(courses, { status: 200 });
} catch (error) {
console.error(error);
return NextResponse.json(
{ message: "Failed to fetch courses", error },
{ status: 500 }
);
}
}
73 changes: 43 additions & 30 deletions src/app/api/upload/route.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { NextRequest, NextResponse } from "next/server";
import { NextResponse } from "next/server";
import { PDFDocument } from "pdf-lib";
import {courses, slots, years} from "@/components/select_options"
import { slots, years } from "@/components/select_options";
import { connectToDatabase } from "@/lib/mongoose";
import cloudinary from "cloudinary";
import {

CloudinaryUploadResult,
} from "@/interface";
import {PaperAdmin} from "@/db/papers";
import { type ICourses, type CloudinaryUploadResult } from "@/interface";
import { PaperAdmin } from "@/db/papers";
import axios from "axios";
// TODO: REMOVE THUMBNAIL FROM admin-buffer DB
cloudinary.v2.config({
cloud_name: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,
Expand All @@ -29,14 +27,19 @@ export async function POST(req: Request) {
const year = formData.get("year") as string;
const exam = formData.get("exam") as string;
const isPdf = formData.get("isPdf") === "true"; // Convert string to boolean
if(!(courses.includes(subject) && slots.includes(slot) && years.includes(year)))
{
return NextResponse.json(
{ message: "Bad Request" },

{ status: 400 },
);

const { data } = await axios.get<ICourses[]>(`${process.env.SERVER_URL}/api/course-list`);
const courses = data.map((course: { name: string }) => course.name);
if (
!(
courses.includes(subject) &&
slots.includes(slot) &&
years.includes(year)
)
) {
return NextResponse.json({ message: "Bad Request" }, { status: 400 });
}

await connectToDatabase();
let finalUrl: string | undefined = "";
let public_id_cloudinary: string | undefined = "";
Expand All @@ -53,18 +56,25 @@ export async function POST(req: Request) {
if (!process.env.NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET) {
return;
}

const mergedPdfBytes = await CreatePDF(files);
[public_id_cloudinary, finalUrl] = await uploadPDFFile(mergedPdfBytes, uploadPreset);
[public_id_cloudinary, finalUrl] = await uploadPDFFile(
mergedPdfBytes,
uploadPreset,
);
} catch (error) {
return NextResponse.json(
{ error: "Failed to process PDF" },
{ status: 500 },
);
}
} else {
[public_id_cloudinary, finalUrl] = await uploadPDFFile(files[0]!, uploadPreset);
[public_id_cloudinary, finalUrl] = await uploadPDFFile(
files[0]!,
uploadPreset,
);
}

const thumbnailResponse = cloudinary.v2.image(finalUrl!, {
format: "jpg",
});
Expand All @@ -73,7 +83,6 @@ export async function POST(req: Request) {
.replace("upload", "upload/w_400,h_400,c_fill")
.replace(/<img src='|'\s*\/>/g, "");
const paper = new PaperAdmin({

public_id_cloudinary,
finalUrl,
thumbnailUrl,
Expand All @@ -91,35 +100,39 @@ export async function POST(req: Request) {
console.error(error);
return NextResponse.json(
{ message: "Failed to upload papers", error },

{ status: 500 },
);
}
}


async function uploadPDFFile(file: File | ArrayBuffer, uploadPreset: string) {
let bytes;
if(file instanceof File) //for pdf
{
if (file instanceof File) {
bytes = await file.arrayBuffer();
}
else // for images that are pdf
{
} else {
bytes = file;
}
return uploadFile(bytes, uploadPreset, "application/pdf")
return uploadFile(bytes, uploadPreset, "application/pdf");
}
async function uploadFile(bytes: ArrayBuffer, uploadPreset: string, fileType: string) {

async function uploadFile(
bytes: ArrayBuffer,
uploadPreset: string,
fileType: string,
) {
try {
const buffer = Buffer.from(bytes);
const dataUrl = `data:${fileType};base64,${buffer.toString("base64")}`;
const uploadResult = await cloudinary.v2.uploader.unsigned_upload(dataUrl, uploadPreset) as CloudinaryUploadResult;
return [uploadResult.public_id, uploadResult.secure_url ];
const uploadResult = (await cloudinary.v2.uploader.unsigned_upload(
dataUrl,
uploadPreset,
)) as CloudinaryUploadResult;
return [uploadResult.public_id, uploadResult.secure_url];
} catch (e) {
throw (e);
throw e;
}
}

async function CreatePDF(files: File[]) {
const pdfDoc = await PDFDocument.create();

Expand Down
28 changes: 24 additions & 4 deletions src/components/searchbarSubjectList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import { useState, useCallback, useRef, useEffect } from "react";
import { Search } from "lucide-react";
import debounce from "debounce";
import axios from "axios";
import { Input } from "@/components/ui/input";
import { courses } from "./select_options";
import { type ICourses } from "@/interface";

function SearchbarSubjectList({
setSubject,
Expand All @@ -16,10 +17,28 @@ function SearchbarSubjectList({
const [suggestions, setSuggestions] = useState<string[]>([]);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const [courses, setCourses] = useState<string[]>([]);
const suggestionsRef = useRef<HTMLUListElement | null>(null);

const fetchCourses = async () => {
try {
setLoading(true);
const response = await axios.get<ICourses[]>("/api/course-list");
const fetchedCourses = response.data.map((course: { name: string }) => course.name);
setCourses(fetchedCourses);
setLoading(false);
} catch (err) {
setError("Failed to fetch courses");
setLoading(false);
}
};

useEffect(() => {
void fetchCourses();
}, []);

const debouncedSearch = useCallback(
debounce(async (text: string) => {
debounce((text: string) => {
if (text.length > 0) {
setLoading(true);
const escapedSearchText = text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
Expand All @@ -32,6 +51,7 @@ function SearchbarSubjectList({
if (filteredSubjects.length === 0) {
setError("Subject not found");
setSuggestions([]);
setLoading(false);
return;
}
setSuggestions(filteredSubjects);
Expand All @@ -41,7 +61,7 @@ function SearchbarSubjectList({
setSuggestions([]);
}
}, 500),
[],
[courses]
);

const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -50,7 +70,7 @@ function SearchbarSubjectList({
if (text.length <= 0) {
setSuggestions([]);
}
void debouncedSearch(text);
debouncedSearch(text);
};

const handleSelectSuggestion = (suggestion: string) => {
Expand Down
8 changes: 7 additions & 1 deletion src/db/papers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import mongoose, { Schema, type Model } from "mongoose";
import { type IPaper } from "@/interface";
import { type IPaper, type ICourses } from "@/interface";

const paperSchema = new Schema<IPaper>({
public_id_cloudinary: { type: String, required: true },
Expand All @@ -12,10 +12,16 @@ const paperSchema = new Schema<IPaper>({
isSelected: { type: Boolean, default: false },
});

const courseSchema = new Schema<ICourses>({
name: { type: String, required: true },
});

paperSchema.index({ subject: 1 });

export const PaperAdmin: Model<IPaper> =
mongoose.models.Admin ?? mongoose.model<IPaper>("Admin", paperSchema);
export const Course: Model<ICourses> =
mongoose.models.Course ?? mongoose.model("Course", courseSchema);
const Paper: Model<IPaper> =
mongoose.models.Paper ?? mongoose.model<IPaper>("Paper", paperSchema);

Expand Down
4 changes: 4 additions & 0 deletions src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ export interface IPaper{
exam: "CAT-1" | "CAT-2" | "FAT";
isSelected: boolean;
}

export interface ICourses{
name: string;
}
export interface IAdminUpload{
formData: FormData;
files: File[];
Expand Down

0 comments on commit 30bf8db

Please sign in to comment.