├── jsconfig.json ├── public ├── favicon.ico └── anokha_logo.png ├── postcss.config.mjs ├── next.config.mjs ├── app ├── _fonts │ ├── Gilroy-Black.ttf │ ├── Gilroy-Bold.ttf │ ├── Gilroy-Heavy.ttf │ ├── Gilroy-Light.ttf │ ├── Gilroy-Thin.ttf │ ├── Gilroy-Medium.ttf │ ├── Gilroy-Regular.ttf │ ├── Gilroy-SemiBold.ttf │ ├── Gilroy-BoldItalic.ttf │ ├── Gilroy-ExtraBold.ttf │ ├── Gilroy-ThinItalic.ttf │ ├── Gilroy-UltraLight.ttf │ ├── Gilroy-BlackItalic.ttf │ ├── Gilroy-HeavyItalic.ttf │ ├── Gilroy-LightItalic.ttf │ ├── Gilroy-MediumItalic.ttf │ ├── Gilroy-RegularItalic.ttf │ ├── Gilroy-ExtraBoldItalic.ttf │ ├── Gilroy-SemiBoldItalic.ttf │ └── Gilroy-UltraLightItalic.ttf ├── user │ ├── proposals │ │ ├── add │ │ │ └── page.jsx │ │ ├── [id] │ │ │ ├── edit │ │ │ │ └── page.jsx │ │ │ └── page.jsx │ │ └── page.jsx │ ├── page.jsx │ └── layout.jsx ├── reviewer │ ├── proposals │ │ ├── [id] │ │ │ └── page.jsx │ │ └── page.jsx │ ├── page.jsx │ └── layout.jsx ├── api │ ├── user │ │ ├── getAll │ │ │ └── route.js │ │ ├── [id] │ │ │ ├── pending │ │ │ │ └── route.js │ │ │ ├── reviewed │ │ │ │ └── route.js │ │ │ ├── proposal │ │ │ │ └── route.js │ │ │ ├── proposals │ │ │ │ └── route.js │ │ │ └── route.js │ │ ├── search │ │ │ └── route.js │ │ └── role │ │ │ └── route.js │ ├── proposal │ │ ├── [id] │ │ │ ├── reply │ │ │ │ └── route.js │ │ │ ├── forward │ │ │ │ └── route.js │ │ │ ├── status │ │ │ │ └── route.js │ │ │ ├── history │ │ │ │ └── route.js │ │ │ └── route.js │ │ ├── add │ │ │ └── route.js │ │ └── dept │ │ │ └── route.js │ ├── reviewer │ │ ├── getAll │ │ │ └── route.js │ │ ├── [id] │ │ │ ├── route.js │ │ │ ├── history │ │ │ │ └── route.js │ │ │ └── proposals │ │ │ │ └── route.js │ │ └── route.js │ ├── deleteUser │ │ └── route.js │ ├── createUser │ │ └── route.js │ ├── userService.js │ ├── reviewerService.js │ ├── proposalHistoryService.js │ └── proposalService.js ├── _components │ ├── config.js │ ├── ReviewerPages │ │ ├── ReviewerLayout.jsx │ │ └── ApprovedProposalCard.jsx │ └── ProposalPages │ │ ├── Dashboard.jsx │ │ └── ViewProposal.jsx ├── firebase │ └── firebase.js ├── layout.js ├── reset │ └── page.jsx ├── globals.css ├── signup │ └── page.jsx └── login │ └── page.jsx ├── lib └── utils.js ├── components └── ui │ ├── skeleton.jsx │ ├── label.jsx │ ├── separator.jsx │ ├── textarea.jsx │ ├── input.jsx │ ├── password-input.jsx │ ├── checkbox.jsx │ ├── popover.jsx │ ├── alert.jsx │ ├── tooltip.jsx │ ├── tabs.jsx │ ├── card.jsx │ ├── button.jsx │ ├── combo-box-level.jsx │ ├── combo-box-reviewer.jsx │ ├── combo-box.jsx │ ├── form.jsx │ ├── dialog.jsx │ ├── sheet.jsx │ ├── command.jsx │ └── select.jsx ├── components.json ├── hooks └── use-mobile.js ├── .gitignore ├── .github └── workflows │ └── prettier.yml ├── biome.json ├── utils └── apiRequest.js ├── package.json ├── schemas ├── proposal.schema.js └── user.schema.js └── README.md /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: ["@tailwindcss/postcss"], 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /public/anokha_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/public/anokha_logo.png -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /app/_fonts/Gilroy-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-Black.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-Bold.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-Heavy.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-Heavy.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-Light.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-Thin.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-Medium.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-Regular.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-SemiBold.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-BoldItalic.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-ExtraBold.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-ThinItalic.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-UltraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-UltraLight.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-BlackItalic.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-HeavyItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-HeavyItalic.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-LightItalic.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-MediumItalic.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-RegularItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-RegularItalic.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /app/_fonts/Gilroy-UltraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudharsanSaravanan/event-proposal-app/HEAD/app/_fonts/Gilroy-UltraLightItalic.ttf -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | import { clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /components/ui/skeleton.jsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | 3 | function Skeleton({ className, ...props }) { 4 | return ( 5 |
10 | ); 11 | } 12 | 13 | export { Skeleton }; 14 | -------------------------------------------------------------------------------- /app/user/proposals/add/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import AddProposalContent from "@/app/_components/ProposalPages/AddProposal"; 4 | 5 | export default function AddProposalPage() { 6 | return ( 7 |126 | {body} 127 |
128 | ); 129 | } 130 | 131 | export { 132 | useFormField, 133 | Form, 134 | FormItem, 135 | FormLabel, 136 | FormControl, 137 | FormDescription, 138 | FormMessage, 139 | FormField, 140 | }; 141 | -------------------------------------------------------------------------------- /app/api/reviewer/[id]/proposals/route.js: -------------------------------------------------------------------------------- 1 | import { NextRequest, NextResponse } from "next/server"; 2 | import { 3 | collection, 4 | query, 5 | where, 6 | getDocs, 7 | or, 8 | collectionGroup, 9 | doc, 10 | getDoc, 11 | } from "firebase/firestore"; 12 | import { db } from "@/app/firebase/firebase"; 13 | 14 | export async function GET(_, { params }) { 15 | try { 16 | const { id: reviewerId } = await params; 17 | 18 | if (!reviewerId) { 19 | return NextResponse.json( 20 | { success: false, message: "Reviewer ID is required" }, 21 | { status: 400 }, 22 | ); 23 | } 24 | 25 | // Fetch reviewer data. 26 | const reviewerRef = doc(db, "Auth", reviewerId); 27 | const reviewerSnap = await getDoc(reviewerRef); 28 | const reviewerData = reviewerSnap.data(); 29 | 30 | if (reviewerData.level === 2) { 31 | const proposalsRef = collection(db, "Proposals"); 32 | const q = query( 33 | proposalsRef, 34 | where("department", "in", reviewerData.department), 35 | ); 36 | const snapshot = await getDocs(q); 37 | const proposals = snapshot.docs.map((doc) => ({ 38 | id: doc.id, 39 | ...doc.data(), 40 | })); 41 | 42 | return NextResponse.json({ success: true, uniqueProposals: proposals }); 43 | } 44 | 45 | // Query all proposals where currentReviewer.reviewerId == reviewerId 46 | const proposalsRef = collection(db, "Proposals"); 47 | 48 | const q = query( 49 | proposalsRef, 50 | where("currentReviewer.reviewerId", "==", reviewerId), 51 | ); 52 | const snapshot = await getDocs(q); 53 | 54 | const proposals = snapshot.docs.map((doc) => ({ 55 | id: doc.id, 56 | ...doc.data(), 57 | })); 58 | 59 | // Check subcollections for history. 60 | const q2 = query( 61 | collectionGroup(db, "History"), 62 | where("proposalThread.currentReviewer.reviewerId", "==", reviewerId), 63 | ); 64 | const snapshot2 = await getDocs(q2); 65 | const history = snapshot2.docs.map((doc) => ({ 66 | id: doc.id, 67 | ...doc.data().proposalThread, 68 | })); 69 | 70 | // Collect unique id from history. 71 | const uniqueIds = new Set(); 72 | history.forEach((item) => { 73 | uniqueIds.add(item.id); 74 | }); 75 | 76 | // Fetch the latest version of the proposal with doc id. 77 | // use getDoc to fetch the latest version of the proposal. 78 | const latestProposals = await Promise.all( 79 | Array.from(uniqueIds).map(async (id) => { 80 | const docRef = doc(db, "Proposals", id); 81 | const docSnap = await getDoc(docRef); 82 | const data = docSnap.data(); 83 | data.id = docSnap.id; 84 | 85 | if (data) { 86 | const reviewerData = data.reviewerHistory.find( 87 | (item) => item.reviewerId === reviewerId, 88 | ); 89 | if (reviewerData) { 90 | data.status = reviewerData.decision; 91 | } 92 | } 93 | 94 | return data; 95 | }), 96 | ); 97 | 98 | // Merge proposals and history. 99 | const allProposals = [...proposals, ...latestProposals]; 100 | 101 | // Remove duplicates. 102 | const uniqueProposals = allProposals.filter( 103 | (proposal, index, self) => 104 | index === self.findIndex((t) => t.id === proposal.id), 105 | ); 106 | 107 | return NextResponse.json({ 108 | success: true, 109 | uniqueProposals: uniqueProposals, 110 | }); 111 | } catch (error) { 112 | console.error("Error fetching proposals for reviewer:", error); 113 | return NextResponse.json( 114 | { success: false, error: "Failed to fetch proposals" }, 115 | { status: 500 }, 116 | ); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /components/ui/dialog.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as DialogPrimitive from "@radix-ui/react-dialog"; 5 | import { XIcon } from "lucide-react"; 6 | 7 | import { cn } from "@/lib/utils"; 8 | 9 | function Dialog({ ...props }) { 10 | return58 | Enter your Amrita email address and we'll send you instructions to 59 | reset your password. 60 |
61 | 62 | {error && ( 63 |
91 | {error}
} 96 | {hookError && ( 97 |{hookError.message}
98 | )} 99 | 100 |173 | Already have an account?{" "} 174 | 175 | Login 176 | 177 |
178 |
128 | 179 | Don't have an account?{" "} 180 | 181 | Sign Up 182 | 183 |
184 |{error}
} 67 | 68 | {/* Welcome Message */} 69 |74 | This platform enables you to submit and manage proposals for 75 | workshops and events. Create compelling proposals, track their 76 | review progress, and collaborate with reviewers. 77 |
78 |95 | Submit a detailed proposal for technical workshops, training 96 | sessions, or hands-on learning experiences. 97 |
98 |112 | Propose conferences, seminars, or networking events with clear 113 | objectives and engagement plans. 114 |
115 |136 | {loading ? "..." : pendingCount} 137 |
138 |139 | Awaiting review by the committee 140 |
141 |147 | {loading ? "..." : reviewedCount} 148 |
149 |Evaluation completed
150 |No proposals found.
94 |95 | Create a new proposal to get started. 96 |
97 |160 | {proposal.description || "No description provided."} 161 |
162 | 163 |100 | Submitted by:{" "} 101 | 102 | {proposal.proposerName || "N/A"} 103 | 104 |
105 |221 | There are currently no proposals assigned to you for review. 222 |
223 |
133 |
267 | It looks like you haven't created any proposals. Get started by 268 | clicking the button above! 269 |
270 |