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
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
yarn lint-staged
6 changes: 6 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
dist
build
.next
coverage
.env
9 changes: 9 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100,
"bracketSpacing": true,
"arrowParens": "avoid"
}
2 changes: 1 addition & 1 deletion CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,4 @@ version 2.1, available at
https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq.
https://www.contributor-covenant.org/faq.
7 changes: 6 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@ Please note that this project is released with a [Contributor Code of Conduct](C
- Review code on other people’s submissions and help improving / finding vulnerabilities

## Making a PR

- Provide all the appropriate details asked in PR template
- A pull request doesn’t have to represent finished work. It’s usually better to open a pull request early on, so others can watch or give feedback on your progress. Just mark it as a “WIP” (Work in Progress) in the subject line. You can always add more commits later.

## Opening an Issue

- Make use of an appropriate Issue Template
- We welcome Feature request, Bug Report, Documentation fix and others
- Do not open critical security issues here, report them directly at [our email](mailto:contact@codechefvit.com).

## Communicating effectively

**Give context.** Help others get quickly up to speed. If you’re running into an error, explain what you’re trying to do and how to reproduce it. If you’re suggesting a new idea, explain why you think it’d be useful to the project (not just to you!).

```
Expand Down Expand Up @@ -65,8 +68,10 @@ Please note that this project is released with a [Contributor Code of Conduct](C
```

## Misc

- You are welcome to Propose a new feature by creating an **Issue**.
- You may Discuss a high-level topic or idea (for example, community, vision or policies) by writing to us at our [Email](mailto:contact@codechefvit.com).

## Attribution
- [Open Source Guide](https://opensource.guide/how-to-contribute/)

- [Open Source Guide](https://opensource.guide/how-to-contribute/)
26 changes: 16 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
FFCS-inator
</h2>

## FFCS-inator - CodeChef VIT
Generate priority-based timetables in seconds with FFCS-inator. The smartest way to plan your VIT FFCS. Pick your favorite faculties, set your subject and faculty priorities, and let FFCS-inator create the perfect clash-free timetable for you. Save your timetables for later, and easily share them with friends. No hassle. No stress. Just your ideal schedule, tailored to you.
## FFCS-inator - CodeChef VIT

Generate priority-based timetables in seconds with FFCS-inator. The smartest way to plan your VIT FFCS. Pick your favorite faculties, set your subject and faculty priorities, and let FFCS-inator create the perfect clash-free timetable for you. Save your timetables for later, and easily share them with friends. No hassle. No stress. Just your ideal schedule, tailored to you.

## Tech Stack

Expand All @@ -23,35 +23,39 @@ Generate priority-based timetables in seconds with FFCS-inator. The smartest way

## Features

**Faculty Listings by Domain, School, Subject, and Slot**
**Faculty Listings by Domain, School, Subject, and Slot**

- Search and explore a complete list of faculties, organized by school, domain, subject, and slot.

**Automatic Timetable Generation**
**Automatic Timetable Generation**

- Generate all possible clash-free timetables automatically using a smart 2D priority algorithm that considers your faculty and subject preferences.

**Download Detailed Reports**
**Download Detailed Reports**

- Export a comprehensive PDF report showing:
- All faculties possible for each subject
- Detailed clash information between selected slots and faculties

**Save Timetables**
**Save Timetables**

- Save your generated timetables for future access and comparison.

**Share Timetables Securely**
**Share Timetables Securely**

- Share your timetable with others via a unique shareable link.
- Control privacy settings to make your timetable public or private as needed.

**Interactive Slot View & Clash Detection**

- Quickly visualize all available slots on a timetable grid.
- Once a slot is selected, conflicting slots are automatically blocked to prevent clashes and simplify your choices.



## Get Started

The repository has two branches, 'prod' and 'staging'.

prod (Production Branch) represents the live, user-facing version of the project where bug-free, well-tested code resides.
prod (Production Branch) represents the live, user-facing version of the project where bug-free, well-tested code resides.

staging (Staging Branch) serves as a pre-production environment, where features and fixes are tested before they go live.

Expand All @@ -62,6 +66,7 @@ Clone the 'prod' branch (production)
```bash
git clone -b prod https://github.com/CodeChefVIT/ffcs.git
```

OR

Clone the 'staging' branch (staging)
Expand Down Expand Up @@ -91,6 +96,7 @@ Before getting started, please ensure that the .env file is properly configured.
[![License](http://img.shields.io/:license-mit-blue.svg?style=flat-square)](http://badges.mit-license.org)

## Meet the Team

[![Meet the Team](https://img.shields.io/badge/Meet%20the%20Team-Click%20Here-ff69b4?style=for-the-badge)](https://www.codechefvit.com/ffcs-inator)

<p align="center">
Expand Down
2 changes: 1 addition & 1 deletion components.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
}
10 changes: 5 additions & 5 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import { FlatCompat } from '@eslint/eslintrc';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
Expand All @@ -10,7 +10,7 @@ const compat = new FlatCompat({
});

const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
...compat.extends('next/core-web-vitals', 'next/typescript'),
{
rules: {
// "@next/next/no-img-element": "off",
Expand All @@ -27,4 +27,4 @@ const eslintConfig = [
},
];

export default eslintConfig;
export default eslintConfig;
10 changes: 5 additions & 5 deletions middleware.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { NextRequest, NextResponse } from "next/server";
import { NextRequest, NextResponse } from 'next/server';

export async function middleware(request: NextRequest) {
const sessionToken =
request.cookies.get("next-auth.session-token")?.value ||
request.cookies.get("__Secure-next-auth.session-token")?.value;
request.cookies.get('next-auth.session-token')?.value ||
request.cookies.get('__Secure-next-auth.session-token')?.value;

if (!sessionToken) {
return NextResponse.redirect(new URL("/", request.url));
return NextResponse.redirect(new URL('/', request.url));
}
return NextResponse.next();
}

export const config = {
matcher: ["/user/favorites"],
matcher: ['/user/favorites'],
};
10 changes: 5 additions & 5 deletions next.config.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { NextConfig } from "next";
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "lh3.googleusercontent.com",
port: "",
pathname: "/**",
protocol: 'https',
hostname: 'lh3.googleusercontent.com',
port: '',
pathname: '/**',
},
],
},
Expand Down
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"format": "prettier --write .",
"prepare": "husky"
},
"lint-staged": {
"*.{js,jsx,ts,tsx,css,md,json}": "prettier --write"
},
"dependencies": {
"@auth/mongodb-adapter": "^3.9.1",
Expand Down Expand Up @@ -37,6 +42,9 @@
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.3.2",
"husky": "^9.1.7",
"lint-staged": "^16.2.3",
"prettier": "^3.6.2",
"tailwindcss": "^4",
"tw-animate-css": "^1.2.9",
"typescript": "^5"
Expand Down
2 changes: 1 addition & 1 deletion postcss.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const config = {
plugins: ["@tailwindcss/postcss"],
plugins: ['@tailwindcss/postcss'],
};

export default config;
102 changes: 50 additions & 52 deletions public/service-worker.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,58 @@
/// <reference lib="webworker" />

const CACHE_NAME = "FFCS-CodeChefVIT_v1.1.1"; // Increment this on each deploy
const CACHE_NAME = 'FFCS-CodeChefVIT_v1.1.1'; // Increment this on each deploy

const CORE_ASSETS = [
"/",
"/offline",
"/logo_ffcs/icon-16x16.webp",
"/logo_ffcs/icon-32x32.webp",
"/logo_ffcs/icon-48x48.webp",
"/logo_ffcs/icon-64x64.webp",
"/logo_ffcs/icon-72x72.webp",
"/logo_ffcs/icon-76x76.webp",
"/logo_ffcs/icon-96x96.webp",
"/logo_ffcs/icon-114x114.webp",
"/logo_ffcs/icon-120x120.webp",
"/logo_ffcs/icon-128x128.webp",
"/logo_ffcs/icon-144x144.webp",
"/logo_ffcs/icon-152x152.webp",
"/logo_ffcs/icon-180x180.webp",
"/logo_ffcs/icon-192x192.webp",
"/logo_ffcs/icon-196x196.webp",
"/logo_ffcs/icon-228x228.webp",
"/logo_ffcs/icon-256x256.webp",
"/logo_ffcs/icon-384x384.webp",
"/logo_ffcs/icon-512x512.webp",
"/logo_ffcs.png",
"/logo_ffcs.svg",
"/logo_ffcs.webp",
'/',
'/offline',
'/logo_ffcs/icon-16x16.webp',
'/logo_ffcs/icon-32x32.webp',
'/logo_ffcs/icon-48x48.webp',
'/logo_ffcs/icon-64x64.webp',
'/logo_ffcs/icon-72x72.webp',
'/logo_ffcs/icon-76x76.webp',
'/logo_ffcs/icon-96x96.webp',
'/logo_ffcs/icon-114x114.webp',
'/logo_ffcs/icon-120x120.webp',
'/logo_ffcs/icon-128x128.webp',
'/logo_ffcs/icon-144x144.webp',
'/logo_ffcs/icon-152x152.webp',
'/logo_ffcs/icon-180x180.webp',
'/logo_ffcs/icon-192x192.webp',
'/logo_ffcs/icon-196x196.webp',
'/logo_ffcs/icon-228x228.webp',
'/logo_ffcs/icon-256x256.webp',
'/logo_ffcs/icon-384x384.webp',
'/logo_ffcs/icon-512x512.webp',
'/logo_ffcs.png',
'/logo_ffcs.svg',
'/logo_ffcs.webp',
];

const limitCacheSize = (name, maxItems) => {
caches.open(name).then((cache) =>
cache.keys().then((keys) => {
caches.open(name).then(cache =>
cache.keys().then(keys => {
if (keys.length > maxItems) {
cache.delete(keys[0]).then(() => limitCacheSize(name, maxItems));
}
})
);
};

self.addEventListener("install", (event) => {
console.log("Service Worker: Installing...");
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(CORE_ASSETS))
);
self.addEventListener('install', event => {
console.log('Service Worker: Installing...');
event.waitUntil(caches.open(CACHE_NAME).then(cache => cache.addAll(CORE_ASSETS)));
self.skipWaiting();
});

self.addEventListener("activate", (event) => {
console.log("Service Worker: Activating...");
self.addEventListener('activate', event => {
console.log('Service Worker: Activating...');
event.waitUntil(
caches.keys().then((keys) =>
caches.keys().then(keys =>
Promise.all(
keys.map((key) => {
keys.map(key => {
if (key !== CACHE_NAME) {
console.log("Service Worker: Removing old cache", key);
console.log('Service Worker: Removing old cache', key);
return caches.delete(key);
}
})
Expand All @@ -64,47 +62,47 @@ self.addEventListener("activate", (event) => {
self.clients.claim();
});

self.addEventListener("message", (event) => {
if (event.data && event.data.type === "SKIP_WAITING") {
console.log("Service Worker: SKIP_WAITING message received");
self.addEventListener('message', event => {
if (event.data && event.data.type === 'SKIP_WAITING') {
console.log('Service Worker: SKIP_WAITING message received');
self.skipWaiting();
}
});

self.addEventListener("fetch", (event) => {
self.addEventListener('fetch', event => {
const { request } = event;

if (
request.method !== "GET" ||
request.url.includes("/api") ||
request.url.includes("chrome-extension") ||
request.url.startsWith("chrome")
request.method !== 'GET' ||
request.url.includes('/api') ||
request.url.includes('chrome-extension') ||
request.url.startsWith('chrome')
) {
return;
}

if (request.headers.get("accept")?.includes("text/html")) {
if (request.headers.get('accept')?.includes('text/html')) {
event.respondWith(
fetch(request)
.then((res) => {
.then(res => {
const resClone = res.clone();
caches.open(CACHE_NAME).then((cache) => {
caches.open(CACHE_NAME).then(cache => {
cache.put(request, resClone);
});
return res;
})
.catch(() => caches.match(request).then((res) => res || caches.match("/offline")))
.catch(() => caches.match(request).then(res => res || caches.match('/offline')))
);
return;
}

event.respondWith(
caches.match(request).then((res) => {
caches.match(request).then(res => {
return (
res ||
fetch(request)
.then((fetchRes) => {
return caches.open(CACHE_NAME).then((cache) => {
.then(fetchRes => {
return caches.open(CACHE_NAME).then(cache => {
if (request.url.startsWith(self.location.origin)) {
cache.put(request, fetchRes.clone());
limitCacheSize(CACHE_NAME, 100);
Expand Down
Loading