├── .gitignore ├── README.md ├── app ├── (dashboard) │ ├── issues │ │ └── page.tsx │ ├── layout.tsx │ ├── organizations │ │ ├── [org] │ │ │ └── page.tsx │ │ └── page.tsx │ ├── roadmap │ │ └── page.tsx │ └── starred │ │ └── page.tsx ├── (root) │ ├── layout.tsx │ └── page.tsx ├── api │ ├── all-years │ │ └── route.ts │ ├── one-org-data │ │ └── route.ts │ └── orgs-data │ │ └── route.ts └── favicon.ico ├── components.json ├── components ├── Loader.tsx ├── auth │ └── LoginWithGithub.tsx ├── main │ ├── Footer.tsx │ ├── GsocGuide.tsx │ ├── Navbar.tsx │ ├── OrganizationCard.tsx │ └── Pagination.tsx └── ui │ ├── Gallery │ ├── Gallery.tsx │ └── index.ts │ ├── aurora-text.tsx │ ├── blur-fade.tsx │ ├── line-shadow-text.tsx │ ├── select.tsx │ └── shine-border.tsx ├── db ├── Connect.ts └── db.ts ├── eslint.config.mjs ├── fonts └── font.ts ├── hooks └── use-mobile.tsx ├── lib └── utils.ts ├── models ├── starred.model.ts └── user.model.ts ├── next.config.ts ├── package.json ├── pnpm-lock.yaml ├── postcss.config.mjs ├── public ├── combined_projects.json ├── gsoc-orgs-with-years-git - Copy.csv ├── gsoc-orgs-with-years-git01.csv ├── gsoc-orgs-with-years.csv ├── gsoc-orgs.csv ├── gsoc-projects-with-years.csv ├── gsoc_orgs_json │ ├── 2016.json │ ├── 2017.json │ ├── 2018.json │ ├── 2019.json │ ├── 2020.json │ ├── 2021.json │ ├── 2022.json │ ├── 2023.json │ └── 2024.json ├── image.png └── updated_gsoc.csv ├── styles └── globals.css ├── tailwind.config.ts ├── tsconfig.json └── utils ├── cn.ts └── technologies.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HackToGSoC Organizations Dashboard 2 | 3 | Welcome to the HackToGSoC Organizations Dashboard! This platform showcases organizations participating in HackToGSoC. 4 | 5 | ## Features 6 | - **List of Organizations**: Browse through various organizations involved in the event. 7 | - **Contribution Guidelines**: Get the organization's guidelines for contributing to their open-source projects. 8 | - **Repositories**: Access a list of repositories available for contributions. 9 | 10 | ## How to Contribute 11 | 1. Browse the list of organizations. 12 | 2. Check their repositories and guidelines. 13 | 3. Fork, contribute, and make a pull request to participate in HackToGSoC! 14 | 15 | For more details, visit the [HackToGSoC website](https://hacktogsoc.vercel.app/organizations). 16 | -------------------------------------------------------------------------------- /app/(dashboard)/issues/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | 4 | 5 | const page = () => { 6 | return ( 7 |
8 |

9 | Under Development.... 10 |

11 |
12 | ) 13 | } 14 | 15 | export default page -------------------------------------------------------------------------------- /app/(dashboard)/layout.tsx: -------------------------------------------------------------------------------- 1 | import "@/styles/globals.css" 2 | import { jura } from "@/fonts/font"; 3 | import Navbar from "@/components/main/Navbar"; 4 | import { Metadata } from "next"; 5 | import Footer from "@/components/main/Footer"; 6 | import { Analytics } from "@vercel/analytics/react"; 7 | 8 | export const metadata: Metadata = { 9 | title: "HackToGSoC | Dashboard", 10 | description: "An app to simplify gsoc contributions", 11 | }; 12 | 13 | export default function RootLayout({ 14 | children, 15 | }: Readonly<{ 16 | children: React.ReactNode; 17 | }>) { 18 | return ( 19 | 20 | 21 | 22 | 23 |
24 | 25 | {children} 26 |
27 |
28 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /app/(dashboard)/organizations/[org]/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import axios from "axios"; 4 | import Link from "next/link"; 5 | import React, { use, useEffect, useState } from "react"; 6 | import { FaLink, FaCode } from "react-icons/fa"; 7 | import PacmanLoader from "react-spinners/PacmanLoader"; 8 | 9 | const Org = ({ params }: any) => { 10 | const [description, setDescription] = useState(""); 11 | const [projectsByYear, setProjectsByYear] = useState<{ [year: string]: any[] }>({}); 12 | const [loading, setLoading] = useState(true); 13 | const slug: any = use(params); 14 | 15 | useEffect(() => { 16 | const fetchData = async () => { 17 | try { 18 | setLoading(true); 19 | const res = await axios.get(`/api/one-org-data?title=${slug.org}`); 20 | setDescription(res.data.Description); 21 | 22 | const projects = res.data.projects[0]; 23 | const projectsByYear = Object.entries(projects).reduce( 24 | (acc: any, [year, projects]) => { 25 | acc[year] = projects; 26 | return acc; 27 | }, 28 | {} 29 | ); 30 | 31 | setProjectsByYear(projectsByYear); 32 | } catch (error) { 33 | console.error("Error fetching data:", error); 34 | } finally { 35 | setLoading(false); 36 | } 37 | }; 38 | 39 | fetchData(); 40 | }, [slug.org]); 41 | 42 | const sortedYearEntries = Object.entries(projectsByYear) 43 | .sort(([yearA], [yearB]) => parseInt(yearB) - parseInt(yearA)); 44 | 45 | return ( 46 |
47 |
48 |

49 | {decodeURIComponent(slug.org)} 50 |

51 | {loading ? ( 52 |

Loading...

53 | ) : ( 54 |

{description}

55 | )} 56 |
57 |

Projects.

58 | {loading ? ( 59 |
60 | 61 |
62 | ) : ( 63 | sortedYearEntries.map(([year, projects], index) => ( 64 |
65 |

{year}

66 |
{/* Modified justify */} 67 | {projects.map((project: any, projectIndex) => ( 68 |
72 |
73 |

{project.title}

74 |

~ {project.student_name}

75 |

{project.short_description}

76 |
77 |
78 | {project.project_url && ( 79 | 85 | 86 | 87 | )} 88 | {project.code_url && ( 89 | 95 | 96 | 97 | )} 98 |
99 |
100 | ))} 101 |
102 |
103 | )) 104 | )} 105 |
106 | ); 107 | }; 108 | 109 | export default Org; -------------------------------------------------------------------------------- /app/(dashboard)/organizations/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React, { useCallback, useEffect, useState, useRef } from "react"; 4 | import axios from "axios"; 5 | import { PacmanLoader } from "react-spinners"; 6 | import OrganizationCard from "@/components/main/OrganizationCard"; 7 | import { RoughNotation } from "react-rough-notation"; 8 | import { Technologies } from "@/utils/technologies"; 9 | import Pagination from "@/components/main/Pagination"; 10 | import debounce from "lodash/debounce"; 11 | 12 | interface Organization { 13 | "Image URL": string; 14 | Name: string; 15 | Description: string; 16 | Technologies?: string; 17 | Topics: string; 18 | "GitHub URL": string; 19 | URL: string; 20 | years: string; 21 | } 22 | 23 | const STORAGE_KEY = "org-filters"; 24 | const EXPIRATION_TIME = 10 * 60 * 1000; // 10 minutes in milliseconds 25 | 26 | const Organizations = () => { 27 | const [data, setData] = useState([]); 28 | const [loading, setLoading] = useState(true); 29 | const [technologyFilter, setTechnologyFilter] = useState("all"); 30 | const [yearFilter, setYearFilter] = useState("all"); 31 | const [currentPage, setCurrentPage] = useState(1); 32 | const [itemsPerPage] = useState(12); 33 | const searchInputRef = useRef(null); 34 | const [searchTerm, setSearchTerm] = useState(""); 35 | const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(""); 36 | const [filtersLoaded, setFiltersLoaded] = useState(false); // Ensure filters are loaded before fetching 37 | 38 | // Function to save filters to localStorage 39 | const saveFiltersToStorage = () => { 40 | const filters = { 41 | technologyFilter, 42 | yearFilter, 43 | searchTerm, 44 | timestamp: new Date().getTime(), // Store current time 45 | }; 46 | localStorage.setItem(STORAGE_KEY, JSON.stringify(filters)); 47 | }; 48 | 49 | // Function to load filters from localStorage 50 | const loadFiltersFromStorage = () => { 51 | const storedData = localStorage.getItem(STORAGE_KEY); 52 | if (storedData) { 53 | const { technologyFilter, yearFilter, searchTerm, timestamp } = JSON.parse(storedData); 54 | 55 | if (new Date().getTime() - timestamp < EXPIRATION_TIME) { 56 | setTechnologyFilter(technologyFilter); 57 | setYearFilter(yearFilter); 58 | setSearchTerm(searchTerm); 59 | setDebouncedSearchTerm(searchTerm); 60 | } else { 61 | localStorage.removeItem(STORAGE_KEY); 62 | } 63 | } 64 | setFiltersLoaded(true); // Ensure filters are marked as loaded 65 | }; 66 | 67 | useEffect(() => { 68 | loadFiltersFromStorage(); 69 | }, []); 70 | 71 | // Fetch data only when filters are loaded 72 | useEffect(() => { 73 | if (filtersLoaded) { 74 | fetchData(); 75 | } 76 | }, [technologyFilter, yearFilter, filtersLoaded]); 77 | 78 | // Fetch Data 79 | const fetchData = async () => { 80 | setLoading(true); 81 | try { 82 | const res = await axios.get(`/api/orgs-data?technology=${technologyFilter}&year=${yearFilter}`); 83 | setData(res.data); 84 | } catch (err) { 85 | console.error("Error fetching data:", err); 86 | } finally { 87 | setLoading(false); 88 | } 89 | }; 90 | 91 | // Debounced Fetch 92 | const debouncedFetchData = useCallback( 93 | debounce(async () => { 94 | if (!filtersLoaded) return; 95 | setLoading(true); 96 | try { 97 | const res = await axios.get(`/api/orgs-data?technology=${technologyFilter}&year=${yearFilter}`); 98 | setData(res.data); 99 | } catch (err) { 100 | console.log(err); 101 | } finally { 102 | setLoading(false); 103 | } 104 | }, 300), 105 | [technologyFilter, yearFilter, filtersLoaded] 106 | ); 107 | 108 | // Handle search input 109 | const handleSearch = () => { 110 | if (searchInputRef.current) { 111 | setSearchTerm(searchInputRef.current.value); 112 | setDebouncedSearchTerm(searchInputRef.current.value); 113 | } 114 | setCurrentPage(1); 115 | saveFiltersToStorage(); 116 | }; 117 | 118 | useEffect(() => { 119 | debouncedFetchData(); 120 | }, [debouncedSearchTerm, debouncedFetchData]); 121 | 122 | // Update localStorage when filters change 123 | useEffect(() => { 124 | if (filtersLoaded) { 125 | saveFiltersToStorage(); 126 | } 127 | }, [technologyFilter, yearFilter, searchTerm]); 128 | 129 | // Handle Page Change 130 | const handlePageChange = (page: number) => { 131 | setCurrentPage(page); 132 | window.scrollTo({ top: 0, behavior: "smooth" }); 133 | }; 134 | 135 | // Filtered Data 136 | const filteredData = data.filter((item) => { 137 | if (!searchTerm) return true; 138 | const searchLower = searchTerm.toLowerCase(); 139 | return item.Name.toLowerCase().includes(searchLower) || item.Description.toLowerCase().includes(searchLower); 140 | }); 141 | 142 | const indexOfLastItem = currentPage * itemsPerPage; 143 | const indexOfFirstItem = indexOfLastItem - itemsPerPage; 144 | const currentOrgs = filteredData.slice(indexOfFirstItem, indexOfLastItem); 145 | const totalPages = Math.ceil(filteredData.length / itemsPerPage); 146 | const showNoOrgsFound = filteredData.length === 0; 147 | const showPagination = !showNoOrgsFound && totalPages > 1; 148 | 149 | const handleKeyDown = (e: React.KeyboardEvent) => { 150 | if (e.key === "Enter") { 151 | handleSearch(); 152 | } 153 | }; 154 | 155 | return loading ? ( 156 |
157 | 158 |
159 | ) : ( 160 |
161 |
162 |

Organizations

163 |

164 | Find the best{" "} 165 | 166 | organizations 167 | {" "} 168 | to work on. 169 |

170 |
171 |
172 |
173 | 181 | 184 |
185 |
186 | 194 | 202 |
203 |
204 |
{showNoOrgsFound ?

No Organizations Found

: currentOrgs.map((item, idx) => )}
205 | {showPagination && } 206 |
207 | ); 208 | }; 209 | 210 | export default Organizations; 211 | -------------------------------------------------------------------------------- /app/(dashboard)/roadmap/page.tsx: -------------------------------------------------------------------------------- 1 | import GSOCGuide from '@/components/main/GsocGuide' 2 | import React from 'react' 3 | 4 | const Roadmap = () => { 5 | return ( 6 | 7 | ) 8 | } 9 | 10 | export default Roadmap -------------------------------------------------------------------------------- /app/(dashboard)/starred/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import React, { useState , useEffect, useCallback} from 'react' 3 | import OrganizationCard from '@/components/main/OrganizationCard'; 4 | import { RoughNotation } from 'react-rough-notation'; 5 | 6 | interface Organization { 7 | "Image URL": string; 8 | Name: string; 9 | Description: string; 10 | Technologies?: string; 11 | Topics: string; 12 | "GitHub URL": string; 13 | URL: string; 14 | years: string; 15 | } 16 | 17 | 18 | const Starred = () => { 19 | const [bookmarkedOrgs, setBookmarkedOrgs] = useState([]); 20 | 21 | 22 | const handleBookmarks = useCallback(() => { 23 | const storedBookmarks = localStorage.getItem("bookmarks"); 24 | if (storedBookmarks) { 25 | try { 26 | setBookmarkedOrgs(JSON.parse(storedBookmarks)); 27 | } catch (error) { 28 | console.error("Error parsing bookmarks from localStorage:", error); 29 | setBookmarkedOrgs([]); 30 | } 31 | } 32 | }, []) 33 | useEffect(() => { 34 | handleBookmarks(); 35 | 36 | }, []) 37 | 38 | return ( 39 |
40 |
41 | 42 |

Starred

43 |

Contribute karne ka irada hai? ya sirf save hi karna hai

44 |
45 |
46 |
47 | {bookmarkedOrgs && 48 | bookmarkedOrgs.map((item, idx) => ( 49 | 50 | ))} 51 | {bookmarkedOrgs.length === 0 && ( 52 |

53 | No organizations starred yet. 54 |

55 | )} 56 |
57 |
58 |
59 | ) 60 | } 61 | 62 | export default Starred -------------------------------------------------------------------------------- /app/(root)/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Geist, Geist_Mono } from "next/font/google"; 3 | import "@/styles/globals.css"; 4 | import Navbar from "@/components/main/Navbar"; 5 | import Footer from "@/components/main/Footer"; 6 | import { Jura } from "next/font/google"; 7 | import { Analytics } from "@vercel/analytics/react" 8 | 9 | 10 | const jura = Jura({ 11 | weight: "400", 12 | style: "normal", 13 | subsets: ["latin"], 14 | }); 15 | 16 | const geistSans = Geist({ 17 | variable: "--font-geist-sans", 18 | subsets: ["latin"], 19 | }); 20 | 21 | const geistMono = Geist_Mono({ 22 | variable: "--font-geist-mono", 23 | subsets: ["latin"], 24 | }); 25 | 26 | export const metadata: Metadata = { 27 | title: "HackToGSoC", 28 | description: "An app to simplify gsoc contributions", 29 | }; 30 | 31 | export default async function RootLayout({ 32 | children, 33 | }: Readonly<{ 34 | children: React.ReactNode; 35 | }>) { 36 | return ( 37 | 38 | 39 | 42 | 43 | 44 | {children} 45 | 46 |