Merge remote UI updates

This commit is contained in:
Shanmuga Krishnan S M
2026-08-19 08:37:24 +05:30
20 changed files with 2081 additions and 523 deletions

105
README.md
View File

@@ -1,32 +1,87 @@
# React + TypeScript + Vite # 🏫 Rajalakshmi Institute of Technology — Freshers-Hub
This template provides a minimal setup to get React working in Vite with HMR and some Oxlint rules. Welcome to the **Freshers-Hub Portal**, a multi-service campus assistant and student utility ecosystem designed for Rajalakshmi Institute of Technology (RIT). The platform provides student services including live transit tracking, study guides, competitive programming leaderboards, developer collaboration networks, and an automated AI assistant.
Currently, two official plugins are available: ---
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) ## 🏗️ System Architecture & Service Stack
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
## React Compiler The system is built on a distributed microservice architecture utilizing four primary runtimes:
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). ```
┌───────────────────────────────┐
## Expanding the Oxlint configuration │ Nginx Web Server │
│ (Ports 80 / 443) │
If you are developing a production application, we recommend enabling type-aware lint rules by installing `oxlint-tsgolint` and editing `.oxlintrc.json`: └───────────────┬───────────────┘
│
```json ┌───────────────────────┼───────────────────────┐
{ ▼ ▼ ▼
"$schema": "./node_modules/oxlint/configuration_schema.json", ┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐
"plugins": ["react", "typescript", "oxc"], │ React Frontend │ │Spring Boot Backend│ │Go Chatbot Service │
"options": { │ (Vite App) │ │ (Port 8085) │ │ (Port 8081) │
"typeAware": true └───────────────────┘ └─────────┬─────────┘ └───────────────────┘
}, │
"rules": { ▼
"react/rules-of-hooks": "error", ┌───────────────────┐ ┌───────────────────┐
"react/only-export-components": ["warn", { "allowConstantExport": true }] │ Python Gateway │◄──┤ Telegram Bot & │
} │ (Port 8082) │ │ Discord Bot │
} └───────────────────┘ └───────────────────┘
``` ```
See the [Oxlint rules documentation](https://oxc.rs/docs/guide/usage/linter/rules) for the full list of rules and categories. ### Stack Components:
1. **Frontend**: React, TypeScript, Vite, Tailwind CSS, Leaflet Maps, and Framer Motion.
2. **Core API Backend**: Spring Boot, Java 21, Hibernate/JPA, and PostgreSQL.
3. **Go Chatbot Engine**: Go microservice performing spelling correction (Levenshtein Distance) and BM25 ranking queries.
4. **Python Bot Gateway**: FastAPI HTTP listener interfacing with Telegram and Discord bot APIs.
---
## ⚙️ Service Ports & Deployments
| Component | Dev Port | Production Port | Daemon Service | Description |
| :--- | :---: | :---: | :---: | :--- |
| **React App** | `5173` | `80` / `443` | `nginx` | Frontend web client |
| **Spring Boot API** | `8085` | `8085` | `springboot` | Core business logic & database API |
| **Go Chatbot API** | `8081` | `8081` | `chatbot` | Keyword lookup & campus Q&A engine |
| **Python Bot Server** | `8082` | `8082` | `telegram-bot` | Telegram/Discord webhook router |
| **PostgreSQL Database** | `5432` | `5432` | `postgresql` | Primary database cluster |
---
## 📑 Feature Documentation Directories
For full functional breakdowns, REST API schemas, and technical setup guides for each platform feature, refer to the guides inside [docs/features/](file:///c:/Users/deves/OneDrive/project_main/Freshers-Hub/docs/features):
- 📚 **[Academic Resources Sync](file:///c:/Users/deves/OneDrive/project_main/Freshers-Hub/docs/features/academic_resources.md)**: Syncs static uploads on the VPS directly to database catalog endpoints.
- 🛠️ **[Interactive Student Toolkit](file:///c:/Users/deves/OneDrive/project_main/Freshers-Hub/docs/features/student_toolkit.md)**: Contains SGPA/CGPA formulas and college internal marks calculation algorithms.
- 🚌 **[Live Bus GPS Tracking](file:///c:/Users/deves/OneDrive/project_main/Freshers-Hub/docs/features/live_bus_tracking.md)**: Renders live bus coordinates with custom Leaflet overlays and static JSON fail-safes.
- 🤖 **[AI Chatbot Microservice](file:///c:/Users/deves/OneDrive/project_main/Freshers-Hub/docs/features/ai_chatbot_engine.md)**: Custom Go BM25 NLP search engine and spelling correction pipeline details.
- 💬 **[Moderated Q&A Forum](file:///c:/Users/deves/OneDrive/project_main/Freshers-Hub/docs/features/community_qa_forum.md)**: Moderation workflow linking student web queries with Telegram Senior Helper channels.
- 🤝 **[Developer Collab Hub](file:///c:/Users/deves/OneDrive/project_main/Freshers-Hub/docs/features/developer_collab_hub.md)**: Project listings filtered by RIT subdomains and linked with Telegram/Discord bot accept/decline callback requests.
- 🏆 **[LeetCode Leaderboard](file:///c:/Users/deves/OneDrive/project_main/Freshers-Hub/docs/features/leetcode_leaderboard.md)**: LeetCode GraphQL polling details using spaced request queues to avoid rate limit bans.
- 🗺️ **[Interactive Campus Blueprint](file:///c:/Users/deves/OneDrive/project_main/Freshers-Hub/docs/features/campus_map.md)**: Percentage-based blueprint vector coordinates system.
- 👩‍🏫 **[Faculty Directory](file:///c:/Users/deves/OneDrive/project_main/Freshers-Hub/docs/features/faculty_directory.md)**: Unified campus-wide search and sorting directories for RIT professors.
- 🏆 **[Clubs & Centers Explorer](file:///c:/Users/deves/OneDrive/project_main/Freshers-Hub/docs/features/campus_events.md)**: Extracurricular search matching tool with a built-in interest matching quiz.
- 🚀 **[RAISE Incubator Landing](file:///c:/Users/deves/OneDrive/project_main/Freshers-Hub/docs/features/raise_incubator.md)**: Startup slot pitch reservation system.
- 🔑 **[Admin Panel & Service Watchdog](file:///c:/Users/deves/OneDrive/project_main/Freshers-Hub/docs/features/admin_dashboard.md)**: Operations watchdog script, notification lists, and system health status pipelines.
---
## 🛠️ Verification & Building
To verify that the system compiles successfully after local configurations:
1. **Verify Frontend Build**:
```bash
npm run build
```
2. **Verify Java Backend Compilation**:
```bash
cd backend
./mvnw clean package -DskipTests
```
3. **Verify Go Microservice Compilation**:
```bash
cd chatbot-service
go build -o chatbot
```

View File

@@ -0,0 +1,98 @@
package com.rit.portal.controller;
import com.rit.portal.entity.ClubLike;
import com.rit.portal.repository.ClubLikeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/clubs")
@CrossOrigin(origins = "*")
public class ClubController {
@Autowired
private ClubLikeRepository clubLikeRepository;
/**
* Get total count of likes for all clubs & centers
*/
@GetMapping("/likes")
public ResponseEntity<Map<String, Long>> getAllClubLikes() {
List<Object[]> results = clubLikeRepository.countAllGroupedByClubId();
Map<String, Long> likesMap = new HashMap<>();
for (Object[] result : results) {
String clubId = (String) result[0];
Long count = (Long) result[1];
likesMap.put(clubId, count);
}
return ResponseEntity.ok(likesMap);
}
/**
* Get list of club IDs liked by a specific user or device
*/
@GetMapping("/user-likes")
public ResponseEntity<List<String>> getUserLikedClubs(@RequestParam("userIdentifier") String userIdentifier) {
if (userIdentifier == null || userIdentifier.isBlank()) {
return ResponseEntity.ok(Collections.emptyList());
}
List<ClubLike> likes = clubLikeRepository.findByUserIdentifier(userIdentifier);
List<String> likedClubIds = likes.stream()
.map(ClubLike::getClubId)
.collect(Collectors.toList());
return ResponseEntity.ok(likedClubIds);
}
/**
* Toggle like state for a club/center by a user or device
*/
@PostMapping("/{clubId}/like")
@Transactional
public ResponseEntity<Map<String, Object>> toggleClubLike(
@PathVariable("clubId") String clubId,
@RequestBody(required = false) Map<String, String> payload,
@RequestParam(value = "userIdentifier", required = false) String paramUserIdentifier) {
String userIdentifier = null;
if (payload != null && payload.containsKey("userIdentifier")) {
userIdentifier = payload.get("userIdentifier");
}
if ((userIdentifier == null || userIdentifier.isBlank()) && paramUserIdentifier != null) {
userIdentifier = paramUserIdentifier;
}
if (userIdentifier == null || userIdentifier.isBlank()) {
userIdentifier = "anonymous_guest";
}
Optional<ClubLike> existingLike = clubLikeRepository.findByClubIdAndUserIdentifier(clubId, userIdentifier);
boolean isNowLiked;
if (existingLike.isPresent()) {
clubLikeRepository.delete(existingLike.get());
isNowLiked = false;
} else {
ClubLike newLike = ClubLike.builder()
.clubId(clubId)
.userIdentifier(userIdentifier)
.createdAt(LocalDateTime.now())
.build();
clubLikeRepository.save(newLike);
isNowLiked = true;
}
long newCount = clubLikeRepository.countByClubId(clubId);
Map<String, Object> response = new HashMap<>();
response.put("clubId", clubId);
response.put("liked", isNowLiked);
response.put("count", newCount);
return ResponseEntity.ok(response);
}
}

View File

@@ -0,0 +1,31 @@
package com.rit.portal.entity;
import jakarta.persistence.*;
import lombok.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "club_likes", uniqueConstraints = {
@UniqueConstraint(name = "unique_club_user_like", columnNames = {"club_id", "user_identifier"})
})
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ClubLike {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "club_id", nullable = false, length = 100)
private String clubId;
@Column(name = "user_identifier", nullable = false, length = 255)
private String userIdentifier;
@Column(name = "created_at")
@Builder.Default
private LocalDateTime createdAt = LocalDateTime.now();
}

View File

@@ -0,0 +1,23 @@
package com.rit.portal.repository;
import com.rit.portal.entity.ClubLike;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface ClubLikeRepository extends JpaRepository<ClubLike, Long> {
Optional<ClubLike> findByClubIdAndUserIdentifier(String clubId, String userIdentifier);
boolean existsByClubIdAndUserIdentifier(String clubId, String userIdentifier);
long countByClubId(String clubId);
List<ClubLike> findByUserIdentifier(String userIdentifier);
@Query("SELECT c.clubId, COUNT(c) FROM ClubLike c GROUP BY c.clubId")
List<Object[]> countAllGroupedByClubId();
}

View File

@@ -1,7 +1,7 @@
# ─── DATABASE CONNECTION CONFIGURATION ─── # ─── DATABASE CONNECTION CONFIGURATION ───
spring.datasource.url=jdbc:postgresql://localhost:5432/rit_freshers_hub?sslmode=disable spring.datasource.url=jdbc:postgresql://localhost:5432/rit_freshers_hub?sslmode=disable
spring.datasource.username=postgres spring.datasource.username=postgres
spring.datasource.password=${DB_PASSWORD:Anbukathir@2006} spring.datasource.password=${DB_PASSWORD:Amudiesh22@.}
# ─── JPA / HIBERNATE SETTINGS ─── # ─── JPA / HIBERNATE SETTINGS ───
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect

28
package-lock.json generated
View File

@@ -102,7 +102,6 @@
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.29.7", "@babel/code-frame": "^7.29.7",
"@babel/generator": "^7.29.7", "@babel/generator": "^7.29.7",
@@ -1637,6 +1636,17 @@
"tslib": "^2.4.0" "tslib": "^2.4.0"
} }
}, },
"node_modules/@emnapi/runtime": {
"version": "1.11.3",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz",
"integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/wasi-threads": { "node_modules/@emnapi/wasi-threads": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
@@ -4422,7 +4432,6 @@
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.3.tgz", "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.3.tgz",
"integrity": "sha512-ZjgcRwmQUSCythDPj6pE2NKFi9bpQegxIBxgt3hPytXI1ElFHNaa3A7aWW8vFaLCIRjPv467rbREYHbxcdgQbg==", "integrity": "sha512-ZjgcRwmQUSCythDPj6pE2NKFi9bpQegxIBxgt3hPytXI1ElFHNaa3A7aWW8vFaLCIRjPv467rbREYHbxcdgQbg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@tanstack/query-core": "5.101.3" "@tanstack/query-core": "5.101.3"
}, },
@@ -4508,7 +4517,6 @@
"integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"undici-types": "~7.18.0" "undici-types": "~7.18.0"
} }
@@ -4519,7 +4527,6 @@
"integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"csstype": "^3.2.2" "csstype": "^3.2.2"
} }
@@ -4530,7 +4537,6 @@
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"peer": true,
"peerDependencies": { "peerDependencies": {
"@types/react": "^19.2.0" "@types/react": "^19.2.0"
} }
@@ -4606,7 +4612,6 @@
"integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1", "fast-uri": "^3.0.1",
@@ -4865,7 +4870,6 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.10.42", "baseline-browser-mapping": "^2.10.42",
"caniuse-lite": "^1.0.30001803", "caniuse-lite": "^1.0.30001803",
@@ -6541,8 +6545,7 @@
"version": "1.9.4", "version": "1.9.4",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
"license": "BSD-2-Clause", "license": "BSD-2-Clause"
"peer": true
}, },
"node_modules/leven": { "node_modules/leven": {
"version": "3.1.0", "version": "3.1.0",
@@ -7170,7 +7173,6 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"nanoid": "^3.3.16", "nanoid": "^3.3.16",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
@@ -7214,7 +7216,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
"integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -7224,7 +7225,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
"integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"scheduler": "^0.27.0" "scheduler": "^0.27.0"
}, },
@@ -7237,7 +7237,6 @@
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.82.0.tgz", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.82.0.tgz",
"integrity": "sha512-Zw/uFZ2dO+02GHlBn7JFGn8kZJ7LdM33B/0BXOovzFay+CMhf94JMw5BVu+F1tVkUKjNvBuaE3fz5BJhga10Tg==", "integrity": "sha512-Zw/uFZ2dO+02GHlBn7JFGn8kZJ7LdM33B/0BXOovzFay+CMhf94JMw5BVu+F1tVkUKjNvBuaE3fz5BJhga10Tg==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=18.0.0" "node": ">=18.0.0"
}, },
@@ -7544,7 +7543,6 @@
"integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==", "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/estree": "1.0.9" "@types/estree": "1.0.9"
}, },
@@ -8090,7 +8088,6 @@
"integrity": "sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==", "integrity": "sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"peer": true,
"dependencies": { "dependencies": {
"@jridgewell/source-map": "^0.3.3", "@jridgewell/source-map": "^0.3.3",
"acorn": "^8.15.0", "acorn": "^8.15.0",
@@ -8416,7 +8413,6 @@
"integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"lightningcss": "^1.32.0", "lightningcss": "^1.32.0",
"picomatch": "^4.0.5", "picomatch": "^4.0.5",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 KiB

After

Width:  |  Height:  |  Size: 8.8 MiB

View File

@@ -135,3 +135,13 @@ CREATE TABLE IF NOT EXISTS confessions (
reactions_count INT DEFAULT 0, reactions_count INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
); );
-- 10. Club & Center Likes Table
CREATE TABLE IF NOT EXISTS club_likes (
id SERIAL PRIMARY KEY,
club_id VARCHAR(100) NOT NULL,
user_identifier VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_club_user_like UNIQUE (club_id, user_identifier)
);

View File

@@ -58,8 +58,7 @@ export function StaggerContainer({
return ( return (
<motion.div <motion.div
initial="hidden" initial="hidden"
whileInView="visible" animate="visible"
viewport={{ once: true, margin: '-50px' }}
variants={{ variants={{
hidden: {}, hidden: {},
visible: { visible: {

View File

@@ -53,8 +53,8 @@ export default function ProtectedRoute({
</div> </div>
<h1 <h1
className="text-2xl sm:text-3xl font-bold text-[#1E293B] mb-3" className="text-2xl sm:text-3xl font-extrabold text-[#1E293B] mb-3"
style={{ fontFamily: 'Playfair Display, serif' }} style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
> >
{title} {title}
</h1> </h1>

View File

@@ -19,6 +19,8 @@ import {
Users, Users,
Code2, Code2,
Trophy, Trophy,
Ticket,
Award,
GraduationCap, GraduationCap,
LayoutDashboard, LayoutDashboard,
Home as HomeIcon, Home as HomeIcon,
@@ -34,24 +36,29 @@ interface NavItem {
const PRIMARY_LINKS: NavItem[] = [ const PRIMARY_LINKS: NavItem[] = [
{ label: 'Home', path: '/', icon: HomeIcon }, { label: 'Home', path: '/', icon: HomeIcon },
{ label: 'Dashboard', path: '/dashboard', icon: LayoutDashboard }, { label: 'Time table', path: '/dashboard', icon: LayoutDashboard },
{ label: 'Notes', path: '/notes', icon: BookOpen }, { label: 'Notes', path: '/notes', icon: BookOpen },
{ label: 'Toolkit', path: '/toolkit', icon: Wrench },
{ label: 'Chatbot', path: '/ai-assistant', icon: Bot },
{ label: 'Clubs & Centers', path: '/events', icon: Users },
{ label: 'Dev Collab', path: '/collab', icon: Code2 },
{ label: 'LeetCode', path: '/leetcode', icon: Trophy },
];
const MORE_LINKS: NavItem[] = [
{ label: 'Campus Map', path: '/campus', icon: Map },
{ label: 'Bus Routes', path: '/bus-routes', icon: Bus }, { label: 'Bus Routes', path: '/bus-routes', icon: Bus },
{ label: 'Faculty Directory', path: '/faculty', icon: UserCheck }, { label: 'Faculty Directory', path: '/faculty', icon: UserCheck },
{ label: 'Community', path: '/community', icon: MessageCircle }, { label: 'Community', path: '/community', icon: MessageCircle },
{ label: 'RAISE Incubator', path: '/raise', icon: Rocket },
]; ];
const ALL_MOBILE_LINKS: NavItem[] = [...PRIMARY_LINKS, ...MORE_LINKS]; const MORE_LINKS: NavItem[] = [
{ label: 'Chatbot', path: '/ai-assistant', icon: Bot },
{ label: 'Toolkit', path: '/toolkit', icon: Wrench },
{ label: 'Campus Map', path: '/campus', icon: Map },
{ label: 'Clubs & Centers', path: '/events', icon: Users },
{ label: 'Dev Collab', path: '/collab', icon: Code2 },
{ label: 'LeetCode', path: '/leetcode', icon: Trophy },
{ label: 'Student Gate Pass', path: '/notes?portal=gatepass', icon: Ticket },
{ label: 'Student Proficiency', path: '/notes?portal=proficiency', icon: Award },
];
const ALL_MOBILE_LINKS: NavItem[] = [
...PRIMARY_LINKS,
...MORE_LINKS,
{ label: 'RAISE Incubator', path: '/raise', icon: Rocket },
];
export default function Navbar() { export default function Navbar() {
const [isMobileOpen, setIsMobileOpen] = useState(false); const [isMobileOpen, setIsMobileOpen] = useState(false);
@@ -82,9 +89,12 @@ export default function Navbar() {
}, []); }, []);
const checkIsActive = (linkPath: string) => { const checkIsActive = (linkPath: string) => {
const [pathName] = linkPath.split('?'); if (linkPath === '/') return location.pathname === '/';
if (pathName === '/') return location.pathname === '/'; if (linkPath === '/notes') return location.pathname === '/notes' && !location.search.includes('portal=');
return location.pathname.startsWith(pathName); if (linkPath.includes('?')) {
return location.pathname + location.search === linkPath;
}
return location.pathname.startsWith(linkPath);
}; };
const isMoreActive = MORE_LINKS.some((link) => checkIsActive(link.path)); const isMoreActive = MORE_LINKS.some((link) => checkIsActive(link.path));
@@ -97,7 +107,7 @@ export default function Navbar() {
initial={{ y: -60, opacity: 0 }} initial={{ y: -60, opacity: 0 }}
animate={{ y: 0, opacity: 1 }} animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.45, ease: 'easeOut' }} transition={{ duration: 0.45, ease: 'easeOut' }}
className="pointer-events-auto flex items-center justify-between gap-2 lg:gap-4 px-3 sm:px-4 py-2 rounded-2xl bg-slate-950/90 backdrop-blur-xl border border-white/10 shadow-[0_8px_30px_rgba(0,0,0,0.35)] transition-all" className="relative pointer-events-auto flex items-center justify-between gap-2 lg:gap-4 px-3 sm:px-4 py-2 rounded-2xl bg-slate-950/90 backdrop-blur-xl border border-white/10 shadow-[0_8px_30px_rgba(0,0,0,0.35)] transition-all"
> >
{/* Left: Logo & College Brand */} {/* Left: Logo & College Brand */}
<Link to="/" className="flex items-center gap-2.5 group shrink-0"> <Link to="/" className="flex items-center gap-2.5 group shrink-0">
@@ -194,12 +204,12 @@ export default function Navbar() {
onClick={() => setIsMoreOpen(false)} onClick={() => setIsMoreOpen(false)}
className={`flex items-center gap-2.5 px-3 py-2 rounded-xl text-xs font-medium transition-all ${ className={`flex items-center gap-2.5 px-3 py-2 rounded-xl text-xs font-medium transition-all ${
active active
? 'bg-gradient-to-r from-[#FF6B00] to-[#F97316] text-white font-bold' ? 'bg-white/15 text-orange-400 font-semibold'
: 'text-slate-300 hover:text-white hover:bg-white/10' : 'text-slate-300 hover:text-white hover:bg-white/10'
}`} }`}
style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }} style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
> >
{IconComponent && <IconComponent className="w-4 h-4 shrink-0 text-slate-300" />} {IconComponent && <IconComponent className={`w-4 h-4 shrink-0 ${active ? 'text-orange-400' : 'text-slate-300'}`} />}
<span>{item.label}</span> <span>{item.label}</span>
</Link> </Link>
); );
@@ -305,10 +315,10 @@ export default function Navbar() {
<Link <Link
to="/dashboard" to="/dashboard"
onClick={() => setIsProfileOpen(false)} onClick={() => setIsProfileOpen(false)}
className="w-full flex items-center gap-2.5 px-3 py-2 rounded-xl text-slate-300 hover:text-white hover:bg-orange-500/15 text-xs font-semibold transition-all cursor-pointer" className="w-full flex items-center gap-2.5 px-3 py-2 rounded-xl text-slate-300 hover:text-white hover:bg-orange-500/15 hover:border-orange-500/20 text-xs font-semibold transition-all cursor-pointer"
> >
<GraduationCap className="w-4 h-4 text-orange-400" /> <GraduationCap className="w-4 h-4 text-orange-400" />
My Student Dashboard My Time Table
</Link> </Link>
)} )}
<button <button
@@ -366,6 +376,7 @@ export default function Navbar() {
<div className="grid grid-cols-2 gap-2"> <div className="grid grid-cols-2 gap-2">
{ALL_MOBILE_LINKS.map((link) => { {ALL_MOBILE_LINKS.map((link) => {
const isActive = checkIsActive(link.path); const isActive = checkIsActive(link.path);
const isRaise = link.path === '/raise';
const IconComp = link.icon; const IconComp = link.icon;
return ( return (
<Link <Link
@@ -373,7 +384,9 @@ export default function Navbar() {
to={link.path} to={link.path}
onClick={() => setIsMobileOpen(false)} onClick={() => setIsMobileOpen(false)}
className={`px-3 py-2.5 rounded-xl text-xs font-semibold flex items-center gap-2 transition-all ${ className={`px-3 py-2.5 rounded-xl text-xs font-semibold flex items-center gap-2 transition-all ${
isActive isRaise
? 'bg-gradient-to-r from-[#EC4899] via-[#F43F5E] to-[#E11D48] text-white font-bold shadow-md shadow-pink-500/30 col-span-2 justify-center'
: isActive
? 'bg-gradient-to-r from-[#FF6B00] to-[#F97316] text-white shadow-sm' ? 'bg-gradient-to-r from-[#FF6B00] to-[#F97316] text-white shadow-sm'
: 'bg-white/5 text-slate-200 hover:bg-white/10 border border-white/5' : 'bg-white/5 text-slate-200 hover:bg-white/10 border border-white/5'
}`} }`}
@@ -443,6 +456,27 @@ export default function Navbar() {
</motion.div> </motion.div>
)} )}
</AnimatePresence> </AnimatePresence>
{/* Floating RAISE Incubator Button directly below right side of navbar */}
<motion.div
initial={{ y: -10, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.35, ease: 'easeOut', delay: 0.1 }}
className="absolute right-0 sm:right-1 lg:right-1 top-[calc(100%+8px)] pointer-events-auto hidden lg:block z-40"
>
<Link
to="/raise"
className={`flex items-center gap-2 px-3.5 py-2 rounded-xl text-xs font-extrabold text-white transition-all shadow-lg hover:scale-105 active:scale-95 cursor-pointer backdrop-blur-xl ${
checkIsActive('/raise')
? 'bg-gradient-to-r from-pink-600 via-rose-600 to-pink-700 ring-2 ring-pink-300 shadow-pink-500/50'
: 'bg-gradient-to-r from-[#EC4899] via-[#F43F5E] to-[#E11D48] hover:from-[#DB2777] hover:to-[#BE123C] shadow-pink-500/40 border border-pink-400/40'
}`}
style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
>
<Rocket className="w-4 h-4 text-white animate-pulse" />
<span>RAISE Incubator</span>
</Link>
</motion.div>
</motion.nav> </motion.nav>
</header> </header>

View File

@@ -0,0 +1,793 @@
export interface SubjectCurriculum {
name: string;
credits: number;
code?: string;
isElective?: boolean;
electiveSlot?: number;
}
export const PROFESSIONAL_ELECTIVES_LIST: Array<{ code: string; name: string; credits: number }> = [
{ code: 'AD23V11', name: 'Agriculture Data Analytics', credits: 3 },
{ code: 'AD23V12', name: 'Big Data Analytics', credits: 3 },
{ code: 'AD23V13', name: 'Data warehousing and Data Mining', credits: 3 },
{ code: 'AD23V14', name: 'Health Care Analytics', credits: 3 },
{ code: 'AD23V15', name: 'Image and Video Analytics', credits: 3 },
{ code: 'AD23V16', name: 'Web and Social Media Analytics', credits: 3 },
{ code: 'AD23V17', name: 'Predictive Analytics', credits: 3 },
{ code: 'AD23V18', name: 'Sentiment Analysis', credits: 3 },
{ code: 'AD23V21', name: 'AI in IoT', credits: 3 },
{ code: 'AD23V22', name: 'Applied Machine Learning', credits: 3 },
{ code: 'AD23V23', name: 'Cognitive Computing', credits: 3 },
{ code: 'AD23V24', name: 'Computer Vision', credits: 3 },
{ code: 'AD23V25', name: 'Ethics For AI', credits: 3 },
{ code: 'AD23V26', name: 'Game Theory', credits: 3 },
{ code: 'AD23V27', name: 'Quantum Computing Techniques', credits: 3 },
{ code: 'AD23V28', name: 'Responsive AI', credits: 3 },
];
export const DEPARTMENT_CODE_MAP: Record<string, string> = {
'Artificial Intelligence & Data Science': 'AIDS',
'Artificial Intelligence & Machine Learning': 'AIML',
'Biotechnology': 'BIOTECH',
'Communication and Computer Engineering': 'CCE',
'Computer Science & Business Systems': 'CSBS',
'Computer Science & Engineering': 'CSE',
'Electronics & Communication Engineering': 'ECE',
'Electronics Engineering VLSI (Design and Technology)': 'VLSI',
'Mechanical Engineering': 'MECH',
};
export const DEPARTMENT_CURRICULUM: Record<string, Record<number, SubjectCurriculum[]>> = {
AIDS: {
1: [
{ name: "Communicative English (HS23111)", credits: 3 },
{ name: "Matrices and Calculus (MA23111)", credits: 4 },
{ name: "Physics for Information Science (PH23111)", credits: 3 },
{ name: "Problem Solving and C Programming (GE23111)", credits: 3 },
{ name: "Basic Electrical and Electronics Engineering (GE23113)", credits: 3 },
{ name: "Heritage of Tamils (GE23112)", credits: 0 },
{ name: "Physics Laboratory (PH23121)", credits: 1 },
{ name: "Problem Solving and C Programming Laboratory (GE23121)", credits: 1 },
{ name: "Engineering Practices Laboratory (GE23122)", credits: 1 }
],
2: [
{ name: "Professional English (HS23211)", credits: 2 },
{ name: "Engineering Chemistry (CY23211)", credits: 3 },
{ name: "Statistics and Numerical Methods (MA23211)", credits: 4 },
{ name: "Python for Data Science (AD23211)", credits: 3 },
{ name: "Tamil & Technology (GE23213)", credits: 0 },
{ name: "Engineering Graphics (GE23231)", credits: 4 },
{ name: "Data Structures Design (AD23231)", credits: 4 },
{ name: "Chemistry Laboratory (CY23221)", credits: 1 },
{ name: "Python for Data Science Laboratory (AD23221)", credits: 1 },
{ name: "Communication Laboratory / Foreign Language (GE23221)", credits: 1 }
],
3: [
{ name: "Discrete Mathematics (MA23311)", credits: 4 },
{ name: "Artificial Intelligence (AL23311)", credits: 3 },
{ name: "Object Oriented Programming (CS23312)", credits: 3 },
{ name: "Database Management Systems (CS23411)", credits: 3 },
{ name: "Digital Principles and Computer Organization (EC23331)", credits: 4 },
{ name: "Artificial Intelligence Laboratory (AL23321)", credits: 1 },
{ name: "Database Management Systems Laboratory (CS23421)", credits: 1 },
{ name: "Object Oriented Programming Laboratory (CS23322)", credits: 1 },
{ name: "Data Wrangling (AD231C1)", credits: 1 }
],
4: [
{ name: "Environmental Science and Sustainability (GE23411)", credits: 2 },
{ name: "Probability and Statistics (MA23411)", credits: 4 },
{ name: "Data Analytics (AD23411)", credits: 3 },
{ name: "Machine Learning (AL23411)", credits: 3 },
{ name: "Operating Systems (CS23412)", credits: 3 },
{ name: "Design and Analysis of Algorithms (CS23431)", credits: 4 },
{ name: "Data Analytics Laboratory (AD23421)", credits: 1 },
{ name: "Machine Learning Laboratory (AL23421)", credits: 1 },
{ name: "Operating Systems Laboratory (CS23422)", credits: 1 },
{ name: "Introduction to Azure Machine Learning (AD231C1)", credits: 1 }
],
5: [
{ name: "Deep Learning (AD23511)", credits: 3 },
{ name: "Computer Networks (CS23511)", credits: 3 },
{ name: "Data Exploration and Visualization (AD23532)", credits: 4 },
{ name: "Business Analytics (CB23531)", credits: 4 },
{ name: "Disaster Risk Reduction and Management", credits: 0 },
{ name: "Big Data Analytics (AD23531)", credits: 3 },
{ name: "Image & Video Analytics (AD23V15)", credits: 3 },
{ name: "Tableau - Data Visualization (AD23IC3)", credits: 1 },
{ name: "Deep Learning Laboratory (AD23521)", credits: 1 },
{ name: "Computer Networks Laboratory (CS23521)", credits: 1 }
],
6: [
{ name: "Professional Elective III", credits: 3, isElective: true, electiveSlot: 3 },
{ name: "Professional Elective IV", credits: 3, isElective: true, electiveSlot: 4 },
{ name: "Open Elective I", credits: 3 },
{ name: "Open Elective II", credits: 3 },
{ name: "Mandatory Course II", credits: 0 },
{ name: "Embedded Systems and IoT (EC23631)", credits: 4 },
{ name: "Object Oriented Software Engineering (CS23631)", credits: 4 },
{ name: "Mini Project (AD23621)", credits: 2 }
],
7: [
{ name: "Human Values and Ethics (GE23711)", credits: 2 },
{ name: "Elective – Management", credits: 3 },
{ name: "Data and Information Security (CB23511)", credits: 3 },
{ name: "Professional Elective V", credits: 3, isElective: true, electiveSlot: 5 },
{ name: "Professional Elective VI", credits: 3, isElective: true, electiveSlot: 6 },
{ name: "Internship / Certification Course (AD23721)", credits: 2 }
],
8: [
{ name: "Open Elective III", credits: 3 },
{ name: "Project Work (AD23821)", credits: 10 }
]
},
AIML: {
1: [
{ name: "Communicative English (HS23111)", credits: 3 },
{ name: "Engineering Chemistry (CY23111)", credits: 3 },
{ name: "Matrices and Calculus (MA23111)", credits: 4 },
{ name: "Problem Solving and C Programming (GE23111)", credits: 3 },
{ name: "Heritage of Tamils (GE23112)", credits: 0 },
{ name: "Engineering Graphics (GE23131)", credits: 4 },
{ name: "Chemistry Laboratory (CY23121)", credits: 1 },
{ name: "Problem Solving and C Programming Laboratory (GE23121)", credits: 1 },
{ name: "Engineering Practices Laboratory (GE23122)", credits: 1 }
],
2: [
{ name: "Professional English (HS23211)", credits: 2 },
{ name: "Statistics and Numerical Methods (MA23211)", credits: 4 },
{ name: "Physics for Information Science (PH23211)", credits: 3 },
{ name: "Basic Electrical and Electronics Engineering (GE23211)", credits: 3 },
{ name: "Python for Data Science (AD23211)", credits: 3 },
{ name: "Tamils and Technology (GE23213)", credits: 0 },
{ name: "Data Structures and Design (AD23231)", credits: 4 },
{ name: "Physics Laboratory (PH23221)", credits: 1 },
{ name: "Python for Data Science Laboratory (AD23221)", credits: 1 },
{ name: "Communication Laboratory / Foreign Language (GE23221)", credits: 1 }
],
3: [
{ name: "Discrete Mathematics (MA23311)", credits: 4 },
{ name: "Artificial Intelligence (AL23311)", credits: 3 },
{ name: "Object Oriented Programming (CS23312)", credits: 3 },
{ name: "Database Management Systems (CS23411)", credits: 3 },
{ name: "Digital Principles and Computer Organization (EC23331)", credits: 4 },
{ name: "Artificial Intelligence Laboratory (AL23321)", credits: 1 },
{ name: "Database Management Systems Laboratory (CS23421)", credits: 1 },
{ name: "Object Oriented Programming Laboratory (CS23322)", credits: 1 },
{ name: "Data Wrangling (AD231C1)", credits: 1 }
],
4: [
{ name: "Environmental Sciences and Sustainability (GE23411)", credits: 2 },
{ name: "Formal Language and Automata Theory (AD23412)", credits: 4 },
{ name: "Data Analytics (AD23411)", credits: 3 },
{ name: "Machine Learning (AL23411)", credits: 3 },
{ name: "Operating Systems (CS23412)", credits: 3 },
{ name: "Design and Analysis of Algorithms (CS23431)", credits: 4 },
{ name: "Machine Learning Laboratory (AL23421)", credits: 1 },
{ name: "Data Analytics Laboratory (AD23421)", credits: 1 },
{ name: "Operating Systems Laboratory (CS23422)", credits: 1 },
{ name: "Introduction to Azure Machine Learning (AD231C1)", credits: 1 }
],
5: [
{ name: "Deep Learning for Computer Vision (AL23511)", credits: 3 },
{ name: "Computer Networks (CS23511)", credits: 3 },
{ name: "Professional Elective I", credits: 3, isElective: true, electiveSlot: 1 },
{ name: "Professional Elective II", credits: 3, isElective: true, electiveSlot: 2 },
{ name: "Mandatory Course I", credits: 0 },
{ name: "Natural Language Processing (AL23531)", credits: 4 },
{ name: "Big Data Analytics (AD23531)", credits: 4 },
{ name: "Deep Learning for Computer Vision Laboratory (AL23521)", credits: 1 },
{ name: "Computer Networks Laboratory (CS23521)", credits: 1 },
{ name: "Industry Oriented Course III", credits: 1 }
],
6: [
{ name: "Professional Elective III", credits: 3, isElective: true, electiveSlot: 3 },
{ name: "Professional Elective IV", credits: 3, isElective: true, electiveSlot: 4 },
{ name: "Open Elective I", credits: 3 },
{ name: "Open Elective II", credits: 3 },
{ name: "Mandatory Course II", credits: 0 },
{ name: "Embedded Systems and IoT (EC23631)", credits: 4 },
{ name: "Object Oriented Software Engineering (CS23631)", credits: 4 },
{ name: "Mini Project (AL23621)", credits: 2 }
],
7: [
{ name: "Human Values and Ethics (GE23711)", credits: 2 },
{ name: "Elective – Management", credits: 3 },
{ name: "Cryptography and Cyber Security (CS23513)", credits: 3 },
{ name: "Professional Elective V", credits: 3, isElective: true, electiveSlot: 5 },
{ name: "Professional Elective VI", credits: 3, isElective: true, electiveSlot: 6 },
{ name: "Internship / Certification Course (AL23721)", credits: 2 }
],
8: [
{ name: "Open Elective III", credits: 3 },
{ name: "Project Work (AL23821)", credits: 10 }
]
},
BIOTECH: {
1: [
{ name: "Communicative English (HS23111)", credits: 3 },
{ name: "Engineering Chemistry (CY23111)", credits: 3 },
{ name: "Matrices and Calculus (MA23111)", credits: 4 },
{ name: "Problem Solving and C Programming (GE23111)", credits: 3 },
{ name: "Heritage of Tamils (GE23112)", credits: 0 },
{ name: "Engineering Graphics (GE23131)", credits: 4 },
{ name: "Chemistry Laboratory (CY23121)", credits: 1 },
{ name: "Problem Solving and C Programming Laboratory (GE23121)", credits: 1 }
],
2: [
{ name: "Professional English (HS23211)", credits: 2 },
{ name: "Statistics and Numerical Methods (MA23211)", credits: 4 },
{ name: "Physics for Information Science (PH23211)", credits: 3 },
{ name: "Python for Data Science (AD23211)", credits: 3 },
{ name: "Basic Electrical and Electronics Engineering (GE23211)", credits: 3 },
{ name: "Tamils and Technology (GE23213)", credits: 0 },
{ name: "Physics Laboratory (PH23221)", credits: 1 },
{ name: "Python for Data Science Laboratory (AD23221)", credits: 1 },
{ name: "Communication Laboratory / Foreign Language (GE23221)", credits: 1 },
{ name: "Engineering Practices Laboratory (GE23223)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 1", credits: 2 }
],
3: [
{ name: "Discrete Mathematics (MA23311)", credits: 4 },
{ name: "Fundamentals of Economics and Financial Accounting (CB23311)", credits: 4 },
{ name: "Object Oriented Programming (CS23312)", credits: 3 },
{ name: "Data Structures and Algorithms (CS23314)", credits: 3 },
{ name: "Digital Principles and Computer Organization (EC23331)", credits: 4 },
{ name: "Object Oriented Programming Laboratory (CS23322)", credits: 1 },
{ name: "Data Structures and Algorithms Laboratory (CS23324)", credits: 1 },
{ name: "Design Thinking (CB23IC1)", credits: 1 }
],
4: [
{ name: "Environmental Science and Sustainability (GE23411)", credits: 2 },
{ name: "Probability and Statistics (MA23411)", credits: 4 },
{ name: "Introduction to Business Systems (CB23411)", credits: 3 },
{ name: "Database Management Systems (CS23411)", credits: 3 },
{ name: "Operating Systems (CS23412)", credits: 3 },
{ name: "Artificial Intelligence and Machine Learning (AL23431)", credits: 4 },
{ name: "Database Management Systems Laboratory (CS23421)", credits: 1 },
{ name: "Operating Systems Laboratory (CS23422)", credits: 1 },
{ name: "Enterprise Resource Planning (CB23IC2)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 2", credits: 3 }
],
5: [
{ name: "Data and Information Security (CB23511)", credits: 3 },
{ name: "Fundamentals of Management (CB23512)", credits: 3 },
{ name: "Professional Elective I", credits: 3, isElective: true, electiveSlot: 1 },
{ name: "Professional Elective II", credits: 3, isElective: true, electiveSlot: 2 },
{ name: "Mandatory Course I", credits: 0 },
{ name: "Deep Learning Techniques (AD23531)", credits: 4 },
{ name: "Business Analytics (CB23531)", credits: 4 },
{ name: "Data and Information Security Laboratory (CB23521)", credits: 1 },
{ name: "Business Communication Laboratory – I (GE23521)", credits: 1 },
{ name: "Visualization Tools using R (CB23IC3)", credits: 1 }
],
6: [
{ name: "Professional Elective III", credits: 3, isElective: true, electiveSlot: 3 },
{ name: "Professional Elective IV", credits: 3, isElective: true, electiveSlot: 4 },
{ name: "Open Elective I", credits: 3 },
{ name: "Open Elective II", credits: 3 },
{ name: "Mandatory Course II", credits: 0 },
{ name: "Embedded Systems and IoT (EC23631)", credits: 4 },
{ name: "Object Oriented Software Engineering (CS23631)", credits: 4 },
{ name: "Mini Project (CB23621)", credits: 2 },
{ name: "Business Communication Laboratory – II (GE23621)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 3", credits: 3 }
],
7: [
{ name: "Human Values and Ethics (GE23711)", credits: 2 },
{ name: "Elective – Management", credits: 3 },
{ name: "Cryptography and Cyber Security (CS23513)", credits: 3 },
{ name: "Professional Elective V", credits: 3, isElective: true, electiveSlot: 5 },
{ name: "Professional Elective VI", credits: 3, isElective: true, electiveSlot: 6 },
{ name: "Cloud Computing (CS23731)", credits: 4 },
{ name: "Internship / Certification Course (CB23721)", credits: 2 }
],
8: [
{ name: "Open Elective III", credits: 3 },
{ name: "Project Work (CB23821)", credits: 10 }
]
},
CSBS: {
1: [
{ name: "Communicative English (HS23111)", credits: 3 },
{ name: "Engineering Chemistry (CY23111)", credits: 3 },
{ name: "Matrices and Calculus (MA23111)", credits: 4 },
{ name: "Problem Solving and C Programming (GE23111)", credits: 3 },
{ name: "Heritage of Tamils (GE23112)", credits: 0 },
{ name: "Engineering Graphics (GE23131)", credits: 4 },
{ name: "Chemistry Laboratory (CY23121)", credits: 1 },
{ name: "Problem Solving and C Programming Laboratory (GE23121)", credits: 1 }
],
2: [
{ name: "Professional English (HS23211)", credits: 2 },
{ name: "Statistics and Numerical Methods (MA23211)", credits: 4 },
{ name: "Physics for Information Science (PH23211)", credits: 3 },
{ name: "Python for Data Science (AD23211)", credits: 3 },
{ name: "Basic Electrical and Electronics Engineering (GE23211)", credits: 3 },
{ name: "Tamils and Technology (GE23213)", credits: 0 },
{ name: "Physics Laboratory (PH23221)", credits: 1 },
{ name: "Python for Data Science Laboratory (AD23221)", credits: 1 },
{ name: "Communication Laboratory / Foreign Language (GE23221)", credits: 1 },
{ name: "Engineering Practices Laboratory (GE23223)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 1", credits: 2 }
],
3: [
{ name: "Discrete Mathematics (MA23311)", credits: 4 },
{ name: "Fundamentals of Economics and Financial Accounting (CB23311)", credits: 4 },
{ name: "Object Oriented Programming (CS23312)", credits: 3 },
{ name: "Data Structures and Algorithms (CS23314)", credits: 3 },
{ name: "Digital Principles and Computer Organization (EC23331)", credits: 4 },
{ name: "Object Oriented Programming Laboratory (CS23322)", credits: 1 },
{ name: "Data Structures and Algorithms Laboratory (CS23324)", credits: 1 },
{ name: "Design Thinking (CB23IC1)", credits: 1 }
],
4: [
{ name: "Environmental Science and Sustainability (GE23411)", credits: 2 },
{ name: "Probability and Statistics (MA23411)", credits: 4 },
{ name: "Introduction to Business Systems (CB23411)", credits: 3 },
{ name: "Database Management Systems (CS23411)", credits: 3 },
{ name: "Operating Systems (CS23412)", credits: 3 },
{ name: "Artificial Intelligence and Machine Learning (AL23431)", credits: 4 },
{ name: "Database Management Systems Laboratory (CS23421)", credits: 1 },
{ name: "Operating Systems Laboratory (CS23422)", credits: 1 },
{ name: "Enterprise Resource Planning (CB23IC2)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 2", credits: 3 }
],
5: [
{ name: "Data and Information Security (CB23511)", credits: 3 },
{ name: "Fundamentals of Management (CB23512)", credits: 3 },
{ name: "Professional Elective I", credits: 3, isElective: true, electiveSlot: 1 },
{ name: "Professional Elective II", credits: 3, isElective: true, electiveSlot: 2 },
{ name: "Mandatory Course I", credits: 0 },
{ name: "Deep Learning Techniques (AD23531)", credits: 4 },
{ name: "Business Analytics (CB23531)", credits: 4 },
{ name: "Data and Information Security Laboratory (CB23521)", credits: 1 },
{ name: "Business Communication Laboratory – I (GE23521)", credits: 1 },
{ name: "Visualization Tools using R (CB23IC3)", credits: 1 }
],
6: [
{ name: "Professional Elective III", credits: 3, isElective: true, electiveSlot: 3 },
{ name: "Professional Elective IV", credits: 3, isElective: true, electiveSlot: 4 },
{ name: "Open Elective I", credits: 3 },
{ name: "Open Elective II", credits: 3 },
{ name: "Mandatory Course II", credits: 0 },
{ name: "Embedded Systems and IoT (EC23631)", credits: 4 },
{ name: "Object Oriented Software Engineering (CS23631)", credits: 4 },
{ name: "Mini Project (CB23621)", credits: 2 },
{ name: "Business Communication Laboratory – II (GE23621)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 3", credits: 3 }
],
7: [
{ name: "Human Values and Ethics (GE23711)", credits: 2 },
{ name: "Elective – Management", credits: 3 },
{ name: "Cryptography and Cyber Security (CS23513)", credits: 3 },
{ name: "Professional Elective V", credits: 3, isElective: true, electiveSlot: 5 },
{ name: "Professional Elective VI", credits: 3, isElective: true, electiveSlot: 6 },
{ name: "Cloud Computing (CS23731)", credits: 4 },
{ name: "Internship / Certification Course (CB23721)", credits: 2 }
],
8: [
{ name: "Open Elective III", credits: 3 },
{ name: "Project Work (CB23821)", credits: 10 }
]
},
CCE: {
1: [
{ name: "Communicative English (HS23111)", credits: 3 },
{ name: "Matrices and Calculus (MA23111)", credits: 4 },
{ name: "Physics for Electronics Engineering (PH23112)", credits: 3 },
{ name: "Problem Solving and C Programming (GE23111)", credits: 3 },
{ name: "Circuit Analysis (EC23111)", credits: 4 },
{ name: "Heritage of Tamils (GE23112)", credits: 0 },
{ name: "Problem Solving and C Programming Laboratory (GE23121)", credits: 1 },
{ name: "Engineering Practices Laboratory (GE23122)", credits: 1 },
{ name: "Physics Laboratory (PH23121)", credits: 1 }
],
2: [
{ name: "Professional English (HS23211)", credits: 2 },
{ name: "Statistics and Numerical Methods (MA23211)", credits: 4 },
{ name: "Engineering Chemistry (CY23211)", credits: 3 },
{ name: "Python for Data Science (AD23211)", credits: 3 },
{ name: "Tamils and Technology (GE23213)", credits: 0 },
{ name: "Electronic Devices and Circuits (EC23231)", credits: 4 },
{ name: "Engineering Graphics (GE23231)", credits: 4 },
{ name: "Chemistry Laboratory (CY23221)", credits: 1 },
{ name: "Python for Data Science Laboratory (AD23221)", credits: 1 },
{ name: "Communication Laboratory / Foreign Language (GE23221)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 1", credits: 1 }
],
3: [
{ name: "Environmental Science and Sustainability (GE23311)", credits: 2 },
{ name: "Object Oriented Programming (CS23312)", credits: 3 },
{ name: "Wide Band-gap Devices (EV23311)", credits: 3 },
{ name: "Digital System Design (EC23312)", credits: 3 },
{ name: "Signals and Systems (EC23313)", credits: 4 },
{ name: "Digital System Design Laboratory (EC23321)", credits: 1 },
{ name: "Object Oriented Programming Laboratory (CS23322)", credits: 1 },
{ name: "PCB Design (EC23IC1)", credits: 1 }
],
4: [
{ name: "Random Process and Linear Algebra (MA23412)", credits: 4 },
{ name: "Microprocessors and Microcontrollers (EV23411)", credits: 3 },
{ name: "Electromagnetic Fields (EC23412)", credits: 3 },
{ name: "Linear Integrated Circuits (EC23413)", credits: 3 },
{ name: "Digital Signal Processing (EC23431)", credits: 4 },
{ name: "Networks and Security (EC23432)", credits: 3 },
{ name: "Microprocessors and Microcontrollers Laboratory (EV23421)", credits: 1 },
{ name: "Linear Integrated Circuits Laboratory (EC23422)", credits: 1 },
{ name: "Design Thinking for Engineers (CC23IC2)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 2", credits: 2 }
],
5: [
{ name: "ASIC Design (EV23511)", credits: 3 },
{ name: "VLSI and Chip Design (EC23512)", credits: 3 },
{ name: "Professional Elective I", credits: 3, isElective: true, electiveSlot: 1 },
{ name: "Professional Elective II", credits: 3, isElective: true, electiveSlot: 2 },
{ name: "Mandatory Course I", credits: 0 },
{ name: "Embedded Systems and IoT Design (EC23531)", credits: 4 },
{ name: "Artificial Intelligence and Machine Learning (AL23431)", credits: 4 },
{ name: "VLSI Laboratory (EC23521)", credits: 1 },
{ name: "Introduction to ARM Based System Design (EV23IC3)", credits: 1 }
],
6: [
{ name: "CMOS Analog and Mixed Signal IC Design (EV23611)", credits: 3 },
{ name: "Digital Logic Synthesis using HDL (EV23612)", credits: 4 },
{ name: "Professional Elective III", credits: 3, isElective: true, electiveSlot: 3 },
{ name: "Professional Elective IV", credits: 3, isElective: true, electiveSlot: 4 },
{ name: "Open Elective I", credits: 3 },
{ name: "Open Elective II", credits: 3 },
{ name: "Mandatory Course II", credits: 0 },
{ name: "Mini Project (EC23621)", credits: 2 },
{ name: "NCC / Service Club Credit Course Level 3", credits: 3 }
],
7: [
{ name: "Human Values and Ethics (GE23711)", credits: 2 },
{ name: "Elective – Management", credits: 3 },
{ name: "Professional Elective V", credits: 3, isElective: true, electiveSlot: 5 },
{ name: "Professional Elective VI", credits: 3, isElective: true, electiveSlot: 6 },
{ name: "Internship / Certification Course", credits: 2 }
],
8: [
{ name: "Open Elective III", credits: 3 },
{ name: "Project Work", credits: 10 }
]
},
CSE: {
1: [
{ name: "Communicative English (HS23111)", credits: 3 },
{ name: "Engineering Chemistry (CY23111)", credits: 3 },
{ name: "Matrices and Calculus (MA23111)", credits: 4 },
{ name: "Problem Solving and C Programming (GE23111)", credits: 3 },
{ name: "Heritage of Tamils (GE23112)", credits: 0 },
{ name: "Engineering Graphics (GE23131)", credits: 4 },
{ name: "Chemistry Laboratory (CY23121)", credits: 1 },
{ name: "Problem Solving and C Programming Laboratory (GE23121)", credits: 1 }
],
2: [
{ name: "Professional English (HS23211)", credits: 2 },
{ name: "Statistics and Numerical Methods (MA23211)", credits: 4 },
{ name: "Physics for Information Science (PH23211)", credits: 3 },
{ name: "Python for Data Science (AD23211)", credits: 3 },
{ name: "Basic Electrical and Electronics Engineering (GE23211)", credits: 3 },
{ name: "Tamils and Technology (GE23213)", credits: 0 },
{ name: "Physics Laboratory (PH23221)", credits: 1 },
{ name: "Python for Data Science Laboratory (AD23221)", credits: 1 },
{ name: "Communication Laboratory / Foreign Language (GE23221)", credits: 1 },
{ name: "Engineering Practices Laboratory (GE23223)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 1", credits: 2 }
],
3: [
{ name: "Environmental Science and Sustainability (GE23311)", credits: 2 },
{ name: "Discrete Mathematics (MA23311)", credits: 4 },
{ name: "Artificial Intelligence (AL23311)", credits: 3 },
{ name: "Data Structures (CS23311)", credits: 3 },
{ name: "Object Oriented Programming (CS23312)", credits: 3 },
{ name: "Digital Principles and Computer Organization (EC23331)", credits: 4 },
{ name: "Data Structures Laboratory (CS23321)", credits: 1 },
{ name: "Object Oriented Programming Laboratory (CS23322)", credits: 1 },
{ name: "Design Thinking for Software Engineers", credits: 1 }
],
4: [
{ name: "Database Management Systems (CS23411)", credits: 3 },
{ name: "Operating Systems (CS23412)", credits: 3 },
{ name: "Theory of Computation (CS23413)", credits: 3 },
{ name: "Software Development Practices (CS23414)", credits: 3 },
{ name: "Machine Learning Techniques (AL23432)", credits: 4 },
{ name: "Design and Analysis of Algorithms (CS23431)", credits: 4 },
{ name: "Database Management Systems Laboratory (CS23421)", credits: 1 },
{ name: "Operating Systems Laboratory (CS23422)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 2", credits: 3 }
],
5: [
{ name: "Computer Networks (CS23511)", credits: 3 },
{ name: "Compiler Design (CS23512)", credits: 3 },
{ name: "Professional Elective I", credits: 3, isElective: true, electiveSlot: 1 },
{ name: "Professional Elective II", credits: 3, isElective: true, electiveSlot: 2 },
{ name: "Mandatory Course I", credits: 0 },
{ name: "Deep Learning Techniques (AD23531)", credits: 4 },
{ name: "Data Exploration and Visualization (AD23532)", credits: 4 },
{ name: "Computer Networks Laboratory (CS23521)", credits: 1 },
{ name: "Industry Oriented Course", credits: 1 }
],
6: [
{ name: "Professional Elective III", credits: 3, isElective: true, electiveSlot: 3 },
{ name: "Professional Elective IV", credits: 3, isElective: true, electiveSlot: 4 },
{ name: "Open Elective I", credits: 3 },
{ name: "Open Elective II", credits: 3 },
{ name: "Mandatory Course II", credits: 0 },
{ name: "Embedded Systems and IoT (EC23631)", credits: 4 },
{ name: "Object Oriented Software Engineering (CS23631)", credits: 4 },
{ name: "Mini Project", credits: 2 },
{ name: "NCC / Service Club Credit Course Level 3", credits: 3 }
],
7: [
{ name: "Human Values and Ethics (GE23711)", credits: 2 },
{ name: "Elective – Management", credits: 3 },
{ name: "Cryptography and Cyber Security (CS23513)", credits: 3 },
{ name: "Professional Elective V", credits: 3, isElective: true, electiveSlot: 5 },
{ name: "Professional Elective VI", credits: 3, isElective: true, electiveSlot: 6 },
{ name: "Natural Language Processing (AL23531)", credits: 4 },
{ name: "Internship / Certification Course", credits: 2 }
],
8: [
{ name: "Open Elective III", credits: 3 },
{ name: "Project Work", credits: 10 }
]
},
ECE: {
1: [
{ name: "Communicative English (HS23111)", credits: 3 },
{ name: "Matrices and Calculus (MA23111)", credits: 4 },
{ name: "Physics for Electronics Engineering (PH23112)", credits: 3 },
{ name: "Problem Solving and C Programming (GE23111)", credits: 3 },
{ name: "Circuit Analysis (EC23111)", credits: 4 },
{ name: "Heritage of Tamils (GE23112)", credits: 0 },
{ name: "Physics Laboratory (PH23121)", credits: 1 },
{ name: "Problem Solving and C Programming Laboratory (GE23121)", credits: 1 },
{ name: "Engineering Practices Laboratory (GE23122)", credits: 1 }
],
2: [
{ name: "Professional English (HS23211)", credits: 2 },
{ name: "Engineering Chemistry (CY23211)", credits: 3 },
{ name: "Statistics and Numerical Methods (MA23211)", credits: 4 },
{ name: "Python for Data Science (AD23211)", credits: 3 },
{ name: "Tamils and Technology (GE23213)", credits: 0 },
{ name: "Electronic Devices and Circuits (EC23231)", credits: 4 },
{ name: "Engineering Graphics (GE23231)", credits: 4 },
{ name: "Chemistry Laboratory (CY23221)", credits: 1 },
{ name: "Python for Data Science Laboratory (AD23221)", credits: 1 },
{ name: "Communication Laboratory / Foreign Language (GE23221)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 1", credits: 2 }
],
3: [
{ name: "Environmental Science and Sustainability (GE23311)", credits: 2 },
{ name: "Object Oriented Programming (CS23312)", credits: 3 },
{ name: "Control Systems (EC23311)", credits: 4 },
{ name: "Digital System Design (EC23312)", credits: 3 },
{ name: "Signals and Systems (EC23313)", credits: 4 },
{ name: "Digital System Design Laboratory (EC23321)", credits: 1 },
{ name: "Object Oriented Programming Laboratory (CS23322)", credits: 1 },
{ name: "PCB Design (EC23IC1)", credits: 1 }
],
4: [
{ name: "Random Process and Linear Algebra (MA23412)", credits: 4 },
{ name: "Communication Systems (EC23411)", credits: 3 },
{ name: "Electromagnetic Fields (EC23412)", credits: 3 },
{ name: "Linear Integrated Circuits (EC23413)", credits: 3 },
{ name: "Digital Signal Processing (EC23431)", credits: 4 },
{ name: "Networks and Security (EC23432)", credits: 3 },
{ name: "Communication Systems Laboratory (EC23421)", credits: 1 },
{ name: "Linear Integrated Circuits Laboratory (EC23422)", credits: 1 },
{ name: "Cyber Security (EC23IC2)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 2", credits: 3 }
],
5: [
{ name: "Transmission Lines & RF Systems (EC23511)", credits: 3 },
{ name: "VLSI and Chip Design (EC23512)", credits: 3 },
{ name: "Professional Elective I", credits: 3, isElective: true, electiveSlot: 1 },
{ name: "Professional Elective II", credits: 3, isElective: true, electiveSlot: 2 },
{ name: "Mandatory Course I", credits: 0 },
{ name: "Embedded Systems and IoT Design (EC23531)", credits: 4 },
{ name: "Artificial Intelligence and Machine Learning (AL23431)", credits: 4 },
{ name: "VLSI Laboratory (EV23521)", credits: 1 },
{ name: "Introduction to Robotics (EC23IC3)", credits: 1 }
],
6: [
{ name: "Antenna Theory (EC23611)", credits: 3 },
{ name: "Wireless Communication (EC23612)", credits: 3 },
{ name: "Professional Elective III", credits: 3, isElective: true, electiveSlot: 3 },
{ name: "Professional Elective IV", credits: 3, isElective: true, electiveSlot: 4 },
{ name: "Open Elective I", credits: 3 },
{ name: "Open Elective II", credits: 3 },
{ name: "Mandatory Course II", credits: 0 },
{ name: "Mini Project (EC23621)", credits: 2 },
{ name: "Wireless Communication Laboratory (EC23622)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 3", credits: 3 }
],
7: [
{ name: "Human Values and Ethics (GE23711)", credits: 2 },
{ name: "Elective – Management", credits: 3 },
{ name: "Ad Hoc and Wireless Sensor Networks (EC23711)", credits: 3 },
{ name: "Professional Elective V", credits: 3, isElective: true, electiveSlot: 5 },
{ name: "Professional Elective VI", credits: 3, isElective: true, electiveSlot: 6 },
{ name: "Internship / Certification Course", credits: 2 }
],
8: [
{ name: "Open Elective III", credits: 3 },
{ name: "Project Work", credits: 10 }
]
},
VLSI: {
1: [
{ name: "Communicative English (HS23111)", credits: 3 },
{ name: "Matrices and Calculus (MA23111)", credits: 4 },
{ name: "Physics for Electronics Engineering (PH23112)", credits: 3 },
{ name: "Problem Solving and C Programming (GE23111)", credits: 3 },
{ name: "Circuit Analysis (EC23111)", credits: 4 },
{ name: "Heritage of Tamils (GE23112)", credits: 0 },
{ name: "Problem Solving and C Programming Laboratory (GE23121)", credits: 1 },
{ name: "Engineering Practices Laboratory (GE23122)", credits: 1 },
{ name: "Physics Laboratory (PH23121)", credits: 1 }
],
2: [
{ name: "Professional English (HS23211)", credits: 2 },
{ name: "Statistics and Numerical Methods (MA23211)", credits: 4 },
{ name: "Engineering Chemistry (CY23211)", credits: 3 },
{ name: "Python for Data Science (AD23211)", credits: 3 },
{ name: "Tamils and Technology (GE23213)", credits: 0 },
{ name: "Electronic Devices and Circuits (EC23231)", credits: 4 },
{ name: "Engineering Graphics (GE23231)", credits: 4 },
{ name: "Chemistry Laboratory (CY23221)", credits: 1 },
{ name: "Python for Data Science Laboratory (AD23221)", credits: 1 },
{ name: "Communication Laboratory / Foreign Language (GE23221)", credits: 1 }
],
3: [
{ name: "Environmental Science and Sustainability (GE23311)", credits: 2 },
{ name: "Wide Band-gap Devices (EV23311)", credits: 3 },
{ name: "Digital System Design (EC23312)", credits: 3 },
{ name: "Signals and Systems (EC23313)", credits: 4 },
{ name: "Object Oriented Programming (CS23312)", credits: 3 },
{ name: "Digital System Design Laboratory (EC23321)", credits: 1 },
{ name: "Object Oriented Programming Laboratory (CS23322)", credits: 1 },
{ name: "PCB Design (EC23IC1)", credits: 1 }
],
4: [
{ name: "Random Process and Linear Algebra (MA23412)", credits: 4 },
{ name: "Microprocessors and Microcontrollers (EV23411)", credits: 3 },
{ name: "Electromagnetic Fields (EC23412)", credits: 3 },
{ name: "Linear Integrated Circuits (EC23413)", credits: 3 },
{ name: "Digital Signal Processing (EC23431)", credits: 4 },
{ name: "Networks and Security (EC23432)", credits: 3 },
{ name: "Microprocessors and Microcontrollers Laboratory (EV23421)", credits: 1 },
{ name: "Linear Integrated Circuits Laboratory (EC23422)", credits: 1 },
{ name: "Design Thinking for Engineers (CC23IC2)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 2", credits: 2 }
],
5: [
{ name: "ASIC Design (EV23511)", credits: 3 },
{ name: "VLSI and Chip Design (EC23512)", credits: 3 },
{ name: "Professional Elective I", credits: 3, isElective: true, electiveSlot: 1 },
{ name: "Professional Elective II", credits: 3, isElective: true, electiveSlot: 2 },
{ name: "Mandatory Course I", credits: 0 },
{ name: "Embedded Systems and IoT Design (EC23531)", credits: 4 },
{ name: "Artificial Intelligence and Machine Learning (AL23431)", credits: 4 },
{ name: "VLSI Laboratory (EC23521)", credits: 1 },
{ name: "Introduction to ARM Based System Design (EV23IC3)", credits: 1 }
],
6: [
{ name: "CMOS Analog and Mixed Signal IC Design (EV23611)", credits: 3 },
{ name: "Digital Logic Synthesis using HDL (EV23612)", credits: 4 },
{ name: "Professional Elective III", credits: 3, isElective: true, electiveSlot: 3 },
{ name: "Professional Elective IV", credits: 3, isElective: true, electiveSlot: 4 },
{ name: "Open Elective I", credits: 3 },
{ name: "Open Elective II", credits: 3 },
{ name: "Mandatory Course II", credits: 0 },
{ name: "Mini Project", credits: 2 },
{ name: "NCC / Service Club Credit Course Level 3", credits: 3 }
],
7: [
{ name: "Human Values and Ethics (GE23711)", credits: 2 },
{ name: "Elective – Management", credits: 3 },
{ name: "Professional Elective V", credits: 3, isElective: true, electiveSlot: 5 },
{ name: "Professional Elective VI", credits: 3, isElective: true, electiveSlot: 6 },
{ name: "Internship / Certification Course", credits: 2 }
],
8: [
{ name: "Open Elective III", credits: 3 },
{ name: "Project Work", credits: 10 }
]
},
MECH: {
1: [
{ name: "Communicative English (HS23111)", credits: 3 },
{ name: "Matrices and Calculus (MA23111)", credits: 4 },
{ name: "Electrical, Electronics and Instrumentation Engineering (GE23114)", credits: 3 },
{ name: "Problem Solving and C Programming (GE23111)", credits: 3 },
{ name: "Materials Science (PH23113)", credits: 3 },
{ name: "Heritage of Tamils (GE23112)", credits: 0 },
{ name: "Physics Laboratory (PH23121)", credits: 1 },
{ name: "Problem Solving and C Programming Laboratory (GE23121)", credits: 1 },
{ name: "Engineering Practices Laboratory (GE23122)", credits: 1 },
{ name: "Basic Electrical and Electronics Engineering Laboratory (GE23123)", credits: 1 }
],
2: [
{ name: "Professional English (HS23211)", credits: 2 },
{ name: "Engineering Chemistry (CY23211)", credits: 3 },
{ name: "Statistics and Numerical Methods (MA23211)", credits: 4 },
{ name: "Python for Data Science (AD23211)", credits: 3 },
{ name: "Tamils and Technology (GE23213)", credits: 0 },
{ name: "Engineering Graphics (GE23231)", credits: 4 },
{ name: "Chemistry Laboratory (CY23221)", credits: 1 },
{ name: "Python for Data Science Laboratory (AD23221)", credits: 1 },
{ name: "Communication Laboratory / Foreign Language (GE23221)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 1", credits: 2 }
],
3: [
{ name: "Transforms and Partial Differential Equations (MA23313)", credits: 4 },
{ name: "Engineering Mechanics (ME23311)", credits: 3 },
{ name: "Engineering Thermodynamics (ME23312)", credits: 4 },
{ name: "Fluid Mechanics and Machinery (ME23313)", credits: 4 },
{ name: "Manufacturing Processes (ME23314)", credits: 3 },
{ name: "Object Oriented Programming (CS23312)", credits: 3 },
{ name: "Computer Aided Machine Drawing Laboratory (ME23321)", credits: 1 },
{ name: "Object Oriented Programming Laboratory (CS23322)", credits: 1 }
],
4: [
{ name: "Environmental Science for Mechanical Engineers (GE23412)", credits: 2 },
{ name: "Engineering Materials and Metallurgy (ME23411)", credits: 3 },
{ name: "Manufacturing Technology (ME23412)", credits: 3 },
{ name: "Mechanics of Solids (ME23413)", credits: 3 },
{ name: "Theory of Machines (ME23414)", credits: 4 },
{ name: "Thermal Engineering (ME23431)", credits: 4 },
{ name: "Manufacturing Technology Laboratory (ME23421)", credits: 1 },
{ name: "Strength of Materials and Fluid Machinery Laboratory (ME23422)", credits: 1 },
{ name: "Introduction to Product Lifecycle Management (ME23IC1)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 2", credits: 3 }
],
5: [
{ name: "Design of Machine Elements (ME23511)", credits: 4 },
{ name: "Heat and Mass Transfer (ME23512)", credits: 4 },
{ name: "Professional Elective I", credits: 3, isElective: true, electiveSlot: 1 },
{ name: "Professional Elective II", credits: 3, isElective: true, electiveSlot: 2 },
{ name: "Mandatory Course I", credits: 0 },
{ name: "Mechatronics (ME23531)", credits: 4 },
{ name: "Heat Transfer Laboratory (ME23521)", credits: 1 },
{ name: "Professional / Skill Development (ME23522)", credits: 1 },
{ name: "Introduction to Supply Chain Management (ME23IC2)", credits: 1 }
],
6: [
{ name: "Design of Transmission Systems (ME23611)", credits: 3 },
{ name: "Professional Elective III", credits: 3, isElective: true, electiveSlot: 3 },
{ name: "Professional Elective IV", credits: 3, isElective: true, electiveSlot: 4 },
{ name: "Open Elective I", credits: 3 },
{ name: "Open Elective II", credits: 3 },
{ name: "Mandatory Course II", credits: 0 },
{ name: "Metrology and Measurements (ME23631)", credits: 4 },
{ name: "Design and Fabrication Project (ME23621)", credits: 2 },
{ name: "CAD and CAM Laboratory (ME23622)", credits: 1 },
{ name: "Dynamics Laboratory (ME23623)", credits: 1 },
{ name: "Introduction to HVAC Systems (ME23IC3)", credits: 1 },
{ name: "NCC / Service Club Credit Course Level 3", credits: 3 }
],
7: [
{ name: "Human Values and Ethics (GE23711)", credits: 2 },
{ name: "Elective – Management", credits: 3 },
{ name: "Industrial Robotics (ME23712)", credits: 4 },
{ name: "Finite Element Analysis (ME23713)", credits: 4 },
{ name: "Professional Elective V", credits: 3, isElective: true, electiveSlot: 5 },
{ name: "Professional Elective VI", credits: 3, isElective: true, electiveSlot: 6 },
{ name: "Internship / Certification Course (ME23721)", credits: 2 }
],
8: [
{ name: "Open Elective III", credits: 3 },
{ name: "Project Work", credits: 10 }
]
}
};

View File

@@ -117,13 +117,22 @@ export const FEATURES: Feature[] = [
bgColor: '#FFFBEB', bgColor: '#FFFBEB',
}, },
{ {
id: 'raise', id: 'gatepass',
icon: 'Rocket', icon: 'Ticket',
title: 'RAISE Incubator', title: 'Student Gate Pass',
description: 'Pitch startup ideas, get incubation support, seed funding opportunities, and entrepreneurial mentoring.', description: 'Request digital campus out-pass, track warden approvals, and present clearance QR codes.',
path: '/raise', path: '/notes?portal=gatepass',
color: '#EC4899', color: '#10B981',
bgColor: '#FDF2F8', bgColor: '#ECFDF5',
},
{
id: 'proficiency',
icon: 'Award',
title: 'Student Proficiency',
description: 'Track domain competency scores, placement readiness analytics, and verified skill certifications.',
path: '/notes?portal=proficiency',
color: '#8B5CF6',
bgColor: '#F5F3FF',
}, },
]; ];
@@ -3104,6 +3113,26 @@ export const EVENTS_DATA: Event[] = [
// ─── Clubs ─────────────────────────────────────────────────────────────────── // ─── Clubs ───────────────────────────────────────────────────────────────────
export const CLUBS_DATA: Club[] = [ export const CLUBS_DATA: Club[] = [
{
id: 'center_raise',
name: 'RAISE INCUBATOR',
description: 'Pitch startup ideas, get incubation support, seed funding opportunities, and entrepreneurial mentoring.',
details: 'RAISE Incubator is a dedicated Section 8 (not-for-profit) company situated directly within the Rajalakshmi Institute of Technology campus. We transform engineering ideas into market-viable products with seed funding, mentoring, and patent support.',
category: 'Center of Excellence',
type: 'Center',
members: 2450,
presidentName: 'Incubation Director',
vicePresidentName: 'Startup Lead',
coordinatorName: 'Dr. RIT Innovation Cell',
year: 'Campus Incubation Center',
contactEmail: 'incubator@ritchennai.edu.in',
contactPhone: '+91 44 6718 1600',
icon: 'Rocket',
logoUrl: '/logo.png',
socialLinks: {
website: '/raise',
},
},
{ {
id: 'nss', id: 'nss',
name: 'NSS OF RIT', name: 'NSS OF RIT',
@@ -3760,13 +3789,13 @@ export const QUESTIONS_DATA: Question[] = [
// ─── Toolkit Items ─────────────────────────────────────────────────────────── // ─── Toolkit Items ───────────────────────────────────────────────────────────
export const TOOLKIT_ITEMS: ToolkitItem[] = [ export const TOOLKIT_ITEMS: ToolkitItem[] = [
{ id: '7', title: 'GPA and internal calculator', description: 'Access GPA, CGPA, and internal marks calculator utilities.', type: 'link', url: '#', icon: 'Calculator', category: 'Engineering' },
{ id: '1', title: 'CS tools', description: 'Access specialized development environments, IDE configurations, compiler guides, GitHub setup instructions, and essential documentation for Computer Science students.', type: 'link', url: '#', icon: 'Code', category: 'Engineering' }, { id: '1', title: 'CS tools', description: 'Access specialized development environments, IDE configurations, compiler guides, GitHub setup instructions, and essential documentation for Computer Science students.', type: 'link', url: '#', icon: 'Code', category: 'Engineering' },
{ id: '2', title: 'Business tools', description: 'Explore enterprise resource planning (ERP) guides, financial spreadsheets, management templates, and business analytics tools for CSBS and management students.', type: 'link', url: '#', icon: 'Briefcase', category: 'Engineering' }, { id: '2', title: 'Business tools', description: 'Explore enterprise resource planning (ERP) guides, financial spreadsheets, management templates, and business analytics tools for CSBS and management students.', type: 'link', url: '#', icon: 'Briefcase', category: 'Engineering' },
{ id: '3', title: 'Cybersecurity tools', description: 'Discover network simulators, virtualization setup guides, penetration testing tutorials, cryptography tools, and security lab blueprints.', type: 'link', url: '#', icon: 'Shield', category: 'Engineering' }, { id: '3', title: 'Cybersecurity tools', description: 'Discover network simulators, virtualization setup guides, penetration testing tutorials, cryptography tools, and security lab blueprints.', type: 'link', url: '#', icon: 'Shield', category: 'Engineering' },
{ id: '4', title: 'AI tools', description: 'Configure machine learning frameworks, deep learning libraries, Jupyter environments, dataset download scripts, and pre-trained model APIs.', type: 'link', url: '#', icon: 'Brain', category: 'Engineering' }, { id: '4', title: 'AI tools', description: 'Configure machine learning frameworks, deep learning libraries, Jupyter environments, dataset download scripts, and pre-trained model APIs.', type: 'link', url: '#', icon: 'Brain', category: 'Engineering' },
{ id: '5', title: 'Electrical and Electronic tools', description: 'Access circuit design simulators, MATLAB packages, microcontroller IDE setups, hardware debugging guides, and lab calculation spreadsheets.', type: 'link', url: '#', icon: 'Cpu', category: 'Engineering' }, { id: '5', title: 'Electrical and Electronic tools', description: 'Access circuit design simulators, MATLAB packages, microcontroller IDE setups, hardware debugging guides, and lab calculation spreadsheets.', type: 'link', url: '#', icon: 'Cpu', category: 'Engineering' },
{ id: '6', title: 'Biotech tools', description: 'Find biology lab guidelines, chemical structure tools, bioinformatics databases tutorials, genetic sequence analysis resources, and safety manuals.', type: 'link', url: '#', icon: 'FlaskConical', category: 'Engineering' }, { id: '6', title: 'Biotech tools', description: 'Find biology lab guidelines, chemical structure tools, bioinformatics databases tutorials, genetic sequence analysis resources, and safety manuals.', type: 'link', url: '#', icon: 'FlaskConical', category: 'Engineering' },
{ id: '7', title: 'GPA and internal calculator', description: 'Access GPA, CGPA, and internal marks calculator utilities.', type: 'link', url: '#', icon: 'Calculator', category: 'Engineering' },
]; ];
// ─── Campus Locations ──────────────────────────────────────────────────────── // ─── Campus Locations ────────────────────────────────────────────────────────
@@ -3803,5 +3832,9 @@ export const DEPARTMENTS = [
'Computer Science & Engineering', 'Computer Science & Engineering',
'Electronics & Communication Engineering', 'Electronics & Communication Engineering',
'Electronics Engineering VLSI (Design and Technology)', 'Electronics Engineering VLSI (Design and Technology)',
'Humanities & Sciences',
'M.E. (VLSI Design)',
'M.Tech (Data Science)',
'Mathematics',
'Mechanical Engineering', 'Mechanical Engineering',
]; ];

View File

@@ -452,7 +452,7 @@ export default function Community() {
<h3 className={`font-bold text-[#1E293B] mb-1 hover:text-[#F97316] transition-colors tracking-tight ${isFeatured ? 'text-base md:text-lg' : 'text-sm md:text-base' <h3 className={`font-bold text-[#1E293B] mb-1 hover:text-[#F97316] transition-colors tracking-tight ${isFeatured ? 'text-base md:text-lg' : 'text-sm md:text-base'
}`} }`}
style={{ fontFamily: 'Playfair Display, serif' }}> style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
{q.title} {q.title}
</h3> </h3>

View File

@@ -1,4 +1,5 @@
import { useState, useMemo } from 'react'; import { useState, useMemo, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { import {
Users, Tag, X, Mail, Phone, UserCheck, GraduationCap, Info, Users, Tag, X, Mail, Phone, UserCheck, GraduationCap, Info,
@@ -12,6 +13,13 @@ import { StaggerContainer, StaggerItem } from '@/components/AnimatedContainer/An
import AnimatedContainer from '@/components/AnimatedContainer/AnimatedContainer'; import AnimatedContainer from '@/components/AnimatedContainer/AnimatedContainer';
import { CLUBS_DATA } from '@/constants'; import { CLUBS_DATA } from '@/constants';
import type { Club } from '@/types'; import type { Club } from '@/types';
import { useAuth } from '@/context/AuthContext';
import {
getClubUserIdentifier,
fetchClubLikes,
fetchUserLikedClubs,
toggleClubLikeInDb,
} from '@/services/clubService';
const CLUB_CATEGORY_COLORS: Record<string, string> = { const CLUB_CATEGORY_COLORS: Record<string, string> = {
Technical: '#3B82F6', Technical: '#3B82F6',
@@ -118,6 +126,9 @@ const QUIZ_QUESTIONS_ALL = [
]; ];
export default function Events() { export default function Events() {
const { user } = useAuth();
const userIdentifier = useMemo(() => getClubUserIdentifier(user?.email), [user?.email]);
const [selectedClub, setSelectedClub] = useState<Club | null>(null); const [selectedClub, setSelectedClub] = useState<Club | null>(null);
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
const [selectedCategory, setSelectedCategory] = useState<string>('All'); const [selectedCategory, setSelectedCategory] = useState<string>('All');
@@ -139,7 +150,7 @@ export default function Events() {
return { totalCount: CLUBS_DATA.length, centersCount: centers, clubsCount: clubs }; return { totalCount: CLUBS_DATA.length, centersCount: centers, clubsCount: clubs };
}, []); }, []);
// ─── Interactive Like System ───────────────────────────────────────────── // ─── Database-Backed Interactive Like System ─────────────────────────────
const [likedClubs, setLikedClubs] = useState<Set<string>>(() => { const [likedClubs, setLikedClubs] = useState<Set<string>>(() => {
try { try {
localStorage.removeItem('rit_freshers_liked_clubs'); localStorage.removeItem('rit_freshers_liked_clubs');
@@ -158,10 +169,45 @@ export default function Events() {
return initialMap; return initialMap;
}); });
const toggleLike = (clubId: string) => { // Sync likes with database backend on mount and when user identity changes
useEffect(() => {
let isMounted = true;
async function syncWithDatabase() {
// Fetch total like counts for all clubs from database
const dbLikesMap = await fetchClubLikes();
if (isMounted && Object.keys(dbLikesMap).length > 0) {
setLikesMap((prev) => {
const merged = { ...prev };
Object.entries(dbLikesMap).forEach(([id, count]) => {
merged[id] = count;
});
return merged;
});
}
// Fetch user's liked clubs from database
const userLikedList = await fetchUserLikedClubs(userIdentifier);
if (isMounted && userLikedList.length > 0) {
setLikedClubs(new Set(userLikedList));
try {
localStorage.setItem('rit_freshers_liked_clubs_v3', JSON.stringify(userLikedList));
} catch {}
}
}
syncWithDatabase();
return () => {
isMounted = false;
};
}, [userIdentifier]);
const toggleLike = async (clubId: string) => {
const isCurrentlyLiked = likedClubs.has(clubId); const isCurrentlyLiked = likedClubs.has(clubId);
const next = new Set(likedClubs); const next = new Set(likedClubs);
// Optimistic UI update
if (isCurrentlyLiked) { if (isCurrentlyLiked) {
next.delete(clubId); next.delete(clubId);
setLikesMap((l) => ({ ...l, [clubId]: Math.max(0, (l[clubId] || 1) - 1) })); setLikesMap((l) => ({ ...l, [clubId]: Math.max(0, (l[clubId] || 1) - 1) }));
@@ -174,8 +220,24 @@ export default function Events() {
try { try {
localStorage.setItem('rit_freshers_liked_clubs_v3', JSON.stringify(Array.from(next))); localStorage.setItem('rit_freshers_liked_clubs_v3', JSON.stringify(Array.from(next)));
} catch {} } catch {}
// Persist like directly in PostgreSQL database backend
const dbResult = await toggleClubLikeInDb(clubId, userIdentifier);
if (dbResult) {
setLikesMap((l) => ({ ...l, [clubId]: dbResult.count }));
if (dbResult.liked) {
setLikedClubs((prev) => new Set([...prev, clubId]));
} else {
setLikedClubs((prev) => {
const updated = new Set(prev);
updated.delete(clubId);
return updated;
});
}
}
}; };
// ─── Quiz Matcher State ────────────────────────────────────────────────── // ─── Quiz Matcher State ──────────────────────────────────────────────────
const [isQuizOpen, setIsQuizOpen] = useState(false); const [isQuizOpen, setIsQuizOpen] = useState(false);
const [matcherTarget, setMatcherTarget] = useState<'All' | 'Club' | 'Center'>('All'); const [matcherTarget, setMatcherTarget] = useState<'All' | 'Club' | 'Center'>('All');
@@ -268,7 +330,7 @@ export default function Events() {
// 2. Category Filter check // 2. Category Filter check
if (selectedCategory !== 'All') { if (selectedCategory !== 'All') {
if (selectedCategory === 'Center of Excellence' && !isCenter) return false; if (selectedCategory === 'Center of Excellence' && !isCenter) return false;
if (selectedCategory !== 'Center of Excellence' && item.category !== selectedCategory) return false; if (selectedCategory !== 'Center of Excellence' && item.category.toLowerCase() !== selectedCategory.toLowerCase()) return false;
} }
// 3. Search Query check // 3. Search Query check
@@ -298,7 +360,7 @@ export default function Events() {
<Building2 className="w-3.5 h-3.5" /> <Building2 className="w-3.5 h-3.5" />
<span>RIT Official Directory</span> <span>RIT Official Directory</span>
</div> </div>
<h1 className="text-3xl md:text-4xl font-bold text-[#1E293B] mb-2" style={{ fontFamily: 'Playfair Display, serif' }}> <h1 className="text-3xl md:text-4xl font-extrabold text-[#1E293B] mb-2" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
Student Clubs &{' '} Student Clubs &{' '}
<span style={{ background: 'linear-gradient(135deg, #F97316, #FB923C)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}> <span style={{ background: 'linear-gradient(135deg, #F97316, #FB923C)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
Centers of Excellence Centers of Excellence
@@ -329,7 +391,7 @@ export default function Events() {
<Target className="w-3.5 h-3.5" /> <Target className="w-3.5 h-3.5" />
<span>AI-Powered Matcher Quiz</span> <span>AI-Powered Matcher Quiz</span>
</div> </div>
<h2 className="text-2xl md:text-3xl font-bold text-white mb-2" style={{ fontFamily: 'Playfair Display, serif' }}> <h2 className="text-2xl md:text-3xl font-extrabold text-white mb-2" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
Find Your Ideal Club or Future Tech Center! Find Your Ideal Club or Future Tech Center!
</h2> </h2>
<p className="text-slate-300 text-sm leading-relaxed" style={{ fontFamily: 'Inter, sans-serif' }}> <p className="text-slate-300 text-sm leading-relaxed" style={{ fontFamily: 'Inter, sans-serif' }}>
@@ -446,7 +508,7 @@ export default function Events() {
</button> </button>
</div> </div>
) : ( ) : (
<StaggerContainer className="pb-8"> <StaggerContainer key={`${selectedType}-${selectedCategory}-${searchQuery}`} className="pb-8">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 md:gap-7"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 md:gap-7">
{filteredData.map((club) => { {filteredData.map((club) => {
const IconComponent = (club.icon && CLUB_ICON_MAP[club.icon]) || Atom; const IconComponent = (club.icon && CLUB_ICON_MAP[club.icon]) || Atom;
@@ -594,7 +656,7 @@ export default function Events() {
</div> </div>
{/* Question Header */} {/* Question Header */}
<h3 className="text-xl font-bold text-[#1E293B] mb-5" style={{ fontFamily: 'Playfair Display, serif' }}> <h3 className="text-xl font-extrabold text-[#1E293B] mb-5" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
{QUIZ_QUESTIONS_ALL[quizStep].title} {QUIZ_QUESTIONS_ALL[quizStep].title}
</h3> </h3>
@@ -628,7 +690,7 @@ export default function Events() {
<div className="w-14 h-14 rounded-2xl bg-orange-100 text-[#F97316] flex items-center justify-center mx-auto mb-3"> <div className="w-14 h-14 rounded-2xl bg-orange-100 text-[#F97316] flex items-center justify-center mx-auto mb-3">
<CheckCircle2 className="w-8 h-8" /> <CheckCircle2 className="w-8 h-8" />
</div> </div>
<h3 className="text-2xl font-bold text-[#1E293B]" style={{ fontFamily: 'Playfair Display, serif' }}> <h3 className="text-2xl font-extrabold text-[#1E293B]" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
Your Recommended Matches! Your Recommended Matches!
</h3> </h3>
<p className="text-xs text-slate-500 mt-1">Based on your selections, here are your top recommended RIT entities:</p> <p className="text-xs text-slate-500 mt-1">Based on your selections, here are your top recommended RIT entities:</p>
@@ -768,7 +830,7 @@ export default function Events() {
</span> </span>
)} )}
</div> </div>
<h2 className="text-2xl font-bold text-[#1E293B]" style={{ fontFamily: 'Playfair Display, serif' }}> <h2 className="text-2xl font-extrabold text-[#1E293B]" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
{selectedClub.name} {selectedClub.name}
</h2> </h2>
</div> </div>
@@ -869,7 +931,8 @@ export default function Events() {
selectedClub.socialLinks?.instagram || selectedClub.socialLinks?.instagram ||
selectedClub.socialLinks?.linkedin || selectedClub.socialLinks?.linkedin ||
selectedClub.socialLinks?.whatsapp || selectedClub.socialLinks?.whatsapp ||
selectedClub.socialLinks?.youtube selectedClub.socialLinks?.youtube ||
selectedClub.socialLinks?.website
) && ( ) && (
<div className="mb-6 bg-orange-50/60 border border-orange-200/80 rounded-2xl p-4"> <div className="mb-6 bg-orange-50/60 border border-orange-200/80 rounded-2xl p-4">
<div className="flex items-center justify-between mb-3"> <div className="flex items-center justify-between mb-3">
@@ -881,6 +944,16 @@ export default function Events() {
</div> </div>
<div className="grid grid-cols-2 gap-2.5"> <div className="grid grid-cols-2 gap-2.5">
{selectedClub.socialLinks?.website && (
<Link
to={selectedClub.socialLinks.website}
onClick={() => setSelectedClub(null)}
className="flex items-center gap-2 p-2.5 rounded-xl bg-gradient-to-r from-[#EC4899] via-[#F43F5E] to-[#E11D48] text-white hover:from-pink-600 hover:to-rose-600 transition-all text-xs font-bold shadow-xs col-span-2 justify-center"
>
<Rocket className="w-4 h-4 text-white shrink-0 animate-pulse" />
<span>Launch RAISE Incubator Portal →</span>
</Link>
)}
{selectedClub.socialLinks?.instagram && ( {selectedClub.socialLinks?.instagram && (
<a <a

View File

@@ -1,6 +1,6 @@
import { useState, useMemo, useEffect } from 'react'; import { useState, useMemo, useEffect } from 'react';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { Search, Users, Filter, ArrowUpDown, BookOpen, ChevronRight, AlertTriangle, Sparkles, X } from 'lucide-react'; import { Search, Users, Filter, ArrowUpDown, BookOpen, ChevronRight, Sparkles, X } from 'lucide-react';
import FacultyCard from '@/components/FacultyCard/FacultyCard'; import FacultyCard from '@/components/FacultyCard/FacultyCard';
import { StaggerContainer, StaggerItem } from '@/components/AnimatedContainer/AnimatedContainer'; import { StaggerContainer, StaggerItem } from '@/components/AnimatedContainer/AnimatedContainer';
import { FACULTY_DATA, DEPARTMENTS } from '@/constants'; import { FACULTY_DATA, DEPARTMENTS } from '@/constants';
@@ -8,17 +8,17 @@ import { Link, useSearchParams } from 'react-router-dom';
import { getStoredImsSession } from '@/services/imsService'; import { getStoredImsSession } from '@/services/imsService';
const DEPT_MAP: Record<string, { abrv: string; full: string }> = { const DEPT_MAP: Record<string, { abrv: string; full: string }> = {
'Computer Science & Engineering': { abrv: 'CSE', full: 'Computer Science and Engineering' }, 'Computer Science & Engineering': { abrv: 'CSE', full: 'Computer Science & Engineering' },
'Computer Science & Business Systems': { abrv: 'CSBS', full: 'Computer Science and Business Systems' }, 'Computer Science & Business Systems': { abrv: 'CSBS', full: 'Computer Science & Business Systems' },
'Artificial Intelligence & Machine Learning': { abrv: 'AIML', full: 'Artificial Intelligence and Machine Learning' }, 'Artificial Intelligence & Machine Learning': { abrv: 'AIML', full: 'Artificial Intelligence & Machine Learning' },
'Electronics & Communication Engineering': { abrv: 'ECE', full: 'Electronics and Communication Engineering' }, 'Electronics & Communication Engineering': { abrv: 'ECE', full: 'Electronics & Communication Engineering' },
'Mechanical Engineering': { abrv: 'MECH', full: 'Mechanical Engineering' }, 'Mechanical Engineering': { abrv: 'MECH', full: 'Mechanical Engineering' },
'Civil Engineering': { abrv: 'CIVIL', full: 'Civil Engineering' }, 'Civil Engineering': { abrv: 'CIVIL', full: 'Civil Engineering' },
'Artificial Intelligence & Data Science': { abrv: 'AI&DS', full: 'Artificial Intelligence and Data Science' }, 'Artificial Intelligence & Data Science': { abrv: 'AI & DS', full: 'Artificial Intelligence & Data Science' },
'Electrical & Electronics Engineering': { abrv: 'EEE', full: 'Electrical and Electronics Engineering' }, 'Electrical & Electronics Engineering': { abrv: 'EEE', full: 'Electrical & Electronics Engineering' },
'Communication and Computer Engineering': { abrv: 'CCE', full: 'Communication and Computer Engineering' }, 'Communication and Computer Engineering': { abrv: 'CCE', full: 'Communication and Computer Engineering' },
'Electronics Engineering VLSI (Design and Technology)': { abrv: 'EE VLSI (D&T)', full: 'Electronics Engineering (VLSI Design and Technology)' }, 'Electronics Engineering VLSI (Design and Technology)': { abrv: 'EE VLSI (D&T)', full: 'Electronics Engineering VLSI (Design and Technology)' },
'Humanities & Sciences': { abrv: 'H&S', full: 'Humanities and Sciences' }, 'Humanities & Sciences': { abrv: 'H&S', full: 'Humanities & Sciences' },
'M.Tech (Data Science)': { abrv: 'M.TECH-DS', full: 'M.Tech (Data Science)' }, 'M.Tech (Data Science)': { abrv: 'M.TECH-DS', full: 'M.Tech (Data Science)' },
'Mathematics': { abrv: 'MATHS', full: 'Mathematics' }, 'Mathematics': { abrv: 'MATHS', full: 'Mathematics' },
'M.E. (VLSI Design)': { abrv: 'MEVLSI', full: 'M.E. (VLSI Design)' }, 'M.E. (VLSI Design)': { abrv: 'MEVLSI', full: 'M.E. (VLSI Design)' },
@@ -109,8 +109,13 @@ export default function Faculty() {
}, [filteredAndSortedFaculty]); }, [filteredAndSortedFaculty]);
const departmentOrder = useMemo(() => { const departmentOrder = useMemo(() => {
return DEPARTMENTS.filter(d => d !== 'All Departments'); const presentDepts = Object.keys(groupedFaculty);
}, []); const sorted = DEPARTMENTS.filter(d => d !== 'All Departments');
presentDepts.forEach(d => {
if (!sorted.includes(d)) sorted.push(d);
});
return sorted;
}, [groupedFaculty]);
return ( return (
<div className="min-h-screen relative overflow-hidden bg-[#F8FAFC]"> <div className="min-h-screen relative overflow-hidden bg-[#F8FAFC]">
@@ -173,27 +178,6 @@ export default function Faculty() {
</motion.div> </motion.div>
)} )}
{/* Under Development Banner */}
<motion.div
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
className="bg-amber-50 border border-amber-200 rounded-2xl p-4 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 text-amber-900 shadow-2xs"
>
<div className="flex items-center gap-3">
<div className="w-9 h-9 rounded-xl bg-amber-500/10 border border-amber-500/20 flex items-center justify-center shrink-0">
<AlertTriangle className="w-5 h-5 text-amber-600" />
</div>
<div>
<div className="text-xs font-bold uppercase tracking-wider text-amber-700" style={{ fontFamily: 'Inter, sans-serif' }}>Under Development</div>
<div className="text-xs font-medium text-amber-900" style={{ fontFamily: 'Inter, sans-serif' }}>
Currently displaying sample mock faculty profiles. Official RIT faculty directory integration is in progress.
</div>
</div>
</div>
<span className="px-3 py-1 rounded-full bg-amber-100 border border-amber-300 text-[11px] font-bold text-amber-800 shrink-0">
Mock Data Active
</span>
</motion.div>
{/* Faculty Hero Section */} {/* Faculty Hero Section */}
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6 items-stretch"> <div className="grid grid-cols-1 lg:grid-cols-12 gap-6 items-stretch">

View File

@@ -1,10 +1,12 @@
import { useState, useRef, useEffect } from 'react'; import { useState, useRef, useEffect } from 'react';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { Search, Download, Filter, BookOpen, FileText, ScrollText, BookMarked, ChevronRight, ArrowLeft, ExternalLink, Code, Globe, GitBranch, Cloud, Palette, Shield, DollarSign, BarChart3, Users, Megaphone, TrendingUp, UserCheck, ClipboardList, ShoppingCart, Cpu, Activity, LayoutGrid, Layers, Zap, Settings, Gauge, Radio, Wifi, Bot, Sun, Wrench, Binary, BatteryCharging, CircuitBoard, ShieldCheck, BrainCircuit, FlaskConical, Lock, Terminal, Dna, Microscope, Sparkles, Smartphone, Monitor, Server, CheckCircle2, Database, Briefcase, AlertTriangle, RefreshCw } from 'lucide-react'; import { Search, Download, Filter, BookOpen, FileText, ScrollText, BookMarked, ChevronRight, ArrowLeft, ExternalLink, Code, Globe, GitBranch, Cloud, Palette, Shield, DollarSign, BarChart3, Users, Megaphone, TrendingUp, UserCheck, ClipboardList, ShoppingCart, Cpu, Activity, LayoutGrid, Layers, Zap, Settings, Gauge, Radio, Wifi, Bot, Sun, Wrench, Binary, BatteryCharging, CircuitBoard, ShieldCheck, BrainCircuit, FlaskConical, Lock, Terminal, Dna, Microscope, Sparkles, Smartphone, Monitor, Server, CheckCircle2, Database, Briefcase, AlertTriangle, RefreshCw, Ticket, Award, QrCode } from 'lucide-react';
import SectionTitle from '@/components/SectionTitle/SectionTitle'; import SectionTitle from '@/components/SectionTitle/SectionTitle';
const MOODLE_URL = 'http://182.74.17.142/moodle/login/index.php'; const MOODLE_URL = 'http://182.74.17.142/moodle/login/index.php';
const GATE_PASS_URL = 'http://182.74.17.142/gatepass/login';
const PROFICIENCY_URL = 'http://182.74.17.142/proficiency/login';
import { getBackendUrl } from '@/lib/utils'; import { getBackendUrl } from '@/lib/utils';
import { StaggerContainer, StaggerItem } from '@/components/AnimatedContainer/AnimatedContainer'; import { StaggerContainer, StaggerItem } from '@/components/AnimatedContainer/AnimatedContainer';
import { TOOLKIT_ITEMS, DEPARTMENTS } from '@/constants'; import { TOOLKIT_ITEMS, DEPARTMENTS } from '@/constants';
@@ -1329,6 +1331,7 @@ export default function Notes() {
const [selectedCategory, setSelectedCategory] = useState<number | null>(null); const [selectedCategory, setSelectedCategory] = useState<number | null>(null);
const [selectedLanguage, setSelectedLanguage] = useState<number | null>(null); const [selectedLanguage, setSelectedLanguage] = useState<number | null>(null);
const [selectedTool, setSelectedTool] = useState<{ name: string; desc: string; url: string; category: string; bg: string; color: string } | null>(null); const [selectedTool, setSelectedTool] = useState<{ name: string; desc: string; url: string; category: string; bg: string; color: string } | null>(null);
const [activePortalTab, setActivePortalTab] = useState<'moodle' | 'gatepass' | 'proficiency'>('moodle');
const [notes, setNotes] = useState<any[]>([]); const [notes, setNotes] = useState<any[]>([]);
@@ -1363,6 +1366,10 @@ export default function Notes() {
}, []); }, []);
useEffect(() => { useEffect(() => {
const portalParam = searchParams.get('portal');
if (portalParam === 'gatepass' || portalParam === 'proficiency' || portalParam === 'moodle') {
setActivePortalTab(portalParam as any);
}
if (searchParams.get('section') === 'toolkit') { if (searchParams.get('section') === 'toolkit') {
setTimeout(() => { setTimeout(() => {
toolkitRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }); toolkitRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
@@ -1407,6 +1414,42 @@ export default function Notes() {
return matchSearch && matchSem && matchDept && matchType && matchSubject; return matchSearch && matchSem && matchDept && matchType && matchSubject;
}); });
const portalParam = searchParams.get('portal');
let headerBreadcrumb = 'Notes & PYQs';
let headerTitle = (
<>
Notes &{' '}
<span style={{ background: 'linear-gradient(135deg, #F97316, #FB923C)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
PYQs
</span>
</>
);
let headerSubtitle = 'Access semester-wise notes, previous year question papers, and study materials.';
if (portalParam === 'gatepass') {
headerBreadcrumb = 'Student Gate Pass';
headerTitle = (
<>
Student{' '}
<span style={{ background: 'linear-gradient(135deg, #F97316, #FB923C)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
Gate Pass
</span>
</>
);
headerSubtitle = 'Official digital campus out-pass system for day scholars and hostellers.';
} else if (portalParam === 'proficiency') {
headerBreadcrumb = 'Student Proficiency';
headerTitle = (
<>
Student{' '}
<span style={{ background: 'linear-gradient(135deg, #F97316, #FB923C)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
Proficiency
</span>
</>
);
headerSubtitle = 'Official RIT skill evaluation platform for tracking domain competencies and certifications.';
}
return ( return (
<div className="min-h-screen" style={{ backgroundColor: '#FAFAFA' }}> <div className="min-h-screen" style={{ backgroundColor: '#FAFAFA' }}>
{/* Header */} {/* Header */}
@@ -1417,16 +1460,13 @@ export default function Notes() {
<div className="flex items-center gap-2 text-xs text-[#94A3B8] mb-3" style={{ fontFamily: 'Inter, sans-serif' }}> <div className="flex items-center gap-2 text-xs text-[#94A3B8] mb-3" style={{ fontFamily: 'Inter, sans-serif' }}>
<span>Home</span> <span>Home</span>
<ChevronRight className="w-3 h-3" /> <ChevronRight className="w-3 h-3" />
<span className="text-[#F97316]">Notes & PYQs</span> <span className="text-[#F97316]">{headerBreadcrumb}</span>
</div> </div>
<h1 className="text-3xl md:text-4xl font-bold text-[#1E293B] mb-2" style={{ fontFamily: 'Playfair Display, serif' }}> <h1 className="text-3xl md:text-4xl font-extrabold text-[#1E293B] mb-2" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
Notes &{' '} {headerTitle}
<span style={{ background: 'linear-gradient(135deg, #F97316, #FB923C)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
PYQs
</span>
</h1> </h1>
<p className="text-[#475569]" style={{ fontFamily: 'Inter, sans-serif' }}> <p className="text-[#475569]" style={{ fontFamily: 'Inter, sans-serif' }}>
Access semester-wise notes, previous year question papers, and study materials. {headerSubtitle}
</p> </p>
</motion.div> </motion.div>
</div> </div>
@@ -1486,7 +1526,7 @@ export default function Notes() {
: 'Back to Toolkit'} : 'Back to Toolkit'}
</button> </button>
<h2 className="text-3xl font-bold text-[#1E293B] mb-2" style={{ fontFamily: 'Playfair Display, serif' }}> <h2 className="text-2xl md:text-3xl font-extrabold text-[#1E293B] mb-2" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
{selectedTool !== null ? ( {selectedTool !== null ? (
<> <>
{selectedTool.name}{' '} {selectedTool.name}{' '}
@@ -1548,7 +1588,7 @@ export default function Notes() {
</div> </div>
<div> <div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<h3 className="text-2xl md:text-3xl font-bold text-[#1E293B]" style={{ fontFamily: 'Playfair Display, serif' }}> <h3 className="text-2xl md:text-3xl font-extrabold text-[#1E293B]" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
{selectedTool.name} {selectedTool.name}
</h3> </h3>
<span <span
@@ -1675,7 +1715,7 @@ export default function Notes() {
</div> </div>
<div> <div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<h3 className="text-2xl md:text-3xl font-bold text-[#1E293B]" style={{ fontFamily: 'Playfair Display, serif' }}> <h3 className="text-2xl md:text-3xl font-extrabold text-[#1E293B]" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
{PROGRAMMING_LANGUAGES_GUIDES[selectedLanguage].name} {PROGRAMMING_LANGUAGES_GUIDES[selectedLanguage].name}
</h3> </h3>
<span <span
@@ -1919,114 +1959,304 @@ export default function Notes() {
) : ( ) : (
<div> <div>
<div className="mb-12"> <div className="mb-12">
{/* Moodle Academic Portal Hero Hub */}
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
className="bg-white rounded-3xl border border-[#E5E7EB] p-8 md:p-10 shadow-sm relative overflow-hidden mb-8"
style={{ boxShadow: '0 4px 25px -5px rgba(0,0,0,0.05)' }}
>
{/* Subtle Decorative Background Glow */}
<div className="absolute -right-20 -top-20 w-80 h-80 bg-orange-500/5 rounded-full blur-3xl pointer-events-none" />
<div className="absolute -left-20 -bottom-20 w-80 h-80 bg-amber-500/5 rounded-full blur-3xl pointer-events-none" />
<div className="relative z-10 max-w-3xl mx-auto text-center">
{/* Header Badge */}
<div className="inline-flex items-center gap-2 px-3.5 py-1.5 rounded-full bg-orange-50 border border-orange-200 text-xs font-bold text-[#F97316] mb-6">
<BookOpen className="w-4 h-4 text-[#F97316]" />
<span>Official RIT Learning Management System</span>
</div>
{/* Main Heading */} {/* 1. MOODLE LMS CARD */}
<h2 className="text-3xl sm:text-4xl font-extrabold text-[#1E293B] mb-4 leading-tight" style={{ fontFamily: 'Playfair Display, serif' }}> {activePortalTab === 'moodle' && (
Access Academic Notes & Study Materials on{' '} <motion.div
<span style={{ background: 'linear-gradient(135deg, #F97316, #FB923C)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}> initial={{ opacity: 0, y: 16 }}
Moodle animate={{ opacity: 1, y: 0 }}
</span> transition={{ duration: 0.4 }}
</h2> className="bg-white rounded-3xl border border-[#E5E7EB] p-8 md:p-10 shadow-sm relative overflow-hidden mb-8"
style={{ boxShadow: '0 4px 25px -5px rgba(0,0,0,0.05)' }}
>
<div className="absolute -right-20 -top-20 w-80 h-80 bg-orange-500/5 rounded-full blur-3xl pointer-events-none" />
<div className="absolute -left-20 -bottom-20 w-80 h-80 bg-amber-500/5 rounded-full blur-3xl pointer-events-none" />
<p className="text-base text-[#64748B] mb-8 leading-relaxed max-w-2xl mx-auto" style={{ fontFamily: 'Inter, sans-serif' }}> <div className="relative z-10 max-w-3xl mx-auto text-center">
All official semester notes, previous year question papers (PYQs), syllabus data, and lab manuals are hosted directly on the official Rajalakshmi Institute of Technology Moodle server. <div className="inline-flex items-center gap-2 px-3.5 py-1.5 rounded-full bg-orange-50 border border-orange-200 text-xs font-bold text-[#F97316] mb-6">
</p> <BookOpen className="w-4 h-4 text-[#F97316]" />
<span>Official RIT Learning Management System</span>
{/* Primary Launch Action Buttons */}
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 mb-10">
<a
href={MOODLE_URL}
target="_blank"
rel="noopener noreferrer"
className="w-full sm:w-auto inline-flex items-center justify-center gap-3 px-8 py-4 rounded-2xl text-base font-bold text-white shadow-lg hover:shadow-xl hover:-translate-y-0.5 transition-all cursor-pointer"
style={{
fontFamily: 'Poppins, sans-serif',
background: 'linear-gradient(135deg, #F97316, #FB923C)',
}}
>
<ExternalLink className="w-5 h-5" />
Launch Official RIT Moodle Portal
</a>
<button
onClick={() => {
navigator.clipboard.writeText(MOODLE_URL);
alert('Moodle URL copied to clipboard!');
}}
className="w-full sm:w-auto inline-flex items-center justify-center gap-2 px-6 py-4 rounded-2xl border border-[#E5E7EB] hover:border-[#F97316] bg-[#F8FAFC] hover:bg-white text-sm font-semibold text-[#475569] hover:text-[#F97316] transition-all cursor-pointer"
style={{ fontFamily: 'Poppins, sans-serif' }}
>
<Globe className="w-4 h-4 text-[#64748B]" />
Copy Moodle Direct URL
</button>
</div>
{/* Feature Highlights Grid */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-left border-t border-[#F1F5F9] pt-8">
<div className="p-4 rounded-2xl bg-[#F8FAFC] border border-[#E5E7EB]/70 flex flex-col gap-2">
<div className="w-8 h-8 rounded-xl bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center text-emerald-600">
<ShieldCheck className="w-4 h-4" />
</div> </div>
<div className="text-xs font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>
100% Direct & Secure <h2 className="text-2xl md:text-3xl font-extrabold text-[#1E293B] mb-4 leading-tight" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
Access Academic Notes & Study Materials on{' '}
<span style={{ background: 'linear-gradient(135deg, #F97316, #FB923C)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
Moodle
</span>
</h2>
<p className="text-base text-[#64748B] mb-8 leading-relaxed max-w-2xl mx-auto" style={{ fontFamily: 'Inter, sans-serif' }}>
All official semester notes, previous year question papers (PYQs), syllabus data, and lab manuals are hosted directly on the official Rajalakshmi Institute of Technology Moodle server.
</p>
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 mb-10">
<a
href={MOODLE_URL}
target="_blank"
rel="noopener noreferrer"
className="w-full sm:w-auto inline-flex items-center justify-center gap-3 px-8 py-4 rounded-2xl text-base font-bold text-white shadow-lg hover:shadow-xl hover:-translate-y-0.5 transition-all cursor-pointer"
style={{
fontFamily: 'Poppins, sans-serif',
background: 'linear-gradient(135deg, #F97316, #FB923C)',
}}
>
<ExternalLink className="w-5 h-5" />
Launch Official RIT Moodle Portal
</a>
<button
onClick={() => {
navigator.clipboard.writeText(MOODLE_URL);
alert('Moodle URL copied to clipboard!');
}}
className="w-full sm:w-auto inline-flex items-center justify-center gap-2 px-6 py-4 rounded-2xl border border-[#E5E7EB] hover:border-[#F97316] bg-[#F8FAFC] hover:bg-white text-sm font-semibold text-[#475569] hover:text-[#F97316] transition-all cursor-pointer"
style={{ fontFamily: 'Poppins, sans-serif' }}
>
<Globe className="w-4 h-4 text-[#64748B]" />
Copy Moodle Direct URL
</button>
</div> </div>
<div className="text-[11px] text-[#64748B]" style={{ fontFamily: 'Inter, sans-serif' }}>
Authenticates directly on official college server <code className="bg-slate-200/80 px-1 py-0.5 rounded text-[10px]">182.74.17.142</code>. <div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-left border-t border-[#F1F5F9] pt-8">
<div className="p-4 rounded-2xl bg-[#F8FAFC] border border-[#E5E7EB]/70 flex flex-col gap-2">
<div className="w-8 h-8 rounded-xl bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center text-emerald-600">
<ShieldCheck className="w-4 h-4" />
</div>
<div className="text-xs font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>
100% Direct & Secure
</div>
<div className="text-[11px] text-[#64748B]" style={{ fontFamily: 'Inter, sans-serif' }}>
Authenticates directly on official college server <code className="bg-slate-200/80 px-1 py-0.5 rounded text-[10px]">182.74.17.142</code>.
</div>
</div>
<div className="p-4 rounded-2xl bg-[#F8FAFC] border border-[#E5E7EB]/70 flex flex-col gap-2">
<div className="w-8 h-8 rounded-xl bg-blue-500/10 border border-blue-500/20 flex items-center justify-center text-blue-600">
<FileText className="w-4 h-4" />
</div>
<div className="text-xs font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>
Official Semester Notes
</div>
<div className="text-[11px] text-[#64748B]" style={{ fontFamily: 'Inter, sans-serif' }}>
Complete 1st to 8th Semester course notes, PYQs, and assignments.
</div>
</div>
<div className="p-4 rounded-2xl bg-[#F8FAFC] border border-[#E5E7EB]/70 flex flex-col gap-2">
<div className="w-8 h-8 rounded-xl bg-purple-500/10 border border-purple-500/20 flex items-center justify-center text-purple-600">
<Lock className="w-4 h-4" />
</div>
<div className="text-xs font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>
Student Login
</div>
<div className="text-[11px] text-[#64748B]" style={{ fontFamily: 'Inter, sans-serif' }}>
Log in using your RIT Register Number and official Moodle password.
</div>
</div>
</div> </div>
</div> </div>
</motion.div>
)}
<div className="p-4 rounded-2xl bg-[#F8FAFC] border border-[#E5E7EB]/70 flex flex-col gap-2"> {/* 2. STUDENT GATE PASS CARD */}
<div className="w-8 h-8 rounded-xl bg-blue-500/10 border border-blue-500/20 flex items-center justify-center text-blue-600"> {activePortalTab === 'gatepass' && (
<FileText className="w-4 h-4" /> <motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
className="bg-white rounded-3xl border border-[#E5E7EB] p-8 md:p-10 shadow-sm relative overflow-hidden mb-8"
style={{ boxShadow: '0 4px 25px -5px rgba(0,0,0,0.05)' }}
>
<div className="absolute -right-20 -top-20 w-80 h-80 bg-orange-500/5 rounded-full blur-3xl pointer-events-none" />
<div className="absolute -left-20 -bottom-20 w-80 h-80 bg-amber-500/5 rounded-full blur-3xl pointer-events-none" />
<div className="relative z-10 max-w-3xl mx-auto text-center">
<div className="inline-flex items-center gap-2 px-3.5 py-1.5 rounded-full bg-orange-50 border border-orange-200 text-xs font-bold text-[#F97316] mb-6">
<Ticket className="w-4 h-4 text-[#F97316]" />
<span>Official RIT Campus Gate Pass Portal</span>
</div> </div>
<div className="text-xs font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>
Official Semester Notes <h2 className="text-2xl md:text-3xl font-extrabold text-[#1E293B] mb-4 leading-tight" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
Request & Manage Out-Passes on{' '}
<span style={{ background: 'linear-gradient(135deg, #F97316, #FB923C)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
Student Gate Pass
</span>
</h2>
<p className="text-base text-[#64748B] mb-8 leading-relaxed max-w-2xl mx-auto" style={{ fontFamily: 'Inter, sans-serif' }}>
Official digital campus out-pass system for day scholars and hostellers. Submit leave requests, track warden approvals, and present digital QR passes to campus security.
</p>
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 mb-10">
<button
onClick={() => {
alert('Student Gate Pass Portal dummy link triggered! You can provide the live URL anytime to connect it.');
}}
className="w-full sm:w-auto inline-flex items-center justify-center gap-3 px-8 py-4 rounded-2xl text-base font-bold text-white shadow-lg hover:shadow-xl hover:-translate-y-0.5 transition-all cursor-pointer"
style={{
fontFamily: 'Poppins, sans-serif',
background: 'linear-gradient(135deg, #F97316, #FB923C)',
}}
>
<ExternalLink className="w-5 h-5" />
Launch Student Gate Pass Portal
</button>
<button
onClick={() => {
navigator.clipboard.writeText(GATE_PASS_URL);
alert('Student Gate Pass URL copied to clipboard!');
}}
className="w-full sm:w-auto inline-flex items-center justify-center gap-2 px-6 py-4 rounded-2xl border border-[#E5E7EB] hover:border-[#F97316] bg-[#F8FAFC] hover:bg-white text-sm font-semibold text-[#475569] hover:text-[#F97316] transition-all cursor-pointer"
style={{ fontFamily: 'Poppins, sans-serif' }}
>
<Globe className="w-4 h-4 text-[#64748B]" />
Copy Gate Pass Direct URL
</button>
</div> </div>
<div className="text-[11px] text-[#64748B]" style={{ fontFamily: 'Inter, sans-serif' }}>
Complete 1st to 8th Semester course notes, PYQs, and assignments. <div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-left border-t border-[#F1F5F9] pt-8">
<div className="p-4 rounded-2xl bg-emerald-50/50 border border-emerald-100 flex flex-col gap-2">
<div className="w-8 h-8 rounded-xl bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center text-emerald-600">
<QrCode className="w-4 h-4" />
</div>
<div className="text-xs font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>
Digital QR Clearance
</div>
<div className="text-[11px] text-[#64748B]" style={{ fontFamily: 'Inter, sans-serif' }}>
Generate real-time encrypted QR code for security verification at campus gates.
</div>
</div>
<div className="p-4 rounded-2xl bg-emerald-50/50 border border-emerald-100 flex flex-col gap-2">
<div className="w-8 h-8 rounded-xl bg-teal-500/10 border border-teal-500/20 flex items-center justify-center text-teal-600">
<CheckCircle2 className="w-4 h-4" />
</div>
<div className="text-xs font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>
Warden & Parent Approval
</div>
<div className="text-[11px] text-[#64748B]" style={{ fontFamily: 'Inter, sans-serif' }}>
Automated SMS and email notifications to wardens, mentors, and registered guardians.
</div>
</div>
<div className="p-4 rounded-2xl bg-emerald-50/50 border border-emerald-100 flex flex-col gap-2">
<div className="w-8 h-8 rounded-xl bg-green-500/10 border border-green-500/20 flex items-center justify-center text-green-600">
<ShieldCheck className="w-4 h-4" />
</div>
<div className="text-xs font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>
Verified RIT Student ID
</div>
<div className="text-[11px] text-[#64748B]" style={{ fontFamily: 'Inter, sans-serif' }}>
Authenticates securely with official college Roll Number & registered portal password.
</div>
</div>
</div> </div>
</div> </div>
</motion.div>
)}
<div className="p-4 rounded-2xl bg-[#F8FAFC] border border-[#E5E7EB]/70 flex flex-col gap-2"> {/* 3. STUDENT PROFICIENCY CARD */}
<div className="w-8 h-8 rounded-xl bg-purple-500/10 border border-purple-500/20 flex items-center justify-center text-purple-600"> {activePortalTab === 'proficiency' && (
<Lock className="w-4 h-4" /> <motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
className="bg-white rounded-3xl border border-[#E5E7EB] p-8 md:p-10 shadow-sm relative overflow-hidden mb-8"
style={{ boxShadow: '0 4px 25px -5px rgba(0,0,0,0.05)' }}
>
<div className="absolute -right-20 -top-20 w-80 h-80 bg-orange-500/5 rounded-full blur-3xl pointer-events-none" />
<div className="absolute -left-20 -bottom-20 w-80 h-80 bg-amber-500/5 rounded-full blur-3xl pointer-events-none" />
<div className="relative z-10 max-w-3xl mx-auto text-center">
<div className="inline-flex items-center gap-2 px-3.5 py-1.5 rounded-full bg-orange-50 border border-orange-200 text-xs font-bold text-[#F97316] mb-6">
<Award className="w-4 h-4 text-[#F97316]" />
<span>Official RIT Skill Evaluation & Readiness System</span>
</div> </div>
<div className="text-xs font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>
Student Login <h2 className="text-2xl md:text-3xl font-extrabold text-[#1E293B] mb-4 leading-tight" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
Track Skill Milestones & Credentials on{' '}
<span style={{ background: 'linear-gradient(135deg, #F97316, #FB923C)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
Student Proficiency
</span>
</h2>
<p className="text-base text-[#64748B] mb-8 leading-relaxed max-w-2xl mx-auto" style={{ fontFamily: 'Inter, sans-serif' }}>
Official RIT skill evaluation platform for tracking domain competencies, technical certifications, placement readiness scores, and verified proficiency badges.
</p>
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 mb-10">
<button
onClick={() => {
alert('Student Proficiency Portal dummy link triggered! You can provide the live URL anytime to connect it.');
}}
className="w-full sm:w-auto inline-flex items-center justify-center gap-3 px-8 py-4 rounded-2xl text-base font-bold text-white shadow-lg hover:shadow-xl hover:-translate-y-0.5 transition-all cursor-pointer"
style={{
fontFamily: 'Poppins, sans-serif',
background: 'linear-gradient(135deg, #F97316, #FB923C)',
}}
>
<ExternalLink className="w-5 h-5" />
Launch Student Proficiency Portal
</button>
<button
onClick={() => {
navigator.clipboard.writeText(PROFICIENCY_URL);
alert('Student Proficiency URL copied to clipboard!');
}}
className="w-full sm:w-auto inline-flex items-center justify-center gap-2 px-6 py-4 rounded-2xl border border-[#E5E7EB] hover:border-[#F97316] bg-[#F8FAFC] hover:bg-white text-sm font-semibold text-[#475569] hover:text-[#F97316] transition-all cursor-pointer"
style={{ fontFamily: 'Poppins, sans-serif' }}
>
<Globe className="w-4 h-4 text-[#64748B]" />
Copy Proficiency Direct URL
</button>
</div> </div>
<div className="text-[11px] text-[#64748B]" style={{ fontFamily: 'Inter, sans-serif' }}>
Log in using your RIT Register Number and official Moodle password. <div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-left border-t border-[#F1F5F9] pt-8">
<div className="p-4 rounded-2xl bg-purple-50/50 border border-purple-100 flex flex-col gap-2">
<div className="w-8 h-8 rounded-xl bg-purple-500/10 border border-purple-500/20 flex items-center justify-center text-purple-600">
<TrendingUp className="w-4 h-4" />
</div>
<div className="text-xs font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>
Skill Radar & Domain Scores
</div>
<div className="text-[11px] text-[#64748B]" style={{ fontFamily: 'Inter, sans-serif' }}>
Visualize competency scores across programming, core engineering, and analytical reasoning.
</div>
</div>
<div className="p-4 rounded-2xl bg-purple-50/50 border border-purple-100 flex flex-col gap-2">
<div className="w-8 h-8 rounded-xl bg-indigo-500/10 border border-indigo-500/20 flex items-center justify-center text-indigo-600">
<Sparkles className="w-4 h-4" />
</div>
<div className="text-xs font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>
Placement Readiness
</div>
<div className="text-[11px] text-[#64748B]" style={{ fontFamily: 'Inter, sans-serif' }}>
Live feedback on mock interview performances, coding assessments, and company cutoffs.
</div>
</div>
<div className="p-4 rounded-2xl bg-purple-50/50 border border-purple-100 flex flex-col gap-2">
<div className="w-8 h-8 rounded-xl bg-violet-500/10 border border-violet-500/20 flex items-center justify-center text-violet-600">
<Award className="w-4 h-4" />
</div>
<div className="text-xs font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>
Verified Credentials
</div>
<div className="text-[11px] text-[#64748B]" style={{ fontFamily: 'Inter, sans-serif' }}>
Upload, verify, and generate shareable badges for NPTEL, Coursera & RIT certification credits.
</div>
</div>
</div> </div>
</div> </div>
</div> </motion.div>
)}
{/* Technical Security Note */} </div>
<div className="mt-6 text-[11px] text-[#94A3B8] leading-relaxed" style={{ fontFamily: 'Inter, sans-serif' }}>
ℹ️ <span className="font-semibold text-[#64748B]">Security Note:</span> To protect student session privacy and prevent clickjacking attacks, RIT&apos;s Moodle server enforces opening the portal directly in a secure browser tab rather than inside third-party frames (<code className="bg-slate-100 px-1 py-0.5 rounded text-[10px]">X-Frame-Options: SAMEORIGIN</code>).
</div>
</div>
</motion.div>
</div> </div>
</div> )}
)}
</div> </div>
</div> </div>
); );

View File

@@ -1,17 +1,21 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { motion } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { import {
GraduationCap, Clock, MapPin, GraduationCap, Clock, MapPin, Users,
Calendar, Building, ArrowRight, LogOut, Calendar, Building, ShieldCheck,
HelpCircle, Compass, ExternalLink, LogIn Sparkles, KeyRound, ArrowRight, LogOut,
AlertCircle, User, HelpCircle, Compass, ChevronRight, ExternalLink
} from 'lucide-react'; } from 'lucide-react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useAuth } from '@/context/AuthContext'; import { useAuth } from '@/context/AuthContext';
import { import {
fetchStudentDashboard, fetchStudentDashboard,
loginWithIms,
fetchMockImsUsers,
getStoredImsSession, getStoredImsSession,
clearImsSession, clearImsSession,
type StudentDashboardData type StudentDashboardData,
type MockUserCredential
} from '@/services/imsService'; } from '@/services/imsService';
export default function StudentDashboard() { export default function StudentDashboard() {
@@ -21,34 +25,79 @@ export default function StudentDashboard() {
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [activeTab, setActiveTab] = useState<'timetable' | 'location'>('timetable'); const [activeTab, setActiveTab] = useState<'timetable' | 'location'>('timetable');
const imsSession = getStoredImsSession(); // IMS Authentication Session State
const [imsSession, setImsSession] = useState<{ token: string; student: any } | null>(getStoredImsSession());
const [regNumberInput, setRegNumberInput] = useState('');
const [passwordInput, setPasswordInput] = useState('');
const [isLoggingIn, setIsLoggingIn] = useState(false);
const [loginError, setLoginError] = useState<string | null>(null);
const [mockUsers, setMockUsers] = useState<MockUserCredential[]>([]);
const [showDemoBox, setShowDemoBox] = useState(false);
const effectiveRegNumber = user?.regNumber || imsSession?.student?.regNumber; const effectiveRegNumber = user?.regNumber || imsSession?.student?.regNumber;
// Load available mock accounts for demo helper
useEffect(() => {
fetchMockImsUsers().then((users) => {
setMockUsers(users);
if (users.length > 0 && !regNumberInput) {
setRegNumberInput(users[0].regNumber);
setPasswordInput(users[0].defaultPassword);
}
});
}, []);
const loadDashboard = async () => { const loadDashboard = async () => {
if (!effectiveRegNumber) return; const regToUse = effectiveRegNumber || imsSession?.student?.regNumber;
if (!regToUse) return;
setIsLoading(true); setIsLoading(true);
setError(null); setError(null);
try { try {
const result = await fetchStudentDashboard(effectiveRegNumber, user?.email); const res = await fetchStudentDashboard(regToUse, user?.email);
setData(result); setData(res);
} catch (err: any) { } catch (err: any) {
console.error('Error loading student dashboard:', err); console.error('Error loading student dashboard:', err);
setError(err?.message || 'Unable to fetch student records from IMS.'); setError(err.message || 'Failed to fetch timetable and classroom location.');
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
}; };
useEffect(() => { useEffect(() => {
if (effectiveRegNumber) { if (effectiveRegNumber || imsSession?.student?.regNumber) {
loadDashboard(); loadDashboard();
} else {
setData(null);
} }
}, [effectiveRegNumber, user]); }, [effectiveRegNumber, imsSession, user]);
const handleLogout = () => { const handleImsLogin = async (e: React.FormEvent) => {
e.preventDefault();
if (!regNumberInput.trim() || !passwordInput.trim()) return;
setIsLoggingIn(true);
setLoginError(null);
try {
const res = await loginWithIms(regNumberInput.trim(), passwordInput.trim());
if (res.success && res.token && res.student) {
setImsSession({ token: res.token, student: res.student });
} else {
setLoginError(res.message || 'Invalid credentials or Register Number not found.');
}
} catch (err: any) {
setLoginError(err.message || 'Invalid credentials or Register Number not found.');
} finally {
setIsLoggingIn(false);
}
};
const handleQuickFillMock = (mock: MockUserCredential) => {
setRegNumberInput(mock.regNumber);
setPasswordInput(mock.defaultPassword);
};
const handleLogoutIms = () => {
clearImsSession(); clearImsSession();
setImsSession(null);
logout(); logout();
setData(null); setData(null);
}; };
@@ -56,45 +105,163 @@ export default function StudentDashboard() {
// ───────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────
// VIEW 1: UNAUTHENTICATED INVITATION SCREEN // VIEW 1: UNAUTHENTICATED INVITATION SCREEN
// ───────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────
if (!effectiveRegNumber) { if (!effectiveRegNumber && !imsSession) {
return ( return (
<div className="min-h-[80vh] bg-[#0a0f1d] text-slate-100 flex items-center justify-center py-12 px-4 sm:px-6"> <div className="min-h-screen" style={{ backgroundColor: '#FAFAFA' }}>
<div className="w-full max-w-md space-y-6 text-center"> {/* Header */}
<div className="bg-white border-b border-[#E5E7EB] py-10">
<div className="container-custom">
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5 }}>
<div className="flex items-center gap-2 text-xs text-[#94A3B8] mb-3" style={{ fontFamily: 'Inter, sans-serif' }}>
<Link to="/" className="hover:text-[#F97316]">Home</Link>
<ChevronRight className="w-3 h-3" />
<span className="text-[#F97316]">Time Table</span>
</div>
<h1 className="text-3xl md:text-4xl font-extrabold text-[#1E293B] mb-2" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
IMS Student{' '}
<span style={{ background: 'linear-gradient(135deg, #F97316, #FB923C)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
Portal
</span>
</h1>
<p className="text-[#475569]" style={{ fontFamily: 'Inter, sans-serif' }}>
Enter your official college Register Number and Password to access your timetable and assigned classroom venue.
</p>
</motion.div>
</div>
</div>
<div className="w-20 h-20 rounded-3xl bg-gradient-to-br from-[#FF6B00] to-[#F97316] p-0.5 shadow-2xl shadow-orange-500/25 mx-auto"> {/* Login Form Container */}
<div className="w-full h-full bg-slate-950/70 rounded-[22px] flex items-center justify-center backdrop-blur-sm"> <div className="container-custom py-12 flex items-center justify-center pb-28">
<GraduationCap className="w-10 h-10 text-white" /> <div className="w-full max-w-md space-y-6">
<div className="text-center space-y-2">
<div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-[#FF6B00] to-[#F97316] flex items-center justify-center shadow-xl shadow-orange-500/30 mx-auto">
<GraduationCap className="w-8 h-8 text-white" />
</div>
<h2 className="text-xl font-extrabold text-[#1E293B]" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
Sign In to Student Portal
</h2>
<p className="text-xs text-[#64748B] max-w-sm mx-auto" style={{ fontFamily: 'Inter, sans-serif' }}>
Enter your official college credentials below
</p>
</div> </div>
</div>
<div className="space-y-2"> {/* Login Form Card */}
<h1 className="text-2xl sm:text-3xl font-extrabold text-white tracking-tight"> <motion.div
Student Dashboard initial={{ opacity: 0, y: 15 }}
</h1> animate={{ opacity: 1, y: 0 }}
<p className="text-xs sm:text-sm text-slate-400 max-w-sm mx-auto leading-relaxed"> className="bg-white border border-[#E5E7EB] rounded-3xl p-6 sm:p-8 shadow-xl space-y-5"
Sign in with your student credentials to view your live daily timetable, assigned classroom, and department info.
</p>
</div>
<motion.div
initial={{ opacity: 0, y: 15 }}
animate={{ opacity: 1, y: 0 }}
className="bg-slate-900/80 backdrop-blur-xl border border-white/10 rounded-3xl p-6 shadow-2xl space-y-4"
>
<button
type="button"
onClick={openAuthModal}
className="w-full py-3.5 px-4 rounded-xl text-white font-bold text-sm bg-gradient-to-r from-[#FF6B00] to-[#F97316] hover:opacity-95 shadow-lg shadow-orange-500/20 transition-all flex items-center justify-center gap-2 cursor-pointer"
> >
<LogIn className="w-4 h-4" /> {loginError && (
<span>Sign In to Access Dashboard</span> <div className="p-3.5 rounded-2xl bg-rose-50 border border-rose-200 flex items-start gap-2.5 text-xs text-rose-700 font-medium">
<ArrowRight className="w-4 h-4" /> <AlertCircle className="w-4 h-4 text-rose-500 shrink-0 mt-0.5" />
</button> <span>{loginError}</span>
</div>
)}
<p className="text-[11px] text-slate-500"> <form onSubmit={handleImsLogin} className="space-y-4">
Works with Register Number (e.g. <code>2114251001</code> / <code>rit@2026</code>) <div>
</p> <label className="block text-xs font-bold text-[#1E293B] mb-1.5 flex items-center gap-1.5" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
</motion.div> <User className="w-3.5 h-3.5 text-[#F97316]" />
Register Number / Roll No
</label>
<input
type="text"
value={regNumberInput}
onChange={(e) => setRegNumberInput(e.target.value)}
placeholder="e.g. 2114251001"
className="w-full bg-[#F8FAFC] border border-[#E5E7EB] rounded-xl px-4 py-3 text-sm text-[#1E293B] placeholder-[#94A3B8] outline-none focus:border-[#F97316] focus:bg-white transition-colors font-mono"
required
/>
</div>
<div>
<label className="block text-xs font-bold text-[#1E293B] mb-1.5 flex items-center gap-1.5" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
<KeyRound className="w-3.5 h-3.5 text-[#F97316]" />
IMS Password
</label>
<input
type="password"
value={passwordInput}
onChange={(e) => setPasswordInput(e.target.value)}
placeholder="••••••••"
className="w-full bg-[#F8FAFC] border border-[#E5E7EB] rounded-xl px-4 py-3 text-sm text-[#1E293B] placeholder-[#94A3B8] outline-none focus:border-[#F97316] focus:bg-white transition-colors font-mono"
required
/>
</div>
<button
type="submit"
disabled={isLoggingIn}
className="w-full py-3.5 px-4 rounded-xl text-white font-bold text-sm bg-gradient-to-r from-[#FF6B00] to-[#F97316] hover:opacity-95 shadow-lg shadow-orange-500/20 transition-all flex items-center justify-center gap-2 cursor-pointer"
style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
>
{isLoggingIn ? (
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
) : (
<>
<span>Sign In to Student Portal</span>
<ArrowRight className="w-4 h-4" />
</>
)}
</button>
</form>
<div className="text-center pt-2">
<button
type="button"
onClick={openAuthModal}
className="text-xs text-slate-500 hover:text-[#F97316] font-semibold underline cursor-pointer"
>
Or Sign In via Main Portal Account
</button>
</div>
{/* Expandable Demo Helper */}
<div className="pt-3 border-t border-[#E5E7EB]">
<button
type="button"
onClick={() => setShowDemoBox(!showDemoBox)}
className="w-full text-center text-xs font-bold text-[#F97316] hover:underline flex items-center justify-center gap-1 cursor-pointer"
style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
>
<Sparkles className="w-3.5 h-3.5" />
{showDemoBox ? 'Hide Demo Accounts' : 'Testing / Demo Accounts'}
</button>
<AnimatePresence>
{showDemoBox && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
className="overflow-hidden mt-3 space-y-2"
>
<p className="text-[11px] text-[#64748B] text-center" style={{ fontFamily: 'Inter, sans-serif' }}>
Click any sample student below to auto-fill the login form:
</p>
<div className="space-y-1.5">
{mockUsers.map((mock) => (
<button
key={mock.regNumber}
type="button"
onClick={() => handleQuickFillMock(mock)}
className="w-full flex items-center justify-between p-2.5 rounded-xl bg-[#F8FAFC] hover:bg-white border border-[#E5E7EB] text-left text-xs transition-colors cursor-pointer"
>
<div>
<p className="font-bold text-[#1E293B]">{mock.studentName}</p>
<p className="text-[10px] text-[#64748B]">Reg: {mock.regNumber} ({mock.department})</p>
</div>
<span className="text-[10px] text-[#F97316] font-mono font-bold">Fill Form</span>
</button>
))}
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</motion.div>
</div>
</div> </div>
</div> </div>
); );
@@ -106,118 +273,113 @@ export default function StudentDashboard() {
const classInchargeMember = data?.facultyList.find((f) => f.isClassIncharge); const classInchargeMember = data?.facultyList.find((f) => f.isClassIncharge);
return ( return (
<div className="min-h-screen bg-[#0a0f1d] text-slate-100 pt-6 pb-24 px-4 sm:px-6 lg:px-8"> <div className="min-h-screen" style={{ backgroundColor: '#FAFAFA' }}>
<div className="max-w-7xl mx-auto space-y-8"> {/* Header */}
<div className="bg-white border-b border-[#E5E7EB] py-8">
{/* ─── Top Banner ─── */} <div className="container-custom">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 bg-slate-900/60 backdrop-blur-xl border border-white/10 rounded-3xl p-6 shadow-2xl relative overflow-hidden"> <div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div className="absolute top-0 right-0 -mr-16 -mt-16 w-64 h-64 bg-orange-500/10 rounded-full blur-3xl pointer-events-none" /> <div className="flex items-start gap-4">
<div className="absolute bottom-0 left-0 -ml-16 -mb-16 w-64 h-64 bg-indigo-500/10 rounded-full blur-3xl pointer-events-none" /> <div className="w-14 h-14 rounded-2xl bg-gradient-to-br from-[#FF6B00] to-[#F97316] flex items-center justify-center shadow-lg shadow-orange-500/30 shrink-0">
<div className="relative z-10 flex items-start gap-4">
<div className="w-14 h-14 rounded-2xl bg-gradient-to-br from-[#FF6B00] to-[#F97316] p-0.5 shadow-lg shadow-orange-500/20 shrink-0">
<div className="w-full h-full bg-slate-950/40 rounded-[14px] flex items-center justify-center backdrop-blur-sm">
<GraduationCap className="w-7 h-7 text-white" /> <GraduationCap className="w-7 h-7 text-white" />
</div> </div>
</div>
<div> <div>
<div className="flex flex-wrap items-center gap-2 mb-1"> <div className="flex flex-wrap items-center gap-2 mb-1">
<h1 className="text-xl sm:text-2xl font-extrabold text-white tracking-tight"> <h1 className="text-xl sm:text-2xl font-extrabold text-[#1E293B] tracking-tight" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
{data?.student.name || user?.name || imsSession?.student?.name} {data?.student.name || user?.name || imsSession?.student.name}
</h1> </h1>
<span className="px-2.5 py-0.5 rounded-full text-[10px] font-bold tracking-wider bg-emerald-500/20 text-emerald-400 border border-emerald-500/30 uppercase"> <span className="px-2.5 py-0.5 rounded-full text-[10px] font-bold tracking-wider bg-emerald-50 text-emerald-600 border border-emerald-200 uppercase">
Authenticated Student Authenticated Student
</span> </span>
</div> </div>
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-slate-400 font-medium"> <div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-[#64748B] font-medium" style={{ fontFamily: 'Inter, sans-serif' }}>
<span>Reg No: <strong className="text-white">{data?.student.regNumber || effectiveRegNumber}</strong></span> <span>Reg No: <strong className="text-[#1E293B]">{data?.student.regNumber || effectiveRegNumber || imsSession?.student.regNumber}</strong></span>
<span>•</span> <span>•</span>
<span>{data?.student.degree || 'B.E.'} {data?.student.department || user?.department || 'Engineering'}</span> <span>{data?.student.degree} {data?.student.department}</span>
{data?.student.year && ( <span>•</span>
<> <span>Year {data?.student.year}, Sem {data?.student.semester} ({data?.student.section})</span>
<span>•</span> </div>
<span>Year {data.student.year}, Sem {data.student.semester} ({data.student.section})</span>
</>
)}
</div> </div>
</div> </div>
</div>
<div className="relative z-10 flex items-center gap-2.5 self-start md:self-auto"> <div className="flex items-center gap-2.5 self-start md:self-auto">
<Link <Link
to={`/faculty?dept=${encodeURIComponent(data?.student.department || '')}`} to={`/faculty?dept=${encodeURIComponent(data?.student.department || '')}`}
className="flex items-center gap-1.5 px-3.5 py-2 rounded-xl bg-orange-500/15 hover:bg-orange-500/25 text-orange-400 border border-orange-500/30 text-xs font-bold transition-all cursor-pointer" className="flex items-center gap-1.5 px-3.5 py-2 rounded-xl bg-orange-50 hover:bg-orange-100 text-[#F97316] border border-orange-200 text-xs font-bold transition-all cursor-pointer"
> >
<span>Faculty Directory</span> <span>Faculty Directory</span>
<ExternalLink className="w-3.5 h-3.5" /> <ExternalLink className="w-3.5 h-3.5" />
</Link> </Link>
<button <button
onClick={handleLogout} onClick={handleLogoutIms}
className="flex items-center gap-1.5 px-4 py-2 rounded-xl bg-slate-800 hover:bg-rose-500/15 text-slate-300 hover:text-rose-400 border border-white/10 hover:border-rose-500/30 text-xs font-semibold transition-all cursor-pointer" className="flex items-center gap-1.5 px-4 py-2 rounded-xl bg-white hover:bg-rose-50 text-[#64748B] hover:text-rose-600 border border-[#E5E7EB] hover:border-rose-200 text-xs font-semibold transition-all cursor-pointer"
> style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
<LogOut className="w-4 h-4" /> >
Sign Out <LogOut className="w-4 h-4" />
</button> Sign Out
</button>
</div>
</div> </div>
</div> </div>
</div>
<div className="container-custom py-8 pb-32 space-y-8">
{/* ─── Key Stats Quick Bar ─── */} {/* ─── Key Stats Quick Bar ─── */}
{data && ( {data && (
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4"> <div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
<div className="bg-slate-900/40 border border-white/10 rounded-2xl p-4 flex items-center gap-3.5"> <div className="bg-white border border-[#E5E7EB] rounded-2xl p-4 flex items-center gap-3.5 shadow-sm">
<div className="w-10 h-10 rounded-xl bg-orange-500/10 border border-orange-500/20 flex items-center justify-center text-orange-400 shrink-0"> <div className="w-10 h-10 rounded-xl bg-orange-50 border border-orange-200 flex items-center justify-center text-[#F97316] shrink-0">
<Building className="w-5 h-5" /> <Building className="w-5 h-5" />
</div> </div>
<div className="min-w-0"> <div className="min-w-0">
<p className="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">Assigned Room</p> <p className="text-[11px] font-bold text-[#94A3B8] uppercase tracking-wider">Assigned Room</p>
<p className="text-sm sm:text-base font-bold text-white truncate">{data.classLocation.roomNumber}</p> <p className="text-sm sm:text-base font-extrabold text-[#1E293B] truncate" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>{data.classLocation.roomNumber}</p>
<p className="text-[10px] text-slate-400 truncate">{data.classLocation.floor}</p> <p className="text-[10px] text-[#64748B] truncate">{data.classLocation.floor}</p>
</div> </div>
</div> </div>
<div className="bg-slate-900/40 border border-white/10 rounded-2xl p-4 flex items-center gap-3.5"> <div className="bg-white border border-[#E5E7EB] rounded-2xl p-4 flex items-center gap-3.5 shadow-sm">
<div className="w-10 h-10 rounded-xl bg-purple-500/10 border border-purple-500/20 flex items-center justify-center text-purple-400 shrink-0"> <div className="w-10 h-10 rounded-xl bg-purple-50 border border-purple-200 flex items-center justify-center text-purple-600 shrink-0">
<Building className="w-5 h-5" /> <Users className="w-5 h-5" />
</div> </div>
<div className="min-w-0"> <div className="min-w-0">
<p className="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">Class Incharge</p> <p className="text-[11px] font-bold text-[#94A3B8] uppercase tracking-wider">Class Incharge</p>
<p className="text-sm sm:text-base font-bold text-white truncate"> <p className="text-sm sm:text-base font-extrabold text-[#1E293B] truncate" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
{classInchargeMember?.facultyName.split(' ')[0] || 'Assigned'} {classInchargeMember?.facultyName.split(' ')[0] || 'Assigned'}
</p> </p>
<p className="text-[10px] text-slate-400 truncate"> <p className="text-[10px] text-[#64748B] truncate">
{classInchargeMember?.officeLocation || 'Staff Cabin'} {classInchargeMember?.officeLocation || 'Staff Cabin'}
</p> </p>
</div> </div>
</div> </div>
<div className="bg-slate-900/40 border border-white/10 rounded-2xl p-4 flex items-center gap-3.5"> <div className="bg-white border border-[#E5E7EB] rounded-2xl p-4 flex items-center gap-3.5 shadow-sm">
<div className="w-10 h-10 rounded-xl bg-blue-500/10 border border-blue-500/20 flex items-center justify-center text-blue-400 shrink-0"> <div className="w-10 h-10 rounded-xl bg-blue-50 border border-blue-200 flex items-center justify-center text-blue-600 shrink-0">
<GraduationCap className="w-5 h-5" /> <GraduationCap className="w-5 h-5" />
</div> </div>
<div className="min-w-0"> <div className="min-w-0">
<p className="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">Branch & Section</p> <p className="text-[11px] font-bold text-[#94A3B8] uppercase tracking-wider">Branch & Section</p>
<p className="text-sm sm:text-base font-bold text-white truncate">{data.student.departmentCode} ({data.student.section})</p> <p className="text-sm sm:text-base font-extrabold text-[#1E293B] truncate" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>{data.student.departmentCode} ({data.student.section})</p>
<p className="text-[10px] text-slate-400 truncate">Semester {data.student.semester}</p> <p className="text-[10px] text-[#64748B] truncate">Semester {data.student.semester}</p>
</div> </div>
</div> </div>
<div className="bg-slate-900/40 border border-white/10 rounded-2xl p-4 flex items-center gap-3.5"> <div className="bg-white border border-[#E5E7EB] rounded-2xl p-4 flex items-center gap-3.5 shadow-sm">
<div className="w-10 h-10 rounded-xl bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center text-emerald-400 shrink-0"> <div className="w-10 h-10 rounded-xl bg-emerald-50 border border-emerald-200 flex items-center justify-center text-emerald-600 shrink-0">
<Calendar className="w-5 h-5" /> <Calendar className="w-5 h-5" />
</div> </div>
<div className="min-w-0"> <div className="min-w-0">
<p className="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">Academic Batch</p> <p className="text-[11px] font-bold text-[#94A3B8] uppercase tracking-wider">Academic Batch</p>
<p className="text-sm sm:text-base font-bold text-white truncate">{data.student.batch}</p> <p className="text-sm sm:text-base font-extrabold text-[#1E293B] truncate" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>{data.student.batch}</p>
<p className="text-[10px] text-slate-400 truncate">{data.student.regulation}</p> <p className="text-[10px] text-[#64748B] truncate">{data.student.regulation}</p>
</div> </div>
</div> </div>
</div> </div>
)} )}
{/* ─── Navigation Tabs ─── */} {/* ─── Navigation Tabs ─── */}
<div className="flex items-center justify-between border-b border-white/10 pb-4 overflow-x-auto gap-4"> <div className="flex items-center justify-between border-b border-[#E5E7EB] pb-4 overflow-x-auto gap-4">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{[ {[
{ id: 'timetable', label: "Today's Timetable", icon: Clock }, { id: 'timetable', label: "Today's Timetable", icon: Clock },
@@ -232,40 +394,31 @@ export default function StudentDashboard() {
className={`flex items-center gap-2 px-4 py-2.5 rounded-xl text-xs sm:text-sm font-bold transition-all cursor-pointer whitespace-nowrap ${ className={`flex items-center gap-2 px-4 py-2.5 rounded-xl text-xs sm:text-sm font-bold transition-all cursor-pointer whitespace-nowrap ${
isActive isActive
? 'bg-gradient-to-r from-[#FF6B00] to-[#F97316] text-white shadow-md shadow-orange-500/20' ? 'bg-gradient-to-r from-[#FF6B00] to-[#F97316] text-white shadow-md shadow-orange-500/20'
: 'bg-slate-900/60 hover:bg-slate-800 text-slate-300 hover:text-white border border-white/10' : 'bg-white text-[#64748B] hover:text-[#1E293B] border border-[#E5E7EB] hover:bg-slate-50'
}`} }`}
style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
> >
<Icon className="w-4 h-4" /> <Icon className={`w-4 h-4 ${isActive ? 'text-white' : 'text-[#94A3B8]'}`} />
{tab.label} <span>{tab.label}</span>
</button> </button>
); );
})} })}
</div> </div>
{data && (
<Link
to={`/faculty?dept=${encodeURIComponent(data.student.department)}`}
className="flex items-center gap-1.5 px-4 py-2 rounded-xl bg-slate-900 hover:bg-slate-800 border border-white/10 text-xs font-bold text-slate-200 hover:text-white transition-all cursor-pointer whitespace-nowrap shrink-0"
>
<span>View {data.student.departmentCode} Faculty Directory</span>
<ArrowRight className="w-3.5 h-3.5 text-slate-400" />
</Link>
)}
</div> </div>
{/* ─── Main Content Tabs ─── */} {/* ─── Main Content Tabs ─── */}
{isLoading ? ( {isLoading ? (
<div className="flex flex-col items-center justify-center py-20 gap-3"> <div className="flex flex-col items-center justify-center py-20 gap-3">
<div className="w-8 h-8 border-3 border-orange-500 border-t-transparent rounded-full animate-spin" /> <div className="w-8 h-8 border-3 border-[#F97316] border-t-transparent rounded-full animate-spin" />
<p className="text-xs text-slate-400 font-medium">Connecting to IMS services...</p> <p className="text-xs text-[#64748B] font-medium">Connecting to IMS services...</p>
</div> </div>
) : error ? ( ) : error ? (
<div className="bg-rose-500/10 border border-rose-500/30 rounded-3xl p-8 text-center space-y-3"> <div className="bg-rose-50 border border-rose-200 rounded-3xl p-8 text-center space-y-3">
<p className="text-rose-400 font-bold text-base">Error connecting to IMS Gateway</p> <p className="text-rose-700 font-bold text-base">Error connecting to IMS Gateway</p>
<p className="text-slate-400 text-xs max-w-md mx-auto">{error}</p> <p className="text-[#64748B] text-xs max-w-md mx-auto">{error}</p>
<button <button
onClick={() => loadDashboard()} onClick={() => loadDashboard()}
className="px-4 py-2 bg-rose-500 text-white rounded-xl text-xs font-bold hover:bg-rose-600 transition-colors cursor-pointer" className="px-4 py-2 bg-rose-600 text-white rounded-xl text-xs font-bold hover:bg-rose-700 transition-colors cursor-pointer"
> >
Retry Connection Retry Connection
</button> </button>
@@ -275,16 +428,16 @@ export default function StudentDashboard() {
{/* ─── 1. TIMETABLE TAB ─── */} {/* ─── 1. TIMETABLE TAB ─── */}
{activeTab === 'timetable' && ( {activeTab === 'timetable' && (
<div className="space-y-6"> <div className="space-y-6">
<div className="flex flex-wrap items-center justify-between gap-3 bg-slate-900/40 p-4 rounded-2xl border border-white/10"> <div className="flex flex-wrap items-center justify-between gap-3 bg-white p-4 rounded-2xl border border-[#E5E7EB] shadow-sm">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Calendar className="w-4 h-4 text-orange-400" /> <Calendar className="w-4 h-4 text-[#F97316]" />
<span className="text-sm font-bold text-white tracking-wide"> <span className="text-sm font-bold text-[#1E293B] tracking-wide" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
{data.todaySchedule.dayOfWeek} SCHEDULE ({data.todaySchedule.date}) {data.todaySchedule.dayOfWeek} SCHEDULE ({data.todaySchedule.date})
</span> </span>
</div> </div>
<div className="flex items-center gap-3 text-xs"> <div className="flex items-center gap-3 text-xs">
<span className="flex items-center gap-1 text-emerald-400 font-semibold"> <span className="flex items-center gap-1 text-emerald-600 font-bold">
<span className="w-2 h-2 rounded-full bg-emerald-400 animate-pulse" /> <span className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" />
Live Status Active Live Status Active
</span> </span>
</div> </div>
@@ -302,20 +455,20 @@ export default function StudentDashboard() {
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
className={`relative rounded-2xl p-5 border transition-all ${ className={`relative rounded-2xl p-5 border transition-all ${
isOngoing isOngoing
? 'bg-gradient-to-b from-orange-500/15 to-slate-900 border-orange-500/50 shadow-xl shadow-orange-500/10 ring-1 ring-orange-500/40' ? 'bg-gradient-to-b from-orange-500/10 via-white to-white border-[#F97316] shadow-md ring-1 ring-orange-400/30'
: isCompleted : isCompleted
? 'bg-slate-950/40 border-white/5 opacity-60' ? 'bg-slate-50/80 border-[#E5E7EB] opacity-60'
: 'bg-slate-900/50 border-white/10 hover:border-white/20' : 'bg-white border-[#E5E7EB] hover:border-slate-300 shadow-sm'
}`} }`}
> >
{/* Header Badge */} {/* Header Badge */}
<div className="flex items-center justify-between gap-2 mb-3"> <div className="flex items-center justify-between gap-2 mb-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="w-6 h-6 rounded-lg bg-white/10 text-white font-bold text-xs flex items-center justify-center"> <span className="w-6 h-6 rounded-lg bg-slate-100 text-[#1E293B] font-bold text-xs flex items-center justify-center">
P{period.periodNumber} P{period.periodNumber}
</span> </span>
<span className="text-xs font-semibold text-slate-300 flex items-center gap-1"> <span className="text-xs font-semibold text-[#64748B] flex items-center gap-1">
<Clock className="w-3 h-3 text-slate-400" /> <Clock className="w-3 h-3 text-[#94A3B8]" />
{period.timeSlot} {period.timeSlot}
</span> </span>
</div> </div>
@@ -323,10 +476,10 @@ export default function StudentDashboard() {
<span <span
className={`px-2.5 py-0.5 rounded-full text-[10px] font-extrabold uppercase tracking-wider ${ className={`px-2.5 py-0.5 rounded-full text-[10px] font-extrabold uppercase tracking-wider ${
isOngoing isOngoing
? 'bg-orange-500 text-white animate-pulse' ? 'bg-[#F97316] text-white animate-pulse'
: isCompleted : isCompleted
? 'bg-slate-800 text-slate-400' ? 'bg-slate-200 text-[#64748B]'
: 'bg-blue-500/20 text-blue-400 border border-blue-500/30' : 'bg-blue-50 text-blue-600 border border-blue-200'
}`} }`}
> >
{period.status} {period.status}
@@ -336,23 +489,23 @@ export default function StudentDashboard() {
{/* Subject Details */} {/* Subject Details */}
<div className="space-y-1.5 mb-4"> <div className="space-y-1.5 mb-4">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-[11px] font-mono font-bold text-orange-400"> <span className="text-[11px] font-mono font-bold text-[#F97316]">
{period.subjectCode} {period.subjectCode}
</span> </span>
<span className="px-1.5 py-0.2 rounded text-[9px] font-bold bg-white/10 text-slate-300 uppercase"> <span className="px-1.5 py-0.2 rounded text-[9px] font-bold bg-slate-100 text-[#475569] uppercase">
{period.type} {period.type}
</span> </span>
</div> </div>
<h3 className="text-base font-bold text-white line-clamp-1"> <h3 className="text-base font-extrabold text-[#1E293B] line-clamp-1" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
{period.subjectName} {period.subjectName}
</h3> </h3>
</div> </div>
{/* Footer / Instructor & Venue */} {/* Footer / Instructor & Venue */}
<div className="pt-3 border-t border-white/10 flex items-center justify-between text-xs text-slate-300"> <div className="pt-3 border-t border-[#E5E7EB] flex items-center justify-between text-xs text-[#475569]">
<span className="truncate pr-2">{period.facultyName}</span> <span className="truncate pr-2 font-medium">{period.facultyName}</span>
<span className="shrink-0 font-bold text-white px-2 py-0.5 rounded-lg bg-white/10 flex items-center gap-1"> <span className="shrink-0 font-bold text-[#1E293B] px-2 py-0.5 rounded-lg bg-slate-100 flex items-center gap-1">
<MapPin className="w-3 h-3 text-orange-400" /> <MapPin className="w-3 h-3 text-[#F97316]" />
{period.venue} {period.venue}
</span> </span>
</div> </div>
@@ -366,78 +519,78 @@ export default function StudentDashboard() {
{/* ─── 2. CLASS LOCATION TAB ─── */} {/* ─── 2. CLASS LOCATION TAB ─── */}
{activeTab === 'location' && ( {activeTab === 'location' && (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div className="bg-slate-900/60 border border-white/10 rounded-3xl p-6 space-y-6"> <div className="bg-white border border-[#E5E7EB] rounded-3xl p-6 space-y-6 shadow-sm">
<div> <div>
<h3 className="text-lg font-bold text-white flex items-center gap-2 mb-1"> <h3 className="text-lg font-extrabold text-[#1E293B] flex items-center gap-2 mb-1" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
<Building className="w-5 h-5 text-orange-400" /> <Building className="w-5 h-5 text-[#F97316]" />
Classroom & Section Venue Classroom & Section Venue
</h3> </h3>
<p className="text-xs text-slate-400"> <p className="text-xs text-[#64748B]">
Official lecture hall allocation from Academic Registrar. Official lecture hall allocation from Academic Registrar.
</p> </p>
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
<div className="p-4 rounded-2xl bg-white/5 border border-white/10 flex items-center justify-between"> <div className="p-4 rounded-2xl bg-slate-50 border border-[#E5E7EB] flex items-center justify-between">
<div> <div>
<p className="text-xs text-slate-400 uppercase font-semibold">Room Number</p> <p className="text-xs text-[#64748B] uppercase font-semibold">Room Number</p>
<p className="text-2xl font-black text-white">{data.classLocation.roomNumber}</p> <p className="text-2xl font-black text-[#1E293B]" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>{data.classLocation.roomNumber}</p>
</div> </div>
<span className="px-3 py-1 bg-orange-500/20 text-orange-400 border border-orange-500/30 rounded-xl text-xs font-bold"> <span className="px-3 py-1 bg-orange-50 text-[#F97316] border border-orange-200 rounded-xl text-xs font-bold">
{data.student.section} Section {data.student.section} Section
</span> </span>
</div> </div>
<div className="space-y-3 text-xs"> <div className="space-y-3 text-xs">
<div className="flex justify-between py-2 border-b border-white/10"> <div className="flex justify-between py-2 border-b border-[#E5E7EB]">
<span className="text-slate-400">Building Block:</span> <span className="text-[#64748B]">Building Block:</span>
<span className="font-bold text-white text-right">{data.classLocation.buildingName}</span> <span className="font-bold text-[#1E293B] text-right">{data.classLocation.buildingName}</span>
</div> </div>
<div className="flex justify-between py-2 border-b border-white/10"> <div className="flex justify-between py-2 border-b border-[#E5E7EB]">
<span className="text-slate-400">Floor & Wing:</span> <span className="text-[#64748B]">Floor & Wing:</span>
<span className="font-bold text-white">{data.classLocation.floor}, {data.classLocation.wing}</span> <span className="font-bold text-[#1E293B]">{data.classLocation.floor}, {data.classLocation.wing}</span>
</div> </div>
<div className="flex justify-between py-2"> <div className="flex justify-between py-2">
<span className="text-slate-400">Landmark Directions:</span> <span className="text-[#64748B]">Landmark Directions:</span>
<span className="font-bold text-orange-400 text-right max-w-xs">{data.classLocation.landmark}</span> <span className="font-bold text-[#F97316] text-right max-w-xs">{data.classLocation.landmark}</span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="bg-slate-900/60 border border-white/10 rounded-3xl p-6 flex flex-col justify-between space-y-6"> <div className="bg-white border border-[#E5E7EB] rounded-3xl p-6 flex flex-col justify-between space-y-6 shadow-sm">
<div className="space-y-3"> <div className="space-y-3">
<h3 className="text-lg font-bold text-white flex items-center gap-2"> <h3 className="text-lg font-extrabold text-[#1E293B] flex items-center gap-2" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
<Compass className="w-5 h-5 text-orange-400" /> <Compass className="w-5 h-5 text-[#F97316]" />
Academic Department Details Academic Department Details
</h3> </h3>
<p className="text-xs text-slate-400 leading-relaxed"> <p className="text-xs text-[#64748B] leading-relaxed">
Official department profile, curriculum regulation, and academic guidelines for batch {data.student.batch}. Official department profile, curriculum regulation, and academic guidelines for batch {data.student.batch}.
</p> </p>
<div className="grid grid-cols-2 gap-3 pt-3"> <div className="grid grid-cols-2 gap-3 pt-3">
<div className="p-3 bg-white/5 rounded-xl border border-white/5"> <div className="p-3 bg-slate-50 rounded-xl border border-[#E5E7EB]">
<p className="text-[10px] uppercase font-bold text-slate-400">Degree & Branch</p> <p className="text-[10px] uppercase font-bold text-[#94A3B8]">Degree & Branch</p>
<p className="text-xs font-bold text-white mt-0.5">{data.student.degree} {data.student.departmentCode}</p> <p className="text-xs font-bold text-[#1E293B] mt-0.5">{data.student.degree} {data.student.departmentCode}</p>
</div> </div>
<div className="p-3 bg-white/5 rounded-xl border border-white/5"> <div className="p-3 bg-slate-50 rounded-xl border border-[#E5E7EB]">
<p className="text-[10px] uppercase font-bold text-slate-400">Regulation</p> <p className="text-[10px] uppercase font-bold text-[#94A3B8]">Regulation</p>
<p className="text-xs font-bold text-white mt-0.5">{data.student.regulation}</p> <p className="text-xs font-bold text-[#1E293B] mt-0.5">{data.student.regulation}</p>
</div> </div>
<div className="p-3 bg-white/5 rounded-xl border border-white/5"> <div className="p-3 bg-slate-50 rounded-xl border border-[#E5E7EB]">
<p className="text-[10px] uppercase font-bold text-slate-400">Academic Year</p> <p className="text-[10px] uppercase font-bold text-[#94A3B8]">Academic Year</p>
<p className="text-xs font-bold text-white mt-0.5">Year {data.student.year} (Sem {data.student.semester})</p> <p className="text-xs font-bold text-[#1E293B] mt-0.5">Year {data.student.year} (Sem {data.student.semester})</p>
</div> </div>
<div className="p-3 bg-white/5 rounded-xl border border-white/5"> <div className="p-3 bg-slate-50 rounded-xl border border-[#E5E7EB]">
<p className="text-[10px] uppercase font-bold text-slate-400">Batch Cohort</p> <p className="text-[10px] uppercase font-bold text-[#94A3B8]">Batch Cohort</p>
<p className="text-xs font-bold text-white mt-0.5">{data.student.batch}</p> <p className="text-xs font-bold text-[#1E293B] mt-0.5">{data.student.batch}</p>
</div> </div>
</div> </div>
</div> </div>
<div className="p-4 rounded-2xl bg-gradient-to-r from-orange-500/10 to-indigo-500/10 border border-white/10 flex items-center gap-3"> <div className="p-4 rounded-2xl bg-orange-50 border border-orange-200 flex items-center gap-3">
<HelpCircle className="w-5 h-5 text-orange-400 shrink-0" /> <HelpCircle className="w-5 h-5 text-[#F97316] shrink-0" />
<p className="text-xs text-slate-300"> <p className="text-xs text-[#475569]">
Need classroom adjustment? Contact your Class Incharge or Head of Department. Need classroom adjustment? Contact your Class Incharge or Head of Department.
</p> </p>
</div> </div>

View File

@@ -13,6 +13,7 @@ import {
import SectionTitle from '@/components/SectionTitle/SectionTitle'; import SectionTitle from '@/components/SectionTitle/SectionTitle';
import { StaggerContainer, StaggerItem } from '@/components/AnimatedContainer/AnimatedContainer'; import { StaggerContainer, StaggerItem } from '@/components/AnimatedContainer/AnimatedContainer';
import { TOOLKIT_ITEMS, DEPARTMENTS } from '@/constants'; import { TOOLKIT_ITEMS, DEPARTMENTS } from '@/constants';
import { DEPARTMENT_CURRICULUM, DEPARTMENT_CODE_MAP, PROFESSIONAL_ELECTIVES_LIST } from '@/constants/departmentCurriculum';
const TOOLKIT_ICONS: Record<string, { icon: React.ComponentType<{ className?: string }>; bg: string; color: string }> = { const TOOLKIT_ICONS: Record<string, { icon: React.ComponentType<{ className?: string }>; bg: string; color: string }> = {
'CS tools': { icon: Code, bg: '#EFF6FF', color: '#3B82F6' }, 'CS tools': { icon: Code, bg: '#EFF6FF', color: '#3B82F6' },
@@ -655,105 +656,7 @@ const BIOTECH_TOOLS_CATEGORIES = [
} }
]; ];
const PROFESSIONAL_ELECTIVES_LIST: Array<{ code: string; name: string; credits: number }> = [
{ code: 'AD23V11', name: 'Agriculture Data Analytics', credits: 3 },
{ code: 'AD23V12', name: 'Big Data Analytics', credits: 3 },
{ code: 'AD23V13', name: 'Data warehousing and Data Mining', credits: 3 },
{ code: 'AD23V14', name: 'Health Care Analytics', credits: 3 },
{ code: 'AD23V15', name: 'Image and Video Analytics', credits: 3 },
{ code: 'AD23V16', name: 'Web and Social Media Analytics', credits: 3 },
{ code: 'AD23V17', name: 'Predictive Analytics', credits: 3 },
{ code: 'AD23V18', name: 'Sentiment Analysis', credits: 3 },
{ code: 'AD23V21', name: 'AI in IoT', credits: 3 },
{ code: 'AD23V22', name: 'Applied Machine Learning', credits: 3 },
{ code: 'AD23V23', name: 'Cognitive Computing', credits: 3 },
{ code: 'AD23V24', name: 'Computer Vision', credits: 3 },
{ code: 'AD23V25', name: 'Ethics For AI', credits: 3 },
{ code: 'AD23V26', name: 'Game Theory', credits: 3 },
{ code: 'AD23V27', name: 'Quantum Computing Techniques', credits: 3 },
{ code: 'AD23V28', name: 'Responsive AI', credits: 3 },
];
const AIDS_CURRICULUM: Record<number, Array<{ code: string; name: string; credits: number; isElective?: boolean; electiveSlot?: number }>> = {
1: [
{ code: 'HS23111', name: 'Communicative English', credits: 3 },
{ code: 'MA23111', name: 'Matrices and Calculus', credits: 4 },
{ code: 'PH23111', name: 'Physics for Information Science', credits: 3 },
{ code: 'GE23111', name: 'Problem Solving and C Programming', credits: 3 },
{ code: 'GE23113', name: 'Basic Electrical and Electronics Engineering', credits: 3 },
{ code: 'GE23112', name: 'Heritage of Tamils', credits: 0 },
{ code: 'PH23121', name: 'Physics Laboratory', credits: 1 },
{ code: 'GE23121', name: 'Problem Solving and C Programming Laboratory', credits: 1 },
{ code: 'GE23122', name: 'Engineering Practices Laboratory', credits: 1 },
],
2: [
{ code: 'HS23211', name: 'Professional English', credits: 2 },
{ code: 'CY23211', name: 'Engineering Chemistry', credits: 3 },
{ code: 'MA23211', name: 'Statistics and Numerical Methods', credits: 4 },
{ code: 'AD23211', name: 'Python for Data Science', credits: 3 },
{ code: 'GE23213', name: 'Tamils and Technology', credits: 0 },
{ code: 'GE23231', name: 'Engineering Graphics', credits: 4 },
{ code: 'AD23231', name: 'Data Structures Design', credits: 4 },
{ code: 'CY23221', name: 'Chemistry Laboratory', credits: 1 },
{ code: 'AD23221', name: 'Python for Data Science Laboratory', credits: 1 },
{ code: 'GE23221', name: 'Communication Laboratory / Foreign Language', credits: 1 },
],
3: [
{ code: 'MA23311', name: 'Discrete Mathematics', credits: 4 },
{ code: 'AL23311', name: 'Artificial Intelligence', credits: 3 },
{ code: 'CS23312', name: 'Object Oriented Programming', credits: 3 },
{ code: 'CS23411', name: 'Database Management Systems', credits: 3 },
{ code: 'EC23331', name: 'Digital Principles and Computer Organization', credits: 4 },
{ code: 'AL23321', name: 'Artificial Intelligence Laboratory', credits: 1 },
{ code: 'CS23421', name: 'Database Management Systems Laboratory', credits: 1 },
{ code: 'CS23322', name: 'Object Oriented Programming Laboratory', credits: 1 },
{ code: 'AD23IC1', name: 'Data Wrangling', credits: 1 },
],
4: [
{ code: 'GE23411', name: 'Environmental Science and Sustainability', credits: 2 },
{ code: 'MA23411', name: 'Probability and Statistics', credits: 4 },
{ code: 'AD23411', name: 'Data Analytics', credits: 3 },
{ code: 'AL23411', name: 'Machine Learning', credits: 3 },
{ code: 'CS23412', name: 'Operating Systems', credits: 3 },
{ code: 'CS23431', name: 'Design and Analysis of Algorithms', credits: 4 },
{ code: 'AD23421', name: 'Data Analytics Laboratory', credits: 1 },
{ code: 'AL23421', name: 'Machine Learning Laboratory', credits: 1 },
{ code: 'CS23422', name: 'Operating Systems Laboratory', credits: 1 },
{ code: 'AD23IC2', name: 'Introduction to AZURE Machine Learning', credits: 1 },
],
5: [
{ code: 'AD23511', name: 'Deep Learning', credits: 3 },
{ code: 'CS23511', name: 'Computer Networks', credits: 3 },
{ code: 'PEC01', name: 'Professional Elective I', credits: 3, isElective: true, electiveSlot: 1 },
{ code: 'PEC02', name: 'Professional Elective II', credits: 3, isElective: true, electiveSlot: 2 },
{ code: 'CB23531', name: 'Business Analytics', credits: 4 },
{ code: 'AD23532', name: 'Data Exploration and Visualization', credits: 4 },
{ code: 'AD23521', name: 'Deep Learning Laboratory', credits: 1 },
{ code: 'CS23521', name: 'Computer Networks Laboratory', credits: 1 },
{ code: 'AD23IC3', name: 'Tableau - Data Visualization', credits: 1 },
],
6: [
{ code: 'PEC03', name: 'Professional Elective III', credits: 3, isElective: true, electiveSlot: 3 },
{ code: 'PEC04', name: 'Professional Elective IV', credits: 3, isElective: true, electiveSlot: 4 },
{ code: 'OEC01', name: 'Open Elective I', credits: 3 },
{ code: 'OEC02', name: 'Open Elective II', credits: 3 },
{ code: 'EC23631', name: 'Embedded Systems and IoT', credits: 4 },
{ code: 'CS23631', name: 'Object Oriented Software Engineering', credits: 4 },
{ code: 'AD23621', name: 'Mini Project', credits: 2 },
],
7: [
{ code: 'GE23711', name: 'Human Values and Ethics', credits: 2 },
{ code: 'MGT01', name: 'Elective - Management', credits: 3 },
{ code: 'CB23511', name: 'Data and Information Security', credits: 3 },
{ code: 'PEC05', name: 'Professional Elective V', credits: 3, isElective: true, electiveSlot: 5 },
{ code: 'PEC06', name: 'Professional Elective VI', credits: 3, isElective: true, electiveSlot: 6 },
{ code: 'AD23721', name: 'Internship / Certification Course', credits: 2 },
],
8: [
{ code: 'OEC03', name: 'Open Elective III', credits: 3 },
{ code: 'AD23821', name: 'Project Work', credits: 10 },
],
};
export default function Toolkit() { export default function Toolkit() {
const [searchParams, setSearchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
@@ -792,17 +695,20 @@ export default function Toolkit() {
setCalculationType(type); setCalculationType(type);
const semNum = parseInt(gpaSemester.replace(/\D/g, ''), 10) || 1; const semNum = parseInt(gpaSemester.replace(/\D/g, ''), 10) || 1;
const isAIDS = gpaDepartment.toLowerCase().includes('artificial intelligence') || gpaDepartment.toLowerCase().includes('data science'); const deptKey = DEPARTMENT_CODE_MAP[gpaDepartment] || '';
const rawCurriculum = DEPARTMENT_CURRICULUM[deptKey]?.[semNum];
if (isAIDS && AIDS_CURRICULUM[semNum]) { if (rawCurriculum && rawCurriculum.length > 0) {
const loaded = AIDS_CURRICULUM[semNum] let electiveCounter = 1;
const loaded = rawCurriculum
.filter((s) => type === 'internal' || s.credits > 0) .filter((s) => type === 'internal' || s.credits > 0)
.map((s) => { .map((s) => {
if (s.isElective) { const isElective = !!s.isElective;
const defaultCode = s.code === 'PEC01' ? 'AD23V11' : s.code === 'PEC02' ? 'AD23V12' : s.code === 'PEC03' ? 'AD23V13' : s.code === 'PEC04' ? 'AD23V14' : s.code === 'PEC05' ? 'AD23V15' : 'AD23V16'; if (isElective) {
const defaultObj = PROFESSIONAL_ELECTIVES_LIST.find((e) => e.code === defaultCode); const slot = s.electiveSlot || electiveCounter++;
const defaultElectiveObj = PROFESSIONAL_ELECTIVES_LIST[(slot - 1) % PROFESSIONAL_ELECTIVES_LIST.length] || PROFESSIONAL_ELECTIVES_LIST[0];
return { return {
name: defaultObj ? `${defaultObj.code} - ${defaultObj.name}` : `${s.code} - ${s.name}`, name: `${defaultElectiveObj.code} - ${defaultElectiveObj.name}`,
credits: s.credits, credits: s.credits,
grade: 'O', grade: 'O',
cat1: '45', cat1: '45',
@@ -810,12 +716,12 @@ export default function Toolkit() {
assignment: '9', assignment: '9',
attendance: '95', attendance: '95',
isElective: true, isElective: true,
electiveSlot: s.electiveSlot, electiveSlot: slot,
electiveCode: defaultCode, electiveCode: defaultElectiveObj.code,
}; };
} }
return { return {
name: `${s.code} - ${s.name}`, name: s.name,
credits: s.credits, credits: s.credits,
grade: 'O', grade: 'O',
cat1: '45', cat1: '45',
@@ -1498,6 +1404,65 @@ export default function Toolkit() {
<SectionTitle tag="Useful Downloads" title="Student" highlight="Toolkit" align="left" /> <SectionTitle tag="Useful Downloads" title="Student" highlight="Toolkit" align="left" />
<StaggerContainer className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-20"> <StaggerContainer className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-20">
{TOOLKIT_ITEMS.map((item) => { {TOOLKIT_ITEMS.map((item) => {
if (item.title === 'GPA and internal calculator') {
return (
<StaggerItem key={item.id} className="col-span-1 md:col-span-2 flex">
<motion.div
onClick={() => setSelectedToolkit('gpa')}
whileHover={{ y: -4, boxShadow: '0 20px 40px -12px rgba(239,68,68,0.22)' }}
className="relative w-full p-7 md:p-8 bg-gradient-to-r from-[#FFF7ED] via-[#FEF2F2] to-[#FFF1F2] rounded-[26px] border-2 border-[#FECACA] hover:border-[#FCA5A5] cursor-pointer group transition-all shadow-md hover:shadow-xl overflow-hidden flex flex-col md:flex-row items-start md:items-center justify-between gap-6"
>
{/* Top Gradient Bar */}
<div className="absolute top-0 left-0 right-0 h-1.5 bg-gradient-to-r from-[#FF6B00] via-[#EF4444] to-[#F97316]" />
<div className="flex items-start md:items-center gap-5 flex-1 min-w-0 z-10">
<div className="w-16 h-16 md:w-18 md:h-18 rounded-2xl bg-gradient-to-tr from-[#EF4444] via-[#F97316] to-[#FF6B00] text-white flex items-center justify-center shrink-0 font-bold shadow-lg shadow-rose-500/25 group-hover:scale-105 transition-transform">
<Calculator className="w-8 h-8 md:w-9 md:h-9" />
</div>
<div className="flex-1 min-w-0">
<div className="flex flex-wrap items-center gap-2.5 mb-1.5">
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-extrabold bg-gradient-to-r from-[#EF4444] to-[#F97316] text-white shadow-xs tracking-wide uppercase">
<Sparkles className="w-3.5 h-3.5 animate-pulse" /> POPULAR UTILITY
</span>
<span className="inline-flex items-center gap-1 px-2.5 py-0.5 rounded-full text-xs font-semibold bg-white/90 text-[#991B1B] border border-red-200 shadow-2xs">
RIT Regulations
</span>
</div>
<h3 className="text-xl md:text-2xl font-extrabold text-[#1E293B] group-hover:text-[#EF4444] transition-colors mb-1.5" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
GPA, CGPA & Internal Marks Calculator
</h3>
<p className="text-sm text-[#475569] leading-relaxed max-w-2xl" style={{ fontFamily: 'Inter, sans-serif' }}>
Calculate your semester GPA, overall CGPA, and internal assessment marks based on official department course regulations and grade points.
</p>
<div className="flex flex-wrap items-center gap-3 mt-3 text-xs font-medium text-[#64748B]">
<span className="flex items-center gap-1.5 text-rose-700 font-semibold bg-rose-50 px-2.5 py-1 rounded-lg border border-rose-200/80">
<CheckCircle2 className="w-3.5 h-3.5 text-rose-500" /> Semester GPA
</span>
<span className="flex items-center gap-1.5 text-orange-700 font-semibold bg-orange-50 px-2.5 py-1 rounded-lg border border-orange-200/80">
<CheckCircle2 className="w-3.5 h-3.5 text-orange-500" /> Cumulative CGPA
</span>
<span className="flex items-center gap-1.5 text-amber-700 font-semibold bg-amber-50 px-2.5 py-1 rounded-lg border border-amber-200/80">
<CheckCircle2 className="w-3.5 h-3.5 text-amber-500" /> Internal Score
</span>
</div>
</div>
</div>
<div className="flex items-center gap-2 z-10 self-end md:self-center shrink-0">
<button className="px-6 py-3 bg-gradient-to-r from-[#EF4444] via-[#F97316] to-[#FF6B00] hover:from-[#DC2626] hover:to-[#EA580C] text-white font-bold text-sm rounded-xl shadow-md group-hover:shadow-lg group-hover:scale-105 transition-all flex items-center gap-2">
Calculate Now
<ChevronRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
</button>
</div>
</motion.div>
</StaggerItem>
);
}
const iconConfig = TOOLKIT_ICONS[item.title] || { icon: Code, bg: '#FFF7ED', color: '#F97316' }; const iconConfig = TOOLKIT_ICONS[item.title] || { icon: Code, bg: '#FFF7ED', color: '#F97316' };
const IconComponent = iconConfig.icon; const IconComponent = iconConfig.icon;
return ( return (
@@ -1516,8 +1481,6 @@ export default function Toolkit() {
setSelectedToolkit('ai'); setSelectedToolkit('ai');
} else if (item.title === 'Biotech tools') { } else if (item.title === 'Biotech tools') {
setSelectedToolkit('biotech'); setSelectedToolkit('biotech');
} else if (item.title === 'GPA and internal calculator') {
setSelectedToolkit('gpa');
} else if (item.url && item.url !== '#') { } else if (item.url && item.url !== '#') {
window.open(item.url, '_blank'); window.open(item.url, '_blank');
} }

View File

@@ -0,0 +1,83 @@
import { getBackendUrl } from '@/lib/utils';
const DEVICE_ID_KEY = 'rit_device_identifier';
/**
* Gets a persistent user identifier for likes.
* If user is authenticated, uses email. Otherwise uses a persistent UUID in localStorage.
*/
export function getClubUserIdentifier(userEmail?: string | null): string {
if (userEmail && userEmail.trim()) {
return userEmail.trim().toLowerCase();
}
try {
let deviceId = localStorage.getItem(DEVICE_ID_KEY);
if (!deviceId) {
deviceId = 'guest_' + Math.random().toString(36).substring(2, 11) + Date.now().toString(36);
localStorage.setItem(DEVICE_ID_KEY, deviceId);
}
return deviceId;
} catch {
return 'guest_fallback_user';
}
}
/**
* Fetch total counts of likes for all clubs & centers from database
*/
export async function fetchClubLikes(): Promise<Record<string, number>> {
try {
const res = await fetch(getBackendUrl('/api/clubs/likes'));
if (!res.ok) {
throw new Error(`Failed to fetch likes: ${res.status}`);
}
const data = await res.json();
return data || {};
} catch (err) {
console.warn('Unable to fetch club likes from database backend:', err);
return {};
}
}
/**
* Fetch user's liked club IDs from database
*/
export async function fetchUserLikedClubs(userIdentifier: string): Promise<string[]> {
try {
const res = await fetch(
getBackendUrl(`/api/clubs/user-likes?userIdentifier=${encodeURIComponent(userIdentifier)}`)
);
if (!res.ok) {
throw new Error(`Failed to fetch user likes: ${res.status}`);
}
const data = await res.json();
return Array.isArray(data) ? data : [];
} catch (err) {
console.warn('Unable to fetch user liked clubs from database backend:', err);
return [];
}
}
/**
* Toggle like status for a club/center in database
*/
export async function toggleClubLikeInDb(
clubId: string,
userIdentifier: string
): Promise<{ liked: boolean; count: number } | null> {
try {
const res = await fetch(getBackendUrl(`/api/clubs/${encodeURIComponent(clubId)}/like`), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userIdentifier }),
});
if (!res.ok) {
throw new Error(`Toggle like failed: ${res.status}`);
}
return await res.json();
} catch (err) {
console.warn(`Failed to persist like for club ${clubId} in database:`, err);
return null;
}
}