Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
867798a
Add useMemo/useCallback to context providers, fix broken react depend…
krishitag Aug 14, 2026
5c07e22
Split FilterContext into state and actions contexts
krishitag Aug 16, 2026
d35b0e0
Merge pull request #529 from krishitag/fix/issue-471-usememo-usecallback
YOGESH-08 Aug 16, 2026
4fe8959
Improve catalogue and PDF viewer UX and light mode uiux
Rithish-2914 Aug 12, 2026
c8842da
Improve PDF viewer reading mode
Rithish-2914 Aug 13, 2026
d1fd6e9
Refine PDF viewer and sorting UI
Rithish-2914 Aug 15, 2026
a00113d
Add school information to papers
Rithish-2914 Aug 13, 2026
86cb3e4
Fix upcoming slot type definition
Rithish-2914 Aug 14, 2026
a00b9a8
ADDED MORE SCHOOLS
Rithish-2914 Aug 16, 2026
791c783
errors in filtercontext
Rithish-2914 Aug 16, 2026
f62c20d
Update sort button styling
Rithish-2914 Aug 17, 2026
256a338
Merge pull request #527 from Rithish-2914/fix/UI-PDFVIEWER
YOGESH-08 Aug 19, 2026
cf0b778
Merge pull request #528 from Rithish-2914/feat/school-field
YOGESH-08 Aug 20, 2026
ba42a2e
init posthog (w/o) self driving
abhitrueprogrammer Aug 26, 2026
63074e2
update env.example with posthog env vars
abhitrueprogrammer Aug 26, 2026
04c5a50
fix: satisfy PostHog client lint rule
abhitrueprogrammer Aug 26, 2026
8db21f6
chore: remove PostHog wizard artifacts
abhitrueprogrammer Aug 26, 2026
7b68cb9
fix: avoid duplicate completion events
abhitrueprogrammer Aug 26, 2026
8e5c875
fix: use managed PostHog proxy
abhitrueprogrammer Aug 26, 2026
20fa82e
Merge pull request #531 from CodeChefVIT/abhi/posthog
YOGESH-08 Sep 25, 2026
53ed699
chore: Remove completed Gravitas event banners and footer links
Rithish-2914 Sep 25, 2026
9d2a33f
Merge pull request #533 from Rithish-2914/chore/remove-gravitas-banner
YOGESH-08 Sep 25, 2026
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
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,7 @@ GOOGLE_APPLICATION_CREDENTIALS_JSON="" # The content of the JSON file you d

# Upstash_Redis
UPSTASH_REDIS_REST_URL="" # REST URL of your Upstash Redis database
UPSTASH_REDIS_REST_TOKEN="" # REST API token for Upstash Redis
UPSTASH_REDIS_REST_TOKEN="" # REST API token for Upstash Redis

NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN=""
NEXT_PUBLIC_POSTHOG_HOST="https://e.papers.codechefvit.com"
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,12 @@
"next-themes": "^0.3.0",
"pdf-lib": "^1.17.1",
"pdfjs-dist": "4.8.69",
"posthog-js": "^1.419.4",
"posthog-node": "^5.51.2",
"prettier": "^3.5.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"raw-loader": "^4.0.2",
"react": "link:@embedpdf/plugin-viewport/react",
"react": "^18.3.1",
"react-beautiful-dnd": "^13.1.1",
"react-dom": "^18.3.1",
"react-dropzone": "^14.3.8",
Expand Down
181 changes: 143 additions & 38 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

21 changes: 19 additions & 2 deletions src/app/api/request/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { success, failure } from "@/lib/utils/response";
import { createPaperRequest } from "@/lib/services/paper"
import type { CreatePaperInputType } from "@/lib/services/paper"
import { createPaperRequest } from "@/lib/services/paper";
import type { CreatePaperInputType } from "@/lib/services/paper";
import { getPostHogClient } from "@/lib/posthog-server";
import { randomUUID } from "crypto";

export async function POST(req: Request) {
try {
Expand All @@ -11,6 +13,21 @@ export async function POST(req: Request) {
}

const newRequest = await createPaperRequest({subject, exam, slot, year});

const posthog = getPostHogClient();
if (posthog) {
posthog.capture({
distinctId: randomUUID(),
event: "paper_request_created",
properties: {
exam,
slot,
year,
},
});
await posthog.flush();
}

return success({ message: "Paper request submitted successfully!", request: newRequest }, "Created", 201);
} catch (error) {
console.error("Error creating paper request:", error);
Expand Down
16 changes: 16 additions & 0 deletions src/app/api/upload/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { success, failure } from "@/lib/utils/response";
import { uploadPaper } from "@/lib/services/upload";
import { getPostHogClient } from "@/lib/posthog-server";
import { randomUUID } from "crypto";

export const runtime = "nodejs";

Expand All @@ -21,6 +23,20 @@ export async function POST(req: Request) {
return failure(result.message, result.status);
}

const posthog = getPostHogClient();
if (posthog) {
posthog.capture({
distinctId: randomUUID(),
event: "paper_upload_completed",
properties: {
file_count: files.length,
is_pdf: isPdf,
campus: campus ?? "unknown",
},
});
await posthog.flush();
}

return success(
{ file_url: result.file_url, thumbnail_url: result.thumbnail_url },
"Created",
Expand Down
33 changes: 18 additions & 15 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Navbar from "@/components/Navbar";
import Footer from "@/components/Footer";
import ChildrenWrapper from "@/components/ChildrenWrapper";
import { CoursesProvider } from "@/context/courseContext";
import { PostHogProvider } from "@/components/PostHogProvider";

export const metadata: Metadata = {
metadataBase: new URL("https://papers.codechefvit.com/"),
Expand Down Expand Up @@ -113,21 +114,23 @@ export default function RootLayout({
</Script>
</head>
<body>
<ThemeProvider
attribute="class"
defaultTheme="dark"
enableSystem
disableTransitionOnChange
>
<Toaster position="top-right" reverseOrder={false} />
<div className="bg-[#F3F5FF] dark:bg-[#070114]">
<CoursesProvider>
<Navbar />
<ChildrenWrapper>{children}</ChildrenWrapper>
<Footer />
</CoursesProvider>
</div>
</ThemeProvider>
<PostHogProvider>
<ThemeProvider
attribute="class"
defaultTheme="dark"
enableSystem
disableTransitionOnChange
>
<Toaster position="top-right" reverseOrder={false} />
<div className="bg-[#F3F5FF] dark:bg-[#070114]">
<CoursesProvider>
<Navbar />
<ChildrenWrapper>{children}</ChildrenWrapper>
<Footer />
</CoursesProvider>
</div>
</ThemeProvider>
</PostHogProvider>
</body>
</html>
);
Expand Down
26 changes: 15 additions & 11 deletions src/components/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ interface CardProps {
const Card = ({ paper, onSelect, isSelected, isShow=true }: CardProps) => {
const [previewOpen, setPreviewOpen] = React.useState(false);

const paperContextValue = React.useMemo(
() => ({
paperId: paper._id,
subject: paper.subject,
exam: paper.exam,
slot: paper.slot,
year: paper.year,
}),
[paper._id, paper.subject, paper.exam, paper.slot, paper.year],
);

React.useEffect(() => {
if (!previewOpen) return;

Expand Down Expand Up @@ -60,8 +71,8 @@ const Card = ({ paper, onSelect, isSelected, isShow=true }: CardProps) => {
window.open(paperLink, "_blank");
}}
className={cn(
"cursor-pointer overflow-hidden rounded-sm border-2 border-[#734DFF] bg-[#FFFFFF] font-play transition-all duration-150 hover:bg-[#EFEAFF] dark:border-[#36266D] dark:bg-[#171720] hover:dark:bg-[#262635]",
isSelected && "ring-2 ring-[#7480FF] bg-[#EFEAFF]"
"cursor-pointer overflow-hidden rounded-sm border-2 border-[#734DFF] bg-[#FFFFFF] font-play shadow-sm transition-all duration-150 hover:-translate-y-0.5 hover:bg-[#EFEAFF] hover:shadow-md dark:border-[#36266D] dark:bg-[#171720] dark:shadow-none hover:dark:bg-[#262635]",
isSelected && "-translate-y-0.5 bg-[#EFEAFF] shadow-md ring-2 ring-[#7480FF]"
)}
>
<Image
Expand Down Expand Up @@ -89,6 +100,7 @@ const Card = ({ paper, onSelect, isSelected, isShow=true }: CardProps) => {
<Capsule>{paper.slot}</Capsule>
<Capsule>{paper.year}</Capsule>
<Capsule>{paper.semester}</Capsule>
{paper.school && <Capsule>{paper.school}</Capsule>}
</div>
</div>
</div>
Expand Down Expand Up @@ -147,15 +159,7 @@ const Card = ({ paper, onSelect, isSelected, isShow=true }: CardProps) => {
<div
className="relative mx-auto h-full max-w-[760px]"
>
<PaperProvider
value={{
paperId: paper._id,
subject: paper.subject,
exam: paper.exam,
slot: paper.slot,
year: paper.year,
}}
>
<PaperProvider value={paperContextValue}>
<button
className="fixed right-4 top-4 z-[60] rounded-full border border-black/10 bg-white/95 p-2 text-black shadow-md transition hover:bg-white"
onClick={() => setPreviewOpen(false)}
Expand Down
28 changes: 16 additions & 12 deletions src/components/CatalogueContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ import SearchBarChild from "./Searchbar/searchbar-child";
import Link from "next/link";
import { useCourses } from "@/context/courseContext";
import { FilterProvider, useFilters } from "@/context/filterContext";
import posthog from "posthog-js";
import EmptyState from "./ui/EmptyState";
import SidebarButton from "./SidebarButton";
import SelectionToolbar from "./SelectionToolbar";
import SortComponent from "./ui/sorting";

const CatalogueContentInner = ({ subject }: { subject: string | null }) => {
Expand All @@ -39,6 +40,7 @@ const CatalogueContentInner = ({ subject }: { subject: string | null }) => {
selectedCampuses,
selectedAnswerKeyIncluded,
papers,
filteredPapers,
appliedFilters,
filtersPulled,
currentPage,
Expand All @@ -60,6 +62,7 @@ const CatalogueContentInner = ({ subject }: { subject: string | null }) => {
handleSelectAll,
handleDeselectAll,
handleDownloadSelected,
isDownloading,
} = useFilters();

useEffect(() => {
Expand Down Expand Up @@ -141,6 +144,10 @@ const CatalogueContentInner = ({ subject }: { subject: string | null }) => {
: saved.filter((s) => s !== subject);

localStorage.setItem("userSubjects", JSON.stringify(updated));

posthog.capture(current ? "subject_pinned" : "subject_unpinned", {
subject: subject?.split(" [")[0]?.trim() ?? subject,
});
};

// Fetch papers ONLY when subject changes (not when filters change!)
Expand Down Expand Up @@ -316,17 +323,14 @@ const CatalogueContentInner = ({ subject }: { subject: string | null }) => {
currentSort={sortOption}
/>

<SidebarButton onClick={handleSelectAll} className="order-2">
Select All
</SidebarButton>

<SidebarButton onClick={handleDeselectAll} className="order-2">
Deselect All
</SidebarButton>

<SidebarButton onClick={handleDownloadSelected} className="order-2">
Download Selected
</SidebarButton>
<SelectionToolbar
selectedCount={selectedPapers.length}
totalCount={(appliedFilters ? filteredPapers : papers).length}
onSelectAll={handleSelectAll}
onDeselectAll={handleDeselectAll}
onDownload={() => void handleDownloadSelected()}
isDownloading={isDownloading}
/>
</div>

{relatedSubjects.length > 0 && (
Expand Down
5 changes: 3 additions & 2 deletions src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { Mail } from "lucide-react";
import toast from "react-hot-toast";
import type { ApiResponse } from '@/interface'
import posthog from "posthog-js";

export default function Footer() {
const [email, setEmail] = useState("");
Expand Down Expand Up @@ -42,6 +43,8 @@ export default function Footer() {
},
);

posthog.capture("newsletter_subscribed");

setEmail("");
};

Expand Down Expand Up @@ -93,8 +96,6 @@ export default function Footer() {
<div className="flex w-full flex-col gap-2 text-black dark:text-white lg:w-[15%]">
<h3 className="font-jost text-xl font-semibold">Events</h3>
<Link href="https://devsoc26.codechefvit.com" target="_blank">DevSOC</Link>
<Link href="https://gravitas.codechefvit.com" target="_blank">CookOff</Link>
<Link href="https://gravitas.codechefvit.com" target="_blank">Clueminati</Link>
</div>
{/* Projects */}
<div className="flex w-full flex-col gap-2 text-black dark:text-white lg:w-[20%]">
Expand Down
29 changes: 29 additions & 0 deletions src/components/PostHogProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use client";

import posthog from "posthog-js";
import { PostHogProvider as PHProvider } from "posthog-js/react";
import { useEffect } from "react";

export function PostHogProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
const token = process.env.NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN;
const host = process.env.NEXT_PUBLIC_POSTHOG_HOST;
if (!token || !host) {
if (process.env.NODE_ENV !== "production") {
console.error(
"NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN and NEXT_PUBLIC_POSTHOG_HOST variables required by PostHog are missing or un-configured, this causes events to be silently missed. This error stops appearing once both variables are configured",
);
}
return;
}
posthog.init(token, {
api_host: host,
ui_host: "https://us.posthog.com",
defaults: "2026-01-30",
capture_exceptions: true,
debug: process.env.NODE_ENV === "development",
});
}, []);

return <PHProvider client={posthog}>{children}</PHProvider>;
}
8 changes: 7 additions & 1 deletion src/components/ReportTagModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import { MultiSelect } from "@/components/multi-select";
import LabeledInput from "@/components/ui/LabeledInput";
import LabeledSelect from "@/components/ui/LabeledSelect";
import axios from "axios";
import toast from "react-hot-toast";
import toast from "react-hot-toast";
import posthog from "posthog-js";
import { type ApiResponse } from '@/interface'

type ReportResponse = ApiResponse<{ error?: string; message?: string }>;
Expand Down Expand Up @@ -236,6 +237,11 @@ if (reportedFields.length === 0 && comment.trim().length === 0) {
}
)
.then(() => {
posthog.capture("paper_reported", {
fields_reported: reportedFields.map((f) => f.field),
fields_reported_count: reportedFields.length,
has_comment: comment.trim().length > 0,
});
modalSetOpen(false);
setComment("");
setEmail("");
Expand Down
Loading
Loading