diff --git a/.env b/.env deleted file mode 100644 index 4b8469f..0000000 --- a/.env +++ /dev/null @@ -1,10 +0,0 @@ -ASTRA_DB_ID=35eb48e5-0472-4568-9f3d-a452a1c8ab2c -ASTRA_DB_KEYSPACE=default_keyspace -ASTRA_DB_API_ENDPOINT=https://35eb48e5-0472-4568-9f3d-a452a1c8ab2c-eu-west-1.apps.astra.datastax.com -ASTRA_DB_APPLICATION_TOKEN=AstraCS:lNoNTboZjvbvFGrJWTyJwZqp:8a244f6afa4acf88c1a43b4c9e25e6a56dc384caa5176b3141e93dc9375c5c4a - -# JWT Secret -JWT_SECRET=openrockets_secret_key_2024_super_secure - -# Server Configuration -PORT=3000 diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 6f3a291..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "liveServer.settings.port": 5501 -} \ No newline at end of file diff --git a/README-opennetwork-banner.md b/README-opennetwork-banner.md deleted file mode 100644 index 0229c81..0000000 --- a/README-opennetwork-banner.md +++ /dev/null @@ -1,210 +0,0 @@ -# OpenNetwork Collective Banner - -An advanced JavaScript banner that automatically displays registration information and system status for any website. - -## Features - -- ✅ **Auto-Detection**: Automatically detects and displays the current website domain -- ✅ **Professional Design**: Modern glassmorphism design with smooth animations -- ✅ **Responsive**: Fully responsive design that works on all screen sizes -- ✅ **Status Indicator**: Real-time system status with color-coded indicators -- ✅ **Smart Overflow**: Intelligent marquee scrolling when text overflows -- ✅ **Easy Integration**: Single script include - no configuration needed -- ✅ **Customizable**: Full API for programmatic control and customization -- ✅ **Performance**: Lightweight and optimized for fast loading - -## Quick Start - -Simply include the script in any website: - -```html - -``` - -The banner will automatically: -- Detect the current domain name -- Display at the bottom of the page -- Show the OpenNetwork flag logo -- Link to the affiliate page -- Display current system status - -## Demo - -See the banner in action: [Demo Page](opennetwork-banner-demo.html) - -## Configuration - -### Basic Configuration - -```javascript -// Access the banner configuration -window.OpenNetworkBanner.config.position = 'top'; // 'top' or 'bottom' -window.OpenNetworkBanner.config.animationDuration = 10000; // milliseconds -``` - -### Available Settings - -| Option | Default | Description | -|--------|---------|-------------| -| `flagLogoUrl` | Auto-detected | URL to the flag logo image | -| `affiliateUrl` | opennetworked.org/en/privacy/affiliates | Affiliate link URL | -| `statusApiUrl` | Auto-detected | Status API endpoint | -| `animationDuration` | 15000 | Marquee animation duration (ms) | -| `checkInterval` | 300000 | Status check interval (ms) | -| `position` | 'bottom' | Banner position ('top' or 'bottom') | - -## API Methods - -### Initialize Banner -```javascript -window.OpenNetworkBanner.init(); -``` - -### Destroy Banner -```javascript -window.OpenNetworkBanner.destroy(); -``` - -### Update Status -```javascript -window.OpenNetworkBanner.updateStatus('operational', 'All Systems Running'); -window.OpenNetworkBanner.updateStatus('warning', 'Minor Issues Detected'); -window.OpenNetworkBanner.updateStatus('error', 'Service Temporarily Down'); -``` - -## Status Types - -| Type | Color | Use Case | -|------|-------|----------| -| `operational` | Green | Normal operations | -| `warning` | Orange | Minor issues or maintenance | -| `error` | Red | Major issues or outages | - -## Browser Support - -- ✅ Chrome 60+ -- ✅ Firefox 55+ -- ✅ Safari 12+ -- ✅ Edge 79+ -- ✅ Mobile browsers - -## Advanced Usage - -### Custom Status Check - -```javascript -// Override the default status check -window.OpenNetworkBanner.config.statusApiUrl = 'https://your-api.com/status'; - -// Or implement custom status logic -setInterval(async () => { - const customStatus = await checkYourSystemStatus(); - window.OpenNetworkBanner.updateStatus( - customStatus.level, - customStatus.message - ); -}, 60000); // Check every minute -``` - -### Integration with Frameworks - -#### React -```jsx -import { useEffect } from 'react'; - -function App() { - useEffect(() => { - // Banner auto-initializes, but you can control it - return () => window.OpenNetworkBanner?.destroy(); - }, []); - - return
Your app content
; -} -``` - -#### Vue.js -```vue - - - -``` - -## Customization - -### Custom Styling - -You can override the banner styles by adding custom CSS: - -```css -.opennetwork-banner { - /* Your custom styles */ - background: linear-gradient(135deg, #your-color 0%, #your-color-2 100%); -} - -.on-domain { - color: #your-highlight-color; -} -``` - -### Custom Logo - -```javascript -window.OpenNetworkBanner.config.flagLogoUrl = 'https://your-domain.com/your-logo.png'; -``` - -## Technical Details - -### Performance -- **Bundle size**: ~8KB minified -- **Load time**: <50ms on modern browsers -- **Memory usage**: <1MB -- **CPU usage**: Minimal, only during animations - -### Security -- All external links use `rel="noopener noreferrer"` -- No third-party dependencies -- CSP-friendly implementation -- XSS protection built-in - -## Troubleshooting - -### Banner Not Appearing -1. Check browser console for errors -2. Ensure script is loaded correctly -3. Check for conflicting CSS z-index values -4. Verify no other scripts are interfering - -### Status Not Updating -1. Check network connectivity -2. Verify status API endpoint -3. Check browser CORS policies -4. Monitor console for API errors - -### Text Overflow Issues -1. Banner automatically handles overflow -2. Marquee activates when needed -3. Responsive breakpoints adjust text size -4. Mobile view hides status text when needed - -## License - -© 2025 OpenNetwork Collective. All rights reserved. - -## Support - -For technical support or feature requests, please contact the OpenNetwork Collective team. - ---- - -**Made with ❤️ by OpenNetwork Collective** diff --git a/README.md b/README.md deleted file mode 100644 index 6ebd746..0000000 --- a/README.md +++ /dev/null @@ -1,306 +0,0 @@ -# OpenRockets LMS - Complete Learning Management System - -> **Repository Restoration Notice (February 15, 2026)**: This repository has been restored to its last organic commit from October 1, 2025 (commit `55b35d0`), removing all AI-generated commits and pull requests that were merged after that date. The repository now reflects the authentic version of the website created by the OpenRockets community before AI bot interference. - -A modern, full-stack Learning Management System built with Node.js, Express, and AstraDB. Features include user authentication, course management, community interactions, real-time messaging, and calendar-based event scheduling. - -![preview](https://github.com/user-attachments/assets/c6b74ca6-0056-41fe-b895-41ccc0d123cc) - -### Coding meets collaboration. Level up your skills through weekly challenges, open-source teamwork, live sessions, Discussions & peer events! - -## 🚀 Features - -### Authentication & User Management -- **User Registration & Login** with JWT authentication -- **Profile Management** with image upload (converted to DataURI) -- **Role-based Access** (Student Learners & Student Lecturers) -- **Verified Status** system -- **User Status** indicators (online, busy, studying, coding, etc.) - -### Community Features -- **Post Creation** with rich content, images, and tags -- **Comments System** with likes and replies -- **Real-time Interactions** -- **Trending Topics** and hashtag support -- **Study Groups** functionality -- **User Search** and discovery - -### Calendar & Events -- **Event Scheduling** (for instructors) -- **Calendar Views** (week, month, mini-calendar) -- **Event Registration** with capacity limits -- **Real-time Updates** of event status -- **Event Filtering** by type and category - -### Messaging System -- **Direct Messaging** between users -- **Message History** persistence -- **Real-time Notifications** -- **Message Status** tracking - -### Dashboard Features -- **Personalized Dashboard** with progress tracking -- **Upcoming Events** overview -- **Community Highlights** -- **Study Statistics** and progress bars -- **Quick Actions** for common tasks - -## 🛠 Tech Stack - -### Backend -- **Node.js** with Express.js -- **AstraDB** (Cassandra) for data persistence -- **JWT** for authentication -- **bcryptjs** for password hashing -- **Multer** for file uploads -- **CORS** for cross-origin requests - -### Frontend -- **Vanilla JavaScript** (ES6+) -- **CSS3** with CSS Grid and Flexbox -- **Font Awesome** for icons -- **Google Fonts** (Inter, Space Mono) -- **Responsive Design** for all devices - -### Database Collections -- `users` - User profiles and authentication -- `posts` - Community posts and content -- `comments` - Post comments and replies -- `events` - Calendar events and classes -- `messages` - Direct messages between users -- `notifications` - System notifications -- `study_groups` - Study group management -- `user_progress` - Learning progress tracking - -## 📁 Project Structure - -``` -openrockets.com/ -├── server.js # Main backend server -├── package.json # Dependencies and scripts -├── .env # Environment variables -├── landing.html # Landing page -├── dashboard.html # User dashboard -├── community.html # Community page -├── calendar.html # Calendar page -├── about.html # About page (PDF viewer) -├── scripts/ -│ ├── api.js # API client and utilities -│ ├── auth.js # Authentication modals and forms -│ ├── landing.js # Landing page interactions -│ ├── dashboard.js # Dashboard UI functionality -│ ├── dashboard-integration.js # Dashboard backend integration -│ ├── community.js # Community UI functionality -│ ├── community-integration.js # Community backend integration -│ ├── calendar.js # Calendar UI functionality -│ └── calendar-integration.js # Calendar backend integration -├── styles/ -│ ├── lms-main.css # Landing page styles -│ ├── dashboard.css # Dashboard styles -│ ├── community.css # Community styles -│ ├── calendar.css # Calendar styles -│ ├── components.css # Shared components -│ ├── main.css # Global styles -│ └── responsive.css # Mobile responsiveness -└── v/ # Static assets (images, etc.) -``` - -## 🚀 Setup Instructions - -### Prerequisites -- Node.js (v14 or higher) -- npm or yarn -- AstraDB account and database - -### 1. Clone and Install -```bash -git clone -cd openrockets.com -npm install -``` - -### 2. Environment Setup -The `.env` file is already configured with AstraDB credentials: -```env -ASTRA_DB_ID=35eb48e5-0472-4568-9f3d-a452a1c8ab2c -ASTRA_DB_KEYSPACE=default_keyspace -ASTRA_DB_API_ENDPOINT=https://35eb48e5-0472-4568-9f3d-a452a1c8ab2c-eu-west-1.apps.astra.datastax.com -ASTRA_DB_APPLICATION_TOKEN=AstraCS:lNoNTboZjvbvFGrJWTyJwZqp:8a244f6afa4acf88c1a43b4c9e25e6a56dc384caa5176b3141e93dc9375c5c4a -``` - -### 3. Start the Server -```bash -# Development mode -npm run dev - -# Production mode -npm start -``` - -### 4. Access the Application -- Open http://localhost:3000 in your browser -- Create an account or sign in -- Explore the features! - -## 🔧 API Endpoints - -### Authentication -- `POST /api/auth/register` - User registration -- `POST /api/auth/login` - User login -- `GET /api/auth/profile` - Get user profile -- `PUT /api/auth/profile` - Update user profile - -### Posts & Community -- `POST /api/posts` - Create new post -- `GET /api/posts` - Get posts (with filtering) -- `GET /api/posts/:id` - Get single post -- `POST /api/posts/:id/like` - Like/unlike post -- `POST /api/posts/:id/comments` - Add comment -- `GET /api/posts/:id/comments` - Get comments - -### Events & Calendar -- `POST /api/events` - Create new event (instructors only) -- `GET /api/events` - Get events (with filtering) -- `POST /api/events/:id/join` - Join event - -### Messaging -- `POST /api/messages` - Send message -- `GET /api/messages/:userId` - Get conversation - -### Search & Discovery -- `GET /api/search` - Search posts, events, and users - -## 🎨 UI Features - -### Design System -- **Dark Mode** by default with Microsoft Teams inspiration -- **Responsive Layout** that works on all devices -- **Smooth Animations** and micro-interactions -- **Consistent Typography** using Inter and Space Mono fonts -- **Programming-focused** color scheme and iconography - -### Interactive Elements -- **Modal Dialogs** for forms and details -- **Loading States** with spinners and skeletons -- **Toast Notifications** for user feedback -- **Dropdown Menus** for navigation -- **Image Galleries** with lightbox functionality -- **Real-time Updates** without page refresh - -### Accessibility -- **Keyboard Navigation** support -- **ARIA Labels** for screen readers -- **Focus Management** in modals -- **Color Contrast** compliance -- **Responsive Text** sizing - -## 🔒 Security Features - -- **JWT Authentication** with secure token storage -- **Password Hashing** using bcryptjs -- **Input Validation** on frontend and backend -- **File Upload Security** with type checking -- **CORS Configuration** for cross-origin requests -- **SQL Injection Prevention** through parameterized queries - -## 📱 Mobile Support - -- **Responsive Grid** layouts -- **Touch-friendly** interface elements -- **Mobile-optimized** navigation -- **Swipe Gestures** for image galleries -- **Adaptive Typography** for small screens - -## 🚀 Deployment - -### Local Development -```bash -npm run dev -``` - -### Production Build -```bash -npm start -``` - -### Environment Variables -Make sure to set these in production: -- `PORT` - Server port (default: 3000) -- `JWT_SECRET` - Secret key for JWT tokens -- `ASTRA_DB_*` - AstraDB connection details - -## 📖 Usage Guide - -### For Students (Learners) -1. **Register** with your email and create a profile -2. **Browse Events** in the calendar to find interesting classes -3. **Join Classes** by clicking the join button -4. **Participate** in community discussions -5. **Track Progress** on your dashboard - -### For Students (Lecturers) -1. **Register** as a "Student Lecturer" -2. **Create Events** using the calendar interface -3. **Manage Attendees** and event capacity -4. **Share Knowledge** through community posts -5. **Monitor Engagement** through analytics - -### Community Interaction -1. **Create Posts** with rich content and images -2. **Comment and Like** to engage with content -3. **Use Hashtags** to categorize content -4. **Join Study Groups** for collaborative learning -5. **Follow Trending Topics** to stay updated - -## 🐛 Troubleshooting - -### Common Issues - -1. **Database Connection Error** - - Verify AstraDB credentials in `.env` - - Check network connectivity - - Ensure keyspace exists - -2. **Authentication Issues** - - Clear browser localStorage - - Check JWT token expiration - - Verify user credentials - -3. **File Upload Problems** - - Check file size (max 5MB) - - Verify file type (images only) - - Ensure proper form encoding - -### Development Tips - -- Use browser DevTools for debugging -- Check console for JavaScript errors -- Monitor network requests in DevTools -- Use Postman for API testing - -## 🤝 Contributing - -1. Fork the repository -2. Create a feature branch -3. Make your changes -4. Test thoroughly -5. Submit a pull request - -## 📄 License - -MIT License - see LICENSE file for details - -## 🆘 Support - -For issues and questions: -- Create an issue on GitHub -- Contact the development team -- Check the documentation - ---- - -**OpenRockets LMS** - Empowering the next generation of programmers through collaborative learning! 🚀 - -#### OpenRockets.com is the present official domain of the OpenRockets Open-Source Software Foundation, though this may be subject to change in the future. -======= - diff --git a/REDESIGN.md b/REDESIGN.md deleted file mode 100644 index e69de29..0000000 diff --git a/about.html b/about.html deleted file mode 100644 index 186bd63..0000000 --- a/about.html +++ /dev/null @@ -1,242 +0,0 @@ - - - - - - About OpenRockets Foundation - Open Source Software Development - - - - - - - - - - - - - - -
-
-
-

About OpenRockets

-

Learn about our mission, vision, and the passionate community driving open-source innovation

-
- - - - - - -
-
- - - - - - - - \ No newline at end of file diff --git a/assets/i/static-v3/Logo_of_Nalanda_College_Colombo.png b/assets/i/static-v3/Logo_of_Nalanda_College_Colombo.png deleted file mode 100644 index b7390b2..0000000 Binary files a/assets/i/static-v3/Logo_of_Nalanda_College_Colombo.png and /dev/null differ diff --git a/assets/i/static-v3/St_John's_College_Jaffna_crest.png b/assets/i/static-v3/St_John's_College_Jaffna_crest.png deleted file mode 100644 index 3f142fe..0000000 Binary files a/assets/i/static-v3/St_John's_College_Jaffna_crest.png and /dev/null differ diff --git a/assets/i/static-v3/University_of_Moratuwa_logo.png b/assets/i/static-v3/University_of_Moratuwa_logo.png deleted file mode 100644 index 57b2ef9..0000000 Binary files a/assets/i/static-v3/University_of_Moratuwa_logo.png and /dev/null differ diff --git a/assets/i/static-v3/flag-orpheus-left.png b/assets/i/static-v3/flag-orpheus-left.png deleted file mode 100644 index 63886ab..0000000 Binary files a/assets/i/static-v3/flag-orpheus-left.png and /dev/null differ diff --git a/assets/i/static-v3/hackclub.png b/assets/i/static-v3/hackclub.png deleted file mode 100644 index a7bc5e2..0000000 Binary files a/assets/i/static-v3/hackclub.png and /dev/null differ diff --git a/assets/i/static-v3/logo-web-back.png b/assets/i/static-v3/logo-web-back.png deleted file mode 100644 index 4fb1605..0000000 Binary files a/assets/i/static-v3/logo-web-back.png and /dev/null differ diff --git a/assets/i/static-v3/logo.png b/assets/i/static-v3/logo.png deleted file mode 100644 index 428e984..0000000 Binary files a/assets/i/static-v3/logo.png and /dev/null differ diff --git a/assets/i/static-v3/logo1.png b/assets/i/static-v3/logo1.png deleted file mode 100644 index 609986a..0000000 Binary files a/assets/i/static-v3/logo1.png and /dev/null differ diff --git a/assets/i/static-v3/logo_flag.png b/assets/i/static-v3/logo_flag.png deleted file mode 100644 index 01a506a..0000000 Binary files a/assets/i/static-v3/logo_flag.png and /dev/null differ diff --git a/assets/i/static-v3/opennetwork-collective-alliance.png b/assets/i/static-v3/opennetwork-collective-alliance.png deleted file mode 100644 index 2784a7b..0000000 Binary files a/assets/i/static-v3/opennetwork-collective-alliance.png and /dev/null differ diff --git a/assets/i/static-v3/pngtree-dendabor-kindergarten-high-school-logo-vector-png-image_16230886.png b/assets/i/static-v3/pngtree-dendabor-kindergarten-high-school-logo-vector-png-image_16230886.png deleted file mode 100644 index 0feca7f..0000000 Binary files a/assets/i/static-v3/pngtree-dendabor-kindergarten-high-school-logo-vector-png-image_16230886.png and /dev/null differ diff --git a/assets/i/static-v3/royal-college-black.png b/assets/i/static-v3/royal-college-black.png deleted file mode 100644 index 1f46374..0000000 Binary files a/assets/i/static-v3/royal-college-black.png and /dev/null differ diff --git a/assets/i/static-v3/site-logo.svg b/assets/i/static-v3/site-logo.svg deleted file mode 100644 index 9b358ed..0000000 --- a/assets/i/static-v3/site-logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/i/static-v3/sliit-campus-logo-png_seeklogo-611785.png b/assets/i/static-v3/sliit-campus-logo-png_seeklogo-611785.png deleted file mode 100644 index 32e58fc..0000000 Binary files a/assets/i/static-v3/sliit-campus-logo-png_seeklogo-611785.png and /dev/null differ diff --git a/assets/i/static-v3/static1.squarespace.webp b/assets/i/static-v3/static1.squarespace.webp deleted file mode 100644 index 3831da9..0000000 Binary files a/assets/i/static-v3/static1.squarespace.webp and /dev/null differ diff --git a/assets/i/uploa b/assets/i/uploa deleted file mode 100644 index 8b13789..0000000 --- a/assets/i/uploa +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/i/uploads/AE047A9E8D150297E2CF610FEF5BCEE69C442913_IXPANDFLASHDRIVEGOUPMAIN_gallery.png b/assets/i/uploads/AE047A9E8D150297E2CF610FEF5BCEE69C442913_IXPANDFLASHDRIVEGOUPMAIN_gallery.png deleted file mode 100644 index 9f4a2b6..0000000 Binary files a/assets/i/uploads/AE047A9E8D150297E2CF610FEF5BCEE69C442913_IXPANDFLASHDRIVEGOUPMAIN_gallery.png and /dev/null differ diff --git a/assets/i/uploads/ddffd b/assets/i/uploads/ddffd deleted file mode 100644 index 38cec26..0000000 --- a/assets/i/uploads/ddffd +++ /dev/null @@ -1 +0,0 @@ -dsd diff --git a/assets/i/uploads/pngimg.com - laptop_PNG101830.png b/assets/i/uploads/pngimg.com - laptop_PNG101830.png deleted file mode 100644 index 060f2e6..0000000 Binary files a/assets/i/uploads/pngimg.com - laptop_PNG101830.png and /dev/null differ diff --git a/assets/i/uploads/pngtree-aesthetic-dvd-or-cd-disk-png-image_5888606.png b/assets/i/uploads/pngtree-aesthetic-dvd-or-cd-disk-png-image_5888606.png deleted file mode 100644 index dfd50cc..0000000 Binary files a/assets/i/uploads/pngtree-aesthetic-dvd-or-cd-disk-png-image_5888606.png and /dev/null differ diff --git a/assets/i/uploads/pngtree-old-laptop-computer-obsolete-white-dirty-png-image_14053775.png b/assets/i/uploads/pngtree-old-laptop-computer-obsolete-white-dirty-png-image_14053775.png deleted file mode 100644 index 7828ded..0000000 Binary files a/assets/i/uploads/pngtree-old-laptop-computer-obsolete-white-dirty-png-image_14053775.png and /dev/null differ diff --git a/assets/press/opennetwork.png b/assets/press/opennetwork.png deleted file mode 100644 index b673746..0000000 Binary files a/assets/press/opennetwork.png and /dev/null differ diff --git a/calendar.html b/calendar.html deleted file mode 100644 index 108eb49..0000000 --- a/calendar.html +++ /dev/null @@ -1,583 +0,0 @@ - - - - - - Calendar - OpenRockets LMS - - - - - - - - - - - - - - -
- -
-
- -

Calendar

-
-
- - -
-
JD
- -
-
-
- - -
- -
-
- -
-

January 2025

-
- -
- -
-
- - - -
- -
-
- - -
- - - - -
- -
-
-
-
-
Sun
-
5
-
-
-
Mon
-
6
-
-
-
Tue
-
7
-
-
-
Wed
-
8
-
-
-
Thu
-
9
-
-
-
Fri
-
10
-
-
-
Sat
-
11
-
-
- -
-
-
08:00
-
09:00
-
10:00
-
11:00
-
12:00
-
13:00
-
14:00
-
15:00
-
16:00
-
17:00
-
18:00
-
19:00
-
20:00
-
- -
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
JavaScript Advanced Concepts
-
09:00 - 11:00
-
Dr. Sarah Chen
-
- -
-
-
-
-
-
-
-
-
-
-
-
Python ML Workshop
-
14:00 - 15:30
-
Prof. Alex Kumar
-
- -
-
-
-
-
-
-
-
-
-
React Code Review
-
17:30 - 18:30
-
Team Session
-
- -
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
Assignment Due
-
10:00
-
JavaScript Project
-
-
-
-
-
-
-
-
-
-
-
Python Data Structures
-
15:00 - 17:00
-
Dr. Lisa Wang
-
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
React Hooks Deep Dive
-
11:00 - 13:00
-
Sarah Johnson
-
-
-
-
-
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
JavaScript Testing
-
09:00 - 10:30
-
Mike Chen
-
-
-
-
-
-
-
-
-
-
-
Assignment Due
-
14:00
-
Python Data Analysis
-
-
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
-
-
Study Group
-
14:00 - 16:00
-
JavaScript Study Group
-
-
-
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - - - - - - - - - diff --git a/channel.html b/channel.html deleted file mode 100644 index 6225c09..0000000 --- a/channel.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - Redirecting... - - - - - Copyrights (c) 2025 OpenRockets Open-source Software Foundation. All Rights Reserved. -
- Redirecting.. - - \ No newline at end of file diff --git a/community-package.json b/community-package.json deleted file mode 100644 index e69de29..0000000 diff --git a/community-server.js b/community-server.js deleted file mode 100644 index 8b01534..0000000 --- a/community-server.js +++ /dev/null @@ -1,821 +0,0 @@ -const express = require('express'); -const http = require('http'); -const socketIo = require('socket.io'); -const cors = require('cors'); -const jwt = require('jsonwebtoken'); -const multer = require('multer'); -const path = require('path'); -const fs = require('fs').promises; -const { v4: uuidv4 } = require('uuid'); -require('dotenv').config(); - -const app = express(); -const server = http.createServer(app); -const io = socketIo(server, { - cors: { - origin: "*", - methods: ["GET", "POST"] - } -}); - -// Middleware -app.use(cors()); -app.use(express.json({ limit: '10mb' })); -app.use(express.static('public')); -app.use('/uploads', express.static('uploads')); - -// In-memory storage (replace with actual database) -const users = new Map(); -const posts = new Map(); -const comments = new Map(); -const chatMessages = new Map(); -const studyGroups = new Map(); -const activeUsers = new Map(); - -// Configure multer for file uploads -const storage = multer.diskStorage({ - destination: (req, file, cb) => { - cb(null, 'uploads/'); - }, - filename: (req, file, cb) => { - const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); - cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname)); - } -}); -const upload = multer({ - storage: storage, - limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit - fileFilter: (req, file, cb) => { - if (file.mimetype.startsWith('image/')) { - cb(null, true); - } else { - cb(new Error('Only image files are allowed!'), false); - } - } -}); - -// Initialize sample data -const initializeSampleData = () => { - // Add sample posts - const samplePosts = [ - { - id: '1', - title: 'Getting Started with React Hooks', - content: 'Just finished learning about useState and useEffect. The way React handles state management is amazing! Here\'s what I learned...', - author: { - id: 'user1', - fullName: 'Sarah Chen', - profileImage: null, - verified: true - }, - category: 'React', - tags: ['React', 'JavaScript', 'Frontend'], - createdAt: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(), - likes: 24, - shares: 5, - images: [] - }, - { - id: '2', - title: '', - content: 'Anyone else excited about the new Python 3.12 features? The performance improvements are incredible! 🚀\n\nJust migrated our backend and saw 15% speed improvement.', - author: { - id: 'user2', - fullName: 'Mike Johnson', - profileImage: null, - verified: false - }, - category: 'Python', - tags: ['Python', 'Backend', 'Performance'], - createdAt: new Date(Date.now() - 4 * 60 * 60 * 1000).toISOString(), - likes: 18, - shares: 3, - images: [] - }, - { - id: '3', - title: 'My First Machine Learning Project', - content: 'Built a sentiment analysis model using TensorFlow! It can classify movie reviews as positive or negative with 89% accuracy. Super proud of this milestone 💪', - author: { - id: 'user3', - fullName: 'Emma Davis', - profileImage: null, - verified: false - }, - category: 'Machine Learning', - tags: ['ML', 'TensorFlow', 'Python', 'NLP'], - createdAt: new Date(Date.now() - 6 * 60 * 60 * 1000).toISOString(), - likes: 42, - shares: 12, - images: [] - } - ]; - - samplePosts.forEach(post => { - posts.set(post.id, post); - }); - - // Add sample users - const sampleUsers = [ - { id: 'user1', fullName: 'Sarah Chen', status: 'online' }, - { id: 'user2', fullName: 'Mike Johnson', status: 'coding' }, - { id: 'user3', fullName: 'Emma Davis', status: 'studying' }, - { id: 'guest', fullName: 'Guest User', status: 'browsing' } - ]; - - sampleUsers.forEach(user => { - users.set(user.id, user); - }); - - console.log('✅ Sample data initialized'); -}; - -// JWT middleware -const authenticateToken = (req, res, next) => { - const authHeader = req.headers['authorization']; - const token = authHeader && authHeader.split(' ')[1]; - - if (!token) { - // Allow guest access for community features - req.user = { userId: 'guest', fullName: 'Guest User', role: 'guest' }; - return next(); - } - - jwt.verify(token, process.env.JWT_SECRET || 'fallback-secret-key', (err, user) => { - if (err) { - req.user = { userId: 'guest', fullName: 'Guest User', role: 'guest' }; - } else { - req.user = user; - } - next(); - }); -}; - -// Community API Routes -app.get('/api/community/posts', async (req, res) => { - try { - const { category, tag, author } = req.query; - let filteredPosts = Array.from(posts.values()); - - // Apply filters - if (category && category !== 'all') { - filteredPosts = filteredPosts.filter(post => - post.category.toLowerCase() === category.toLowerCase() - ); - } - - if (tag) { - filteredPosts = filteredPosts.filter(post => - post.tags.some(t => t.toLowerCase().includes(tag.toLowerCase())) - ); - } - - if (author) { - filteredPosts = filteredPosts.filter(post => - post.author.fullName.toLowerCase().includes(author.toLowerCase()) - ); - } - - // Sort by creation date (newest first) - filteredPosts.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); - - res.json({ - posts: filteredPosts, - total: filteredPosts.length - }); - } catch (error) { - console.error('Error fetching posts:', error); - res.status(500).json({ error: 'Failed to fetch posts' }); - } -}); - -app.post('/api/posts', authenticateToken, upload.array('images', 5), async (req, res) => { - try { - const postData = JSON.parse(req.body.data || '{}'); - const imageFiles = req.files || []; - - // Generate unique ID - const postId = uuidv4(); - - // Process uploaded images - const imageUrls = imageFiles.map(file => `/uploads/${file.filename}`); - - // Create post object - const newPost = { - id: postId, - title: postData.title || '', - content: postData.content, - category: postData.category || 'general', - tags: postData.tags || [], - author: { - id: req.user?.userId || 'guest', - fullName: postData.author?.fullName || 'Anonymous', - profileImage: postData.author?.profileImage || null, - verified: false - }, - images: imageUrls, - createdAt: new Date().toISOString(), - likes: 0, - shares: 0 - }; - - // Store post - posts.set(postId, newPost); - - // Broadcast to all connected clients - io.emit('new-post', newPost); - - res.status(201).json({ - success: true, - post: newPost - }); - - console.log(`📝 New post created: ${postId}`); - - } catch (error) { - console.error('Error creating post:', error); - res.status(500).json({ error: 'Failed to create post' }); - } -}); - -app.post('/api/community/posts/:postId/like', async (req, res) => { - try { - const { postId } = req.params; - const post = posts.get(postId); - - if (!post) { - return res.status(404).json({ error: 'Post not found' }); - } - - // In a real implementation, you'd track which users liked which posts - post.likes = (post.likes || 0) + 1; - posts.set(postId, post); - - // Broadcast like update - io.emit('post-liked', { postId, likes: post.likes }); - - res.json({ - success: true, - likes: post.likes - }); - - } catch (error) { - console.error('Error liking post:', error); - res.status(500).json({ error: 'Failed to like post' }); - } -}); - -app.get('/api/community/posts/:postId/comments', async (req, res) => { - try { - const { postId } = req.params; - const postComments = comments.get(postId) || []; - - res.json({ - comments: postComments, - total: postComments.length - }); - - } catch (error) { - console.error('Error fetching comments:', error); - res.status(500).json({ error: 'Failed to fetch comments' }); - } -}); - -app.post('/api/community/posts/:postId/comments', async (req, res) => { - try { - const { postId } = req.params; - const { content } = req.body; - - if (!content || !content.trim()) { - return res.status(400).json({ error: 'Comment content is required' }); - } - - const post = posts.get(postId); - if (!post) { - return res.status(404).json({ error: 'Post not found' }); - } - - const commentId = uuidv4(); - const newComment = { - id: commentId, - content: content.trim(), - author: { - id: req.user?.userId || 'guest', - fullName: req.user?.fullName || 'Anonymous', - profileImage: req.user?.profileImage || null - }, - createdAt: new Date().toISOString(), - likes: 0 - }; - - // Add comment to post - const postComments = comments.get(postId) || []; - postComments.push(newComment); - comments.set(postId, postComments); - - // Broadcast new comment - io.emit('new-comment', { postId, comment: newComment }); - - res.status(201).json({ - success: true, - comment: newComment - }); - - console.log(`💬 New comment on post ${postId}: ${commentId}`); - - } catch (error) { - console.error('Error adding comment:', error); - res.status(500).json({ error: 'Failed to add comment' }); - } -}); - -// Socket.io connection handling -io.on('connection', (socket) => { - console.log('User connected:', socket.id); - - // Join user to their rooms - socket.on('join-user', (userData) => { - socket.userId = userData.userId; - socket.username = userData.username; - - // Add to active users - activeUsers.set(socket.id, { - userId: userData.userId, - username: userData.username, - status: 'online', - lastSeen: new Date() - }); - - // Join general chat - socket.join('general'); - - // Broadcast user joined - io.emit('user-status-update', { - userId: userData.userId, - username: userData.username, - status: 'online' - }); - - // Send online users count - io.emit('online-count-update', activeUsers.size); - }); - - // Handle chat messages - socket.on('send-message', (data) => { - const message = { - id: uuidv4(), - userId: socket.userId, - username: socket.username, - message: data.message, - channel: data.channel || 'general', - timestamp: new Date(), - type: 'message' - }; - - // Save message - const channels = chatMessages.get('channels') || new Map(); - const channelMessages = channels.get(data.channel) || []; - channelMessages.push(message); - channels.set(data.channel, channelMessages); - chatMessages.set('channels', channels); - - // Broadcast to channel - io.to(data.channel).emit('new-message', message); - }); - - // Handle typing indicators - socket.on('typing-start', (data) => { - socket.to(data.channel).emit('user-typing', { - userId: socket.userId, - username: socket.username, - channel: data.channel - }); - }); - - socket.on('typing-stop', (data) => { - socket.to(data.channel).emit('user-stop-typing', { - userId: socket.userId, - channel: data.channel - }); - }); - - // Handle real-time post updates - socket.on('post-like', (data) => { - io.emit('post-update', { - type: 'like', - postId: data.postId, - userId: socket.userId, - timestamp: new Date() - }); - }); - - socket.on('new-comment', (data) => { - io.emit('post-update', { - type: 'comment', - postId: data.postId, - comment: data.comment, - timestamp: new Date() - }); - }); - - // Handle disconnection - socket.on('disconnect', () => { - console.log('User disconnected:', socket.id); - - // Remove from active users - const userData = activeUsers.get(socket.id); - if (userData) { - activeUsers.delete(socket.id); - - // Broadcast user offline - io.emit('user-status-update', { - userId: userData.userId, - username: userData.username, - status: 'offline' - }); - - // Send updated online count - io.emit('online-count-update', activeUsers.size); - } - }); -}); - -// API Routes - -// Authentication -app.post('/api/auth/login', async (req, res) => { - try { - const { email, password } = req.body; - - // Find user (simplified - implement proper authentication) - const user = Array.from(users.values()).find(u => u.email === email); - - if (!user) { - return res.status(401).json({ error: 'Invalid credentials' }); - } - - const token = jwt.sign( - { userId: user.id, email: user.email }, - process.env.JWT_SECRET, - { expiresIn: '7d' } - ); - - res.json({ - token, - user: { - id: user.id, - fullName: user.fullName, - email: user.email, - profileImage: user.profileImage, - verified: user.verified, - role: user.role - } - }); - } catch (error) { - res.status(500).json({ error: 'Server error' }); - } -}); - -// Posts -app.get('/api/posts', async (req, res) => { - try { - const { category, limit = 20, offset = 0 } = req.query; - - let filteredPosts = Array.from(posts.values()); - - if (category && category !== 'all') { - filteredPosts = filteredPosts.filter(post => post.category === category); - } - - // Add author information - const postsWithAuthors = filteredPosts.map(post => { - const author = users.get(post.authorId); - return { - ...post, - author: { - id: author.id, - fullName: author.fullName, - profileImage: author.profileImage, - verified: author.verified - } - }; - }); - - // Sort by date - postsWithAuthors.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); - - // Paginate - const paginatedPosts = postsWithAuthors.slice(offset, offset + parseInt(limit)); - - res.json({ - posts: paginatedPosts, - total: filteredPosts.length, - hasMore: offset + parseInt(limit) < filteredPosts.length - }); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch posts' }); - } -}); - -app.post('/api/posts', authenticateToken, upload.array('images', 5), async (req, res) => { - try { - const { title, content, category, tags, visibility = 'public' } = req.body; - - if (!content || !category) { - return res.status(400).json({ error: 'Content and category are required' }); - } - - const postId = uuidv4(); - const imageUrls = req.files ? req.files.map(file => `/uploads/${file.filename}`) : []; - - const post = { - id: postId, - authorId: req.user.userId, - title: title || '', - content, - category, - tags: tags ? tags.split(',').map(tag => tag.trim()) : [], - images: imageUrls, - likes: 0, - shares: 0, - visibility, - createdAt: new Date(), - updatedAt: new Date() - }; - - posts.set(postId, post); - - // Broadcast new post - const author = users.get(req.user.userId); - io.emit('new-post', { - ...post, - author: { - id: author.id, - fullName: author.fullName, - profileImage: author.profileImage, - verified: author.verified - } - }); - - res.status(201).json({ - message: 'Post created successfully', - post: { - ...post, - author: { - id: author.id, - fullName: author.fullName, - profileImage: author.profileImage, - verified: author.verified - } - } - }); - } catch (error) { - res.status(500).json({ error: 'Failed to create post' }); - } -}); - -app.post('/api/posts/:postId/like', authenticateToken, async (req, res) => { - try { - const { postId } = req.params; - const post = posts.get(postId); - - if (!post) { - return res.status(404).json({ error: 'Post not found' }); - } - - // Toggle like (simplified - implement proper like tracking) - post.likes += 1; - post.updatedAt = new Date(); - - posts.set(postId, post); - - // Broadcast like update - io.emit('post-like-update', { - postId, - likes: post.likes, - likedBy: req.user.userId - }); - - res.json({ - message: 'Post liked successfully', - likes: post.likes - }); - } catch (error) { - res.status(500).json({ error: 'Failed to like post' }); - } -}); - -// Comments -app.get('/api/posts/:postId/comments', authenticateToken, async (req, res) => { - try { - const { postId } = req.params; - - const postComments = Array.from(comments.values()) - .filter(comment => comment.postId === postId) - .map(comment => { - const author = users.get(comment.authorId); - return { - ...comment, - author: { - id: author.id, - fullName: author.fullName, - profileImage: author.profileImage - } - }; - }) - .sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt)); - - res.json({ comments: postComments }); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch comments' }); - } -}); - -app.post('/api/posts/:postId/comments', authenticateToken, async (req, res) => { - try { - const { postId } = req.params; - const { content } = req.body; - - if (!content) { - return res.status(400).json({ error: 'Comment content is required' }); - } - - const commentId = uuidv4(); - const comment = { - id: commentId, - postId, - authorId: req.user.userId, - content, - likes: 0, - createdAt: new Date(), - updatedAt: new Date() - }; - - comments.set(commentId, comment); - - // Broadcast new comment - const author = users.get(req.user.userId); - io.emit('new-comment', { - ...comment, - author: { - id: author.id, - fullName: author.fullName, - profileImage: author.profileImage - } - }); - - res.status(201).json({ - message: 'Comment added successfully', - comment: { - ...comment, - author: { - id: author.id, - fullName: author.fullName, - profileImage: author.profileImage - } - } - }); - } catch (error) { - res.status(500).json({ error: 'Failed to add comment' }); - } -}); - -// Chat -app.get('/api/chat/messages/:channel', authenticateToken, async (req, res) => { - try { - const { channel } = req.params; - const { limit = 50 } = req.query; - - const channels = chatMessages.get('channels') || new Map(); - const channelMessages = channels.get(channel) || []; - - const recentMessages = channelMessages - .slice(-parseInt(limit)) - .reverse(); - - res.json({ messages: recentMessages }); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch messages' }); - } -}); - -// Study Groups -app.get('/api/study-groups', authenticateToken, async (req, res) => { - try { - const groups = Array.from(studyGroups.values()) - .filter(group => group.isPublic) - .map(group => ({ - ...group, - isMember: group.members.includes(req.user.userId) - })); - - res.json({ groups }); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch study groups' }); - } -}); - -app.post('/api/study-groups/:groupId/join', authenticateToken, async (req, res) => { - try { - const { groupId } = req.params; - const group = studyGroups.get(groupId); - - if (!group) { - return res.status(404).json({ error: 'Study group not found' }); - } - - if (!group.members.includes(req.user.userId)) { - group.members.push(req.user.userId); - group.memberCount = group.members.length; - studyGroups.set(groupId, group); - - // Broadcast group update - io.emit('study-group-update', { - groupId, - type: 'member-joined', - memberCount: group.memberCount - }); - } - - res.json({ message: 'Joined study group successfully' }); - } catch (error) { - res.status(500).json({ error: 'Failed to join study group' }); - } -}); - -// Active Users -app.get('/api/users/active', authenticateToken, async (req, res) => { - try { - const active = Array.from(activeUsers.values()) - .map(userData => { - const user = users.get(userData.userId); - return { - id: userData.userId, - fullName: user?.fullName || userData.username, - status: userData.status, - lastSeen: userData.lastSeen - }; - }); - - res.json({ users: active, count: active.length }); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch active users' }); - } -}); - -// Trending Topics -app.get('/api/trending', authenticateToken, async (req, res) => { - try { - // Calculate trending topics based on recent posts and activity - const recentPosts = Array.from(posts.values()) - .filter(post => { - const dayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000); - return new Date(post.createdAt) > dayAgo; - }); - - const topicCount = new Map(); - - recentPosts.forEach(post => { - // Count by category - const category = post.category; - topicCount.set(category, (topicCount.get(category) || 0) + 1); - - // Count by tags - post.tags.forEach(tag => { - topicCount.set(tag, (topicCount.get(tag) || 0) + 1); - }); - }); - - const trending = Array.from(topicCount.entries()) - .sort(([,a], [,b]) => b - a) - .slice(0, 10) - .map(([topic, count], index) => ({ - rank: index + 1, - topic, - posts: count, - trend: Math.random() > 0.5 ? 'up' : 'down' // Simulate trend - })); - - res.json({ trending }); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch trending topics' }); - } -}); - -// Health check -app.get('/api/health', (req, res) => { - res.json({ - status: 'healthy', - timestamp: new Date(), - activeConnections: activeUsers.size - }); -}); - -// Initialize sample data -initializeSampleData(); - -const PORT = process.env.PORT || 3001; -server.listen(PORT, () => { - console.log(`🚀 Community server running on port ${PORT}`); - console.log(`📡 WebSocket server ready for real-time features`); -}); diff --git a/community.html b/community.html deleted file mode 100644 index c816c71..0000000 --- a/community.html +++ /dev/null @@ -1,1155 +0,0 @@ - - - - - - Community - OpenRockets LMS - - - - - - - - - - - - - - - -
- -
-
- -

Community

-
-
- - -
-
JD
- -
-
-
- - -
-
- - - - -
- -
-
-
JD
- -
-
- - - - -
-
- - -
- -
-
- - -
- -
-

🚀 New JavaScript ES2024 Features You Should Know

-

I've just finished putting together a comprehensive guide on the latest JavaScript features that landed in ES2024. Some really exciting additions that will change how we write modern JavaScript!

- -
-
- - Array.prototype.with() method -
-
- - Temporal API improvements -
-
- - RegExp v flag -
-
- -
-
// New Array.with() method
-const arr = [1, 2, 3, 4, 5];
-const newArr = arr.with(2, 'updated');
-console.log(newArr); // [1, 2, 'updated', 4, 5]
-
-
- - -
- - -
-
- - -
- -
-

Just finished my first React project! 🎉 Built a task management app with drag & drop functionality using React DnD. The learning curve was steep but so worth it!

- -
- React Task Manager App -
- - -
- - - - -
-
-
-
MJ
-
- Mike Johnson - 2 hours ago -
-
-
-

This looks amazing! I'm working on a similar project. How did you handle the state management for the drag and drop?

-
-
- - -
-
- -
-
-
AS
-
- Alice Smith - 1 hour ago -
-
-
-

@Mike Johnson I used React Context for global state and local state for drag operations. Happy to share the code structure if you're interested!

-
-
- - -
-
- -
-
JD
- - -
-
-
- -
-
- - -
- -
-

📚 Weekend JavaScript Study Group - Join Us!

-

Hey everyone! We're organizing a JavaScript study group this weekend to work through some advanced concepts together. Topics include:

- -
    -
  • Closures and Scope
  • -
  • Async/Await patterns
  • -
  • ES6+ features deep dive
  • -
  • Practice coding challenges
  • -
- -
-
- - Saturday, January 11 - 2:00 PM EST -
-
- - Duration: 2-3 hours -
-
- - Virtual meeting via Zoom -
-
- - -
- - -
- -
-
- - -
- -
-

🐍 Python tip: List comprehensions vs Generator expressions

-

Quick comparison for beginners wondering about the difference:

- -
-
-
List Comprehension
-
# Creates a list in memory
-squares = [x**2 for x in range(1000)]
-print(type(squares))  # 
-
- -
-
Generator Expression
-
# Creates a generator object
-squares = (x**2 for x in range(1000))
-print(type(squares))  # 
-
-
- -

Use generators for large datasets to save memory! 🧠

-
- - -
-
- - -
- -
-
- - - -
-
-
- - - - - - - - - - - - - - - - - - diff --git a/dashboard.html b/dashboard.html deleted file mode 100644 index 76bbb5d..0000000 --- a/dashboard.html +++ /dev/null @@ -1,416 +0,0 @@ - - - - - - Dashboard - OpenRockets LMS - - - - - - - - - - - - - -
- -
-
- -

Dashboard

-
-
- - -
-
JD
- -
-
-
- - -
- -
-
-

Welcome back, John! 👋

-

You have 3 upcoming classes today and 2 pending assignments.

-
-
- - -
-
- - -
- -
-
-

Today's Schedule

- Monday, January 6, 2025 -
-
-
-
- 09:00 AM - 2h -
-
-

Advanced JavaScript Concepts

-

Dr. Sarah Chen

-
- JavaScript - Advanced -
-
-
- -
-
- -
-
- 02:00 PM - 1.5h -
-
-

Python for Machine Learning

-

Prof. Alex Kumar

-
- Python - ML -
-
-
- -
-
- -
-
- 05:30 PM - 1h -
-
-

Code Review Session

-

Team Session

-
- Review -
-
-
- -
-
-
- View Full Calendar → -
- - -
-
-

Learning Progress

- 75% Complete -
-
-
-
-

JavaScript Fundamentals

-

Module 8 of 10

-
-
-
-
- 80% -
- -
-
-

React Development

-

Module 5 of 12

-
-
-
-
- 42% -
- -
-
-

Python Basics

-

Module 12 of 15

-
-
-
-
- 88% -
-
-
- - -
-
-

Pending Assignments

- 2 Due Soon -
-
-
-
-

JavaScript Project - Todo App

-

Advanced JavaScript Concepts

-
- Due: Today, 11:59 PM - Project -
-
- -
- -
-
-

Python Data Analysis Report

-

Python for Machine Learning

-
- Due: Wednesday, Jan 8 - Report -
-
- -
-
- View All Assignments → -
- - -
-
-

Recent Activity

-
-
-
-
- -
-
-

Grade received for React Component Lab

- 2 hours ago -
-
A+
-
- -
-
- -
-
-

New message from Dr. Sarah Chen

- 4 hours ago -
-
- -
-
- -
-
-

Attended class Python Fundamentals

- Yesterday -
-
- -
-
- -
-
-

Achievement unlocked: First Pull Request

- 2 days ago -
-
-
-
- - -
-
-

Quick Stats

-
-
-
-
- -
-
- 6 - Active Courses -
-
- -
-
- -
-
- 32h - This Week -
-
- -
-
- -
-
- 15 - Achievements -
-
- -
-
- -
-
- 124 - Classmates -
-
-
-
- - -
-
-

Community Highlights

-
-
-
- -
- Just completed my first React project! Thanks to everyone who helped in the study group 🚀 -
-
- - -
-
- -
- -
- Anyone interested in a JavaScript study group this weekend? Drop a comment if you're in! -
-
- - -
-
-
- View Community → -
-
-
-
- - - - - - - diff --git a/form/logo.png b/form/logo.png deleted file mode 100644 index d222207..0000000 Binary files a/form/logo.png and /dev/null differ diff --git a/github.html b/github.html deleted file mode 100644 index 2817f7b..0000000 --- a/github.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - Redirecting... - - - - Copyrights (c) 2025 OpenRockets Open-source Software Foundation. All Rights Reserved. -
- Redirecting.. - - \ No newline at end of file diff --git a/i/app.css b/i/app.css deleted file mode 100644 index bdc0fa0..0000000 --- a/i/app.css +++ /dev/null @@ -1,815 +0,0 @@ -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap'); -/*These two fonts are essential. Only use them in the future.*/ -@import url('https://fonts.googleapis.com/css2?family=Cal+Sans&family=Space+Grotesk:wght@300..700&display=swap'); -*{ - margin: 0; - padding: 0; -} -body{ - background-color: white; - font-family: "Cal Sans", sans-serif; -} -html{ - scroll-behavior: smooth; -} -.container{ - width: 100%; - - height: max-content; - display: flex; - justify-content: flex-start; - align-items: center; - flex-direction: column; -} -.navigation-xi{ - width: 100%; - height:5rem; - display: flex; - background-color: #000000; - color: white; - border-bottom: 2px solid #000000; - margin-bottom: 2rem; - justify-content: center; - align-items: center; -} -.naviagtion-left{ - width: 100%; - height: 5rem; - display: flex; - justify-content: flex-start; - align-items: center; - padding-left: 1rem; - -} -.navigation-right{ - width: 100%; - height: 5rem; - display: flex; - justify-content: flex-end; - align-items: center; - padding-right: 1rem; -} -.logo-header-nav-intend, .logo-header-nav-intend img{ - width: 5rem - -} -.content-center{ - width: 100%; - height: max-content; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; -} -.content-cendiv{ - width: 100%; - height: max-content; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; -} -.profileIDf_content{ - width: 100%; - height: max-content; - display: flex; - justify-content: center; - align-items: center; - flex-direction:row; - -} -.profileIDf_content .profile-image-main-ddd{ - min-width:6rem; - max-width: 6rem; - border-radius: 50%; - margin-right: 1rem; -} -.innercontent-content-centdiv-profile{ - width: max-content; - height: max-content; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; -} -.innercontent-content-centdiv-profile .profileIDf_content-organize-tabs{ - width: max-content; - height: max-content; - display: flex; - justify-content: center; - align-items: flex-start; - - flex-direction: column; -} -.profileIDf_content div{ - width: max-content; - height: max-content; - display: flex; - justify-content: center; - align-items: center; - flex-direction: row; - -} -.profileIDf_content div h3{ - font-size: 1.5rem; - font-weight: 500; - -} -.verified-badge-common{ - width: 1.5rem; - height: 1.5rem; - -} -.profileIDf_content div .profileIDf_content-text-fddf { - width: max-content; - height: max-content; - display: flex; - justify-content: center; - align-items: center; - flex-direction: row; - background: black; - border-radius: .3rem; - padding: .2rem .4rem .2rem .4rem; - fill: white; - color: white; - font-size: 80%; - -} -.profileIDf_content .profileIDf_content-text-fddf svg{ - width: .8rem; - height: .8rem; - fill: white; - color: white; -} -.profileCard{ - margin-top: 10vh; - margin-bottom: 2rem; - width: 60%; - height: max-content; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - border-radius: 1rem; - padding: 1rem 1rem 1rem 1rem ; - border: #000000 solid 2px ; - transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); - position: relative; - overflow: hidden; -} -.profileCard:hover { - transform: translateY(-4px); - box-shadow: 0 25px 50px rgba(0, 0, 0, 0.12), 0 8px 16px rgba(0, 0, 0, 0.08); - border-color: #000000; -} -.profileCard::after { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 1rem; - border: 1px solid transparent; - transition: all 0.4s ease; - pointer-events: none; -} -.profileCard:hover::after { - border-color: rgba(0, 0, 0, 0.1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); -} -.profileCard-nav{ - width: 100%; - height: max-content; - display: flex; - justify-content: center; - align-items: center; - flex-direction: row; - - border-radius: 1rem; -} -.profileCard-nav-left{ - width: 100%; - height: max-content; - display: flex; - justify-content: flex-start; - align-items: center; - padding-left: 1rem; - border-radius: 1rem; -} -.profileCard-nav-right{ - width: max-content; - height: max-content; - border-radius: 1rem; - display: flex; - font-family: "Space Grotesk", sans-serif; - justify-content: flex-end; - align-items: center; - padding-right: 1rem; -} -.profileCard-nav-right svg{ - fill: rgb(98, 87, 255); - margin-right: .5rem; -} -.profileCard-details{ - width: 90%; - height: max-content; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - text-align: justify; - margin-top: 5vh; - font-family: "Space Grotesk", sans-serif; -} -.profileCard-details span{ - background-color: black; - color: white; - padding: 0 .3rem 0 .3rem; - border-radius: .2rem; -} -.profileCard-footer{ - width: 100%; - height: max-content; - display: flex; - justify-content: center; - align-items: center; - flex-direction: row; - margin-top: 2rem; -} -.profilecard-footer-left{ - width: 100%; - height: max-content; - display: flex; - justify-content: flex-start; - align-items: center; - padding-left: 1rem; -} -.profilecard-footer-right{ - width: max-content; - height: max-content; - display: flex; - justify-content: flex-end; - align-items: center; - padding-right: 1rem; -} -.profilecard-footer-right div { - border: #000000 solid 2px; - border-radius: 1rem; - padding: .2rem .3rem .2rem .3rem; - width: max-content; - height: max-content; - display: flex; - justify-content: flex-end; - align-items: center; -} -.profilecard-footer-right div svg{ - margin-right: .3rem; - fill: black; - -} -.profilecard-footer-right div h3{ - font-size:80%; - - color: black; -} -.profilecard-footer-left div { - border: #000000 solid 2px; - border-radius: 1rem; - padding: .2rem .3rem .2rem .3rem; - width: max-content; - height: max-content; - display: flex; - justify-content: flex-end; - align-items: center; - margin-right: .3rem; -} - -/* Enhanced Social Media Button Animations - Vercel Style */ -.profilecard-footer-left div, -.profilecard-footer-right div { - transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); - position: relative; - overflow: hidden; -} - -.profilecard-footer-left div:hover, -.profilecard-footer-right div:hover { - transform: translateY(-2px); - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.08); - border-color: #000000; -} - -.profilecard-footer-left div::after, -.profilecard-footer-right div::after { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 1rem; - border: 1px solid transparent; - transition: all 0.3s ease; - pointer-events: none; -} - -.profilecard-footer-left div:hover::after, -.profilecard-footer-right div:hover::after { - border-color: rgba(0, 0, 0, 0.05); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); -} - -/* Subtle text animation */ -.member-card-name, -.member-card-role { - transition: all 0.3s ease; -} - -.member-card:hover .member-card-name { - transform: translateY(-1px); -} - -.member-card:hover .member-card-role { - color: #000000; -} - -/* Badge hover effect */ -.member-card-badge { - transition: all 0.3s ease; -} - -.member-card:hover .member-card-badge { - transform: scale(1.05); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); -} - -.article-header{ - width: 100%; - justify-content: flex-start; - align-items: flex-start; -} -.article-header h2 { - margin-bottom: 1rem; - font-family: "Cal Sans", sans-serif; -} - -.article-header .team-badge { - background: #6257ff; - color: white; - padding: 0.2rem 0.4rem; - border-radius: 0.3rem; - font-size: 0.8rem; - margin-left: 0.5rem; -} - -.article-header .subtitle { - margin-bottom: 0.5rem; - color: #666; - font-size: 0.9rem; -} - -.article-header .location-info { - display: flex; - align-items: center; - margin-bottom: 1.5rem; -} - -.article-header .location-info svg { - margin-right: 0.3rem; -} - -.article-header .location-info span { - color: #ffffff; - font-size: 0.85rem; -} - -.article-content h3 { - margin-bottom: 1rem; - font-weight: 600; -} - -.article-content p { - line-height: 1.7; - margin-bottom: 1rem; -} - -.article-content .thanks { - margin-bottom: 1.5rem; - font-weight: 600; -} - -.footer-meta { - background: #f8f9fa; - border: 1px solid #dee2e6; - color: #6c757d; -} - -.footer-share { - background: #e7f3ff; - border: 1px solid #b3d9ff; - color: #000000; -} -a{ - text-decoration: none; - color: inherit; -} -@media screen and (max-width: 768px) { - .profileCard { - width: 90%; - } - body{ - font-size: 80%; - } - .profileIDf_content { - flex-direction: column; - align-items: flex-start; - margin-left: 2rem; - } - - .profileCard-nav { - flex-direction: column; - align-items: center; - } - .profileCard-nav-right { - margin-top: 1rem; - } - -} - -/* Horizontally Scrollable Buttons Section */ -.members-scroll-section { - width: 100%; - margin-top: 3rem; - margin-bottom: 2rem; - - padding: 0 1rem; -} - -.members-scroll-title { - text-align: center; - margin-bottom: 1.5rem; - font-family: "Cal Sans", sans-serif; - font-size: 1.5rem; - font-weight: 600; -} - -.members-scroll-container { - width: 100%; - display: flex; - justify-content: center; - align-items:center ; - overflow-x: auto; - overflow-y: hidden; - padding: 1rem 0; - white-space: nowrap; - scrollbar-width: thin; - scrollbar-color: #ccc #f0f0f0; -} - -.members-scroll-container::-webkit-scrollbar { - height: 8px; -} - -.members-scroll-container::-webkit-scrollbar-track { - background: #f0f0f0; - border-radius: 10px; -} - -.members-scroll-container::-webkit-scrollbar-thumb { - background: #ccc; - border-radius: 10px; -} - -.members-scroll-container::-webkit-scrollbar-thumb:hover { - background: #999; -} - -.member-card { - display: inline-block; - width: 200px; - margin-right: 1rem; - padding: 1rem; - border: 2px solid #000000; - border-radius: 1rem; - background: white; - text-align: center; - vertical-align: top; - white-space: normal; - transition: all 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94); - cursor: pointer; - position: relative; - overflow: hidden; -} - -.member-card::after { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 1rem; - border: 1px solid transparent; - transition: all 0.35s ease; - pointer-events: none; -} - -.member-card:hover { - transform: translateY(-6px) scale(1.01); - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.12), 0 6px 12px rgba(0, 0, 0, 0.08); - border-color: #000000; -} - -.member-card:hover::after { - border-color: rgba(0, 0, 0, 0.08); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); -} - -.member-card:hover .member-card-image { - transform: scale(1.05); -} - -.member-card:last-child { - margin-right: 0; -} - -.member-card-image { - width: 60px; - height: 60px; - border-radius: 50%; - margin: 0 auto 0.5rem auto; - object-fit: cover; - transition: all 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94); - border: 2px solid #f8f9fa; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); -} - -.member-card:hover .member-card-image { - border-color: #000000; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); -} - -.member-card-name { - font-size: 1rem; - font-weight: 600; - margin-bottom: 0.3rem; - font-family: "Cal Sans", sans-serif; -} - -.member-card-role { - font-size: 0.8rem; - color: #666; - margin-bottom: 0.5rem; - font-family: "Space Grotesk", sans-serif; -} - -.member-card-badge { - background: #000000; - color: white; - padding: 0.2rem 0.4rem; - border-radius: 0.3rem; - font-size: 0.7rem; - display: inline-block; -} - -@media screen and (max-width: 768px) { - .members-scroll-section { - padding: 0 0.5rem; - } - - .member-card { - width: 160px; - padding: 0.8rem; - } - - .member-card-image { - width: 50px; - height: 50px; - } - - .member-card-name { - font-size: 0.9rem; - } - - .member-card-role { - font-size: 0.75rem; - } -} - -@media screen and (max-width: 1200px) { - .profileCard { - width: 70%; - } -} - -@media screen and (max-width: 992px) { - .profileCard { - width: 80%; - padding: 1rem; - } - - .profileIDf_content { - flex-direction: column; - text-align: center; - } - - .profile-image-main-ddd { - margin-bottom: 1rem; - } -} - -@media screen and (max-width: 576px) { - .profileCard { - width: 95%; - margin-top: 5vh; - padding: 0.8rem; - } - - .profileCard-details h3 { - font-size: 0.9rem; - line-height: 1.6; - } - - .profile-image-main-ddd { - width: 120px; - height: 120px; - } -} - -@media screen and (max-width: 480px) { - .member-card { - width: 140px; - padding: 0.6rem; - margin-right: 0.8rem; - } - - .member-card-image { - width: 45px; - height: 45px; - } - - .member-card-name { - font-size: 0.8rem; - } - - .member-card-role { - font-size: 0.7rem; - } -} - -/* Like Button System Styles */ -.like-button { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0.5rem 0.8rem; - border: 2px solid #000000; - border-radius: 1rem; - background: white; - cursor: pointer; - transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); - font-family: "Space Grotesk", sans-serif; - font-weight: 500; - font-size: 0.9rem; - position: relative; - overflow: visible; -} - -.like-button:hover { - transform: translateY(-2px); - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.08); - border-color: #000000; -} - -.like-icon { - width: 16px; - height: 16px; - fill: #666; - transition: all 0.3s ease; -} - -.like-button:hover .like-icon { - fill: #ff6b6b; - transform: scale(1.1); -} - -.like-button.liked .like-icon { - fill: #ff6b6b; - animation: heartPulse 0.6s ease-in-out; -} - -.like-count { - color: #333; - font-weight: 600; - min-width: 20px; - transition: all 0.3s ease; -} - -.like-button.liked .like-count { - color: #ff6b6b; -} - -/* Like Message */ -.like-message { - position: absolute; - top: -40px; - left: 50%; - transform: translateX(-50%); - background: #333; - color: white; - padding: 0.5rem 0.8rem; - border-radius: 0.5rem; - font-size: 0.8rem; - white-space: nowrap; - animation: fadeInOut 2s ease-in-out; - pointer-events: none; - z-index: 1000; -} - -.like-message::after { - content: ''; - position: absolute; - top: 100%; - left: 50%; - transform: translateX(-50%); - border: 5px solid transparent; - border-top-color: #333; -} - -/* Floating Hearts Animation */ -.floating-heart { - position: absolute; - top: -10px; - left: 50%; - transform: translateX(-50%); - pointer-events: none; - z-index: 1000; -} - -/* Animations */ -@keyframes heartPulse { - 0% { transform: scale(1); } - 50% { transform: scale(1.3); } - 100% { transform: scale(1); } -} - -@keyframes floatUp { - 0% { - opacity: 1; - transform: translateY(0) translateX(-50%) scale(1); - } - 100% { - opacity: 0; - transform: translateY(-30px) translateX(-50%) scale(0.5); - } -} - -@keyframes fadeInOut { - 0% { opacity: 0; transform: translateX(-50%) translateY(-10px); } - 20% { opacity: 1; transform: translateX(-50%) translateY(0); } - 80% { opacity: 1; transform: translateX(-50%) translateY(0); } - 100% { opacity: 0; transform: translateX(-50%) translateY(-10px); } -} - -/* Loading state */ -.like-button .like-count:empty::after { - content: '...'; - animation: loadingDots 1.5s infinite; -} - -@keyframes loadingDots { - 0%, 20% { content: '.'; } - 40% { content: '..'; } - 60%, 100% { content: '...'; } -} - -/* Responsive adjustments */ -@media screen and (max-width: 768px) { - .like-button { - padding: 0.4rem 0.6rem; - font-size: 0.8rem; - gap: 0.4rem; - } - - .like-icon { - width: 14px; - height: 14px; - } - - .like-message { - font-size: 0.7rem; - padding: 0.4rem 0.6rem; - } -} - -/* High contrast mode support */ -@media (prefers-contrast: high) { - .like-button { - border-width: 3px; - } - - .like-button:hover { - background: #f0f0f0; - } -} \ No newline at end of file diff --git a/i/assets/static/patch_verfied.png b/i/assets/static/patch_verfied.png deleted file mode 100644 index 3c58b7a..0000000 Binary files a/i/assets/static/patch_verfied.png and /dev/null differ diff --git a/i/chethina.html b/i/chethina.html deleted file mode 100644 index 175b656..0000000 --- a/i/chethina.html +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - Chethina, Lead Contributor | OpenRockets Open-source Software Foundation - - - - - - -
- -
-
-
- profile_image -
-
-
-

Chethina Randidu

-
- FDN - - - - -
-
-
- -

Lead Contributor

- -
-
- -
-
-
- -
-
-
-
-

Profile

-
-
-
-
-
-

- Chethina is a lead contributor and innovative force at OpenRockets Open-source Software Foundation. As a passionate developer with expertise in full-stack technologies, Chethina bridges the gap between complex technical concepts and practical, user-friendly solutions. - With a keen eye for modern web development and emerging technologies, Chethina has spearheaded multiple critical projects that have enhanced the foundation's technical infrastructure. Their collaborative approach and mentorship skills have made them a cornerstone of the development team. - Beyond coding, Chethina actively contributes to the open-source community through technical documentation, code reviews, and fostering an inclusive environment where innovation thrives. Their commitment to quality and sustainable development practices continues to elevate the foundation's standards. -

-
- -
- - - -
-
- - diff --git a/i/menul.html b/i/menul.html deleted file mode 100644 index 0f2eeea..0000000 --- a/i/menul.html +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - Menul de Silva, Lead Contributor | OpenRockets Open-source Software Foundation - - - - - - -
- -
-
-
- profile_image -
-
-
-

Menul de Silva

-
- FDN - - - - -
-
-
- -

Contributor

- -
-
- -
-
-
- -
-
-
-
-

Profile

-
-
-
-
-
-

- Menul is an emerging contributor and rising talent at OpenRockets Open-source Software Foundation. As a young developer with fresh perspectives and boundless enthusiasm, Menul brings innovative ideas and modern approaches to traditional challenges. - With a natural aptitude for rapid learning and adaptation, Menul has quickly become proficient in cutting-edge technologies and development methodologies. Their curiosity-driven approach and willingness to explore new solutions have led to several breakthrough contributions. - Despite being newer to the field, Menul's collaborative spirit and dedication to continuous improvement make them a valuable team member. Their unique viewpoint and eagerness to tackle complex problems demonstrate the promising future of open-source development. -

-
- -
- - - -
-
- - diff --git a/i/mihitha.html b/i/mihitha.html deleted file mode 100644 index d2ff5fa..0000000 --- a/i/mihitha.html +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - Mihitha Pathberiya, Senior Developer | OpenRockets Open-source Software Foundation - - - - - -
- -
-
-
- profile_image -
-
-
-

Mihitha Pathberiya

- verified -
- FDN - - - -
-
-
-

Senior Developer, ML Specialist

-
-
-
-
-
- -
-
-
-
-

Profile

-
-
- - -

847

-
-
-
-

- Mihitha Pathberiya is a Senior Developer and Machine Learning specialist at OpenRockets Open-source Software Foundation. With a strong background in full-stack development and artificial intelligence, Mihitha has been pivotal in creating intelligent solutions that power the foundation's educational platforms. - - Mihitha's expertise spans across Python, JavaScript, and TensorFlow, making them a versatile contributor who can tackle complex problems from both frontend and backend perspectives. Their work on implementing machine learning algorithms for personalized learning experiences has revolutionized how students interact with OpenRockets' educational content. - - As a passionate advocate for accessible technology, Mihitha has led multiple initiatives to make AI and machine learning concepts more approachable for beginners. Their contributions include developing interactive tutorials, creating educational content, and mentoring junior developers in the open-source community. -

-
- -
- - -
-
-
-
-

Recent Articles

-
-
- - - -

Latest

-
-
-
-
-

Building Intelligent Learning Systems with Open Source

-

- The intersection of artificial intelligence and education has opened unprecedented opportunities for creating personalized learning experiences. At OpenRockets, we've been exploring how machine learning algorithms can adapt to individual learning patterns and provide customized educational pathways for students across different skill levels. -

- -

- One of the most exciting developments has been the implementation of natural language processing models that can understand and respond to student queries in real-time. By leveraging open-source frameworks like TensorFlow and PyTorch, we've created systems that can analyze student code, provide intelligent feedback, and suggest improvements - all while maintaining the privacy and security of user data. -

- -

- The beauty of open-source development lies in its collaborative nature. When we open-source our AI tools, we're not just sharing code - we're sharing knowledge, methodologies, and the collective wisdom of a community that believes in accessible education. Every contribution, no matter how small, adds to this growing ecosystem of intelligent educational tools. -

- -

- Let's build the future of education together. -

-
-
- -
-
- - - -
-
- - - diff --git a/i/navindu.html b/i/navindu.html deleted file mode 100644 index 5af6b79..0000000 --- a/i/navindu.html +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - Navindu Jayawardene, Full Stack Developer | OpenRockets Open-source Software Foundation - - - - - -
- -
-
-
- profile_image -
-
-
-

Navindu Jayawardene

- verified -
- FDN - - - -
-
-
-

Full Stack Developer, Open Source Advocate

-
-
-
-
-
- -
-
-
-
-

Profile

-
-
- - -

856

-
-
-
-

- Navindu Jayawardene is a Full Stack Developer and passionate Open Source Advocate who serves as a Foundation member at OpenRockets Open-source Software Foundation. With a comprehensive understanding of both frontend and backend technologies, Navindu creates cohesive, user-centered applications that bridge the gap between complex programming concepts and intuitive learning experiences. - - Navindu's expertise in React, Node.js, and database design has been crucial in developing OpenRockets' interactive learning modules and real-time collaboration features. Their holistic approach to development ensures that every component works seamlessly together, creating smooth user journeys from initial signup to advanced course completion. - - As a strong advocate for open source principles, Navindu actively mentors newcomers in the developer community and contributes to various educational technology projects. Their commitment to knowledge sharing extends beyond code contributions to include writing technical documentation, creating tutorials, and speaking at developer meetups about the importance of accessible programming education. -

-
- -
- - -
-
-
-
-

Recent Articles

-
-
- - - -

Latest

-
-
-
-
-

The Full Stack Mindset: Building Bridges Between Frontend and Backend

-

- In today's development landscape, being a full stack developer means more than just knowing both frontend and backend technologies - it's about understanding how to create cohesive experiences that serve users' needs while maintaining clean, scalable architecture. Through my journey with OpenRockets, I've learned that the most effective educational platforms are built by developers who think holistically about the entire user journey. -

- -

- One of our most successful projects involved rebuilding the interactive code editor to support real-time collaboration. This required seamless integration between React components on the frontend, WebSocket connections for real-time updates, and a Node.js backend that could handle concurrent editing sessions. The key was designing APIs that were intuitive for frontend consumption while remaining performant under heavy load. -

- -

- What excites me most about full stack development in education is the opportunity to create features that genuinely enhance learning. When a student runs their code and sees instant feedback, or when they collaborate with peers in real-time, they're experiencing the result of thoughtful full stack architecture that prioritizes pedagogical effectiveness alongside technical excellence. -

- -

- Code is poetry, but user experience is the story it tells. -

-
-
- -
-
- - - -
-
- - - diff --git a/i/neksha.html b/i/neksha.html deleted file mode 100644 index 5953efb..0000000 --- a/i/neksha.html +++ /dev/null @@ -1,196 +0,0 @@ - - - - - - Neksha DeSilva, Former Executive Director| OpenRockets Open-source Software Foundation - - - - -
- -
-
-
- profile_image -
-
-
-

Neksha DeSilva

- verified -
- FDN - - - -
-
-
-

Former Executive Director (Resigned) - Inactive

-
-
-
-
-
- -
-
-
-
-

Profile

-
-
- - -

1123

-
-
-
-

- Neksha DeSilva is the Former Executive Director (Resigned) of OpenRockets Open-source Software Foundation. With a passion for open-source software, Neksha was instrumental in shaping the foundation's vision and direction. Under their leadership, the foundation grew to become a hub for innovation and collaboration in the open-source community. - Neksha's expertise in software development and commitment to open-source principles were key drivers in the foundation's success. They played a pivotal role in fostering a culture of transparency, inclusivity, and community engagement within the organization. - As the founder and a lead contributor, Neksha guided the foundation's projects and initiatives, ensuring that they aligned with the core values of open-source software. Their leadership not only advanced the foundation's mission but also inspired countless individuals to contribute to the open-source ecosystem. -

-
- -
-
- -
-
-
-
-

Recent Articles

-
-
- - - -

Latest

-
-
-
-
-

Where is volunteering?

-

- You might see it on physical surfaces. In terms of tech, let's call them tangible outputs. We see them on whiteboards, blackboards, under trees, in gardens, crops, roadsides, seasides, highways, and so much more. But did you see the impact on abstraction? Did you see how humans turned the simple modern aircraft into the SpaceX Starlink constellation? Did you see how languages like Rust have immediately changed the era of computing to 'Artificial Intelligence?' Did you see how fast tasks with machine value, data transfers with trillions of operations can now happen without distraction? Yeah, you might think it's because there were people who invented it or were smart enough to invent them. But in fact, none of the above-mentioned things were developed without open source. Power of open source. -

-

- Today, for a valuable example, let's take Mark Zuckerberg, who has nearly open-sourced more than 15 major technologies which do not only help to fasten the user experience, efficiency, and the big topic which is trending nowadays, AI. But they also helped thousands of humans, especially learners and students, to browse and learn a significantly large amount of core and, at the same time, making a significantly big advantage for the entire humankind. Open-source development is not just a thing as an investment but rather, it's a kind of benefit that helps to change the address before you move further. -

-

- And I would like to address something very important: the impact of open-source contributions on newcomers. When you're new to a class, a grade, or even to a book, where do you look first? The syllabi, class contents, index, or the first page? That's the moment where they might say "Wow." So, that moment is called the first impression. In any subject, any grade, any task, no matter how hard or how deep the things are, humans are biologically made to learn every second of their lives. So the point is, how powerful are open-source developers have made a lasting foundation and such a good impression to our newcomers, as you can see the uninterpeted, relative bond between the growth of the world and the growth of technology. Unexpectedly great. -

-

- Thanks. -

-
-
- -
-
- - -
-
- - - diff --git a/i/senul.html b/i/senul.html deleted file mode 100644 index 0d3b86f..0000000 --- a/i/senul.html +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - Senul Withana, Backend Developer | OpenRockets Open-source Software Foundation - - - - - -
- -
-
-
- profile_image -
-
-
-

Senul Withana

- verified -
- EXT - - - -
-
-
-

Backend Developer, Systems Architect

-
-
-
-
-
- -
-
-
-
-

Profile

-
-
- - -

534

-
-
-
-

- Senul Withana is a Backend Developer and Systems Architect who serves as an External Contributor to OpenRockets Open-source Software Foundation. With a strong foundation in server-side technologies and database management, Senul designs and implements the robust infrastructure that supports thousands of learners on the OpenRockets platform. - - Senul's expertise in Node.js, Python, and cloud technologies has been instrumental in building scalable APIs and microservices that handle the complex requirements of modern educational platforms. Their work on implementing real-time collaboration features and optimizing database queries has significantly improved the platform's performance and user experience. - - As an external contributor, Senul brings fresh perspectives and industry best practices to the OpenRockets ecosystem. Their contributions include developing monitoring systems, implementing security protocols, and creating comprehensive documentation that helps other developers understand and contribute to the platform's backend architecture. -

-
- -
- - -
-
-
-
-

Recent Articles

-
-
- - - -

Latest

-
-
-
-
-

Building Scalable Educational APIs: Lessons from the Field

-

- When designing backend systems for educational platforms, scalability isn't just about handling more users - it's about maintaining performance while supporting diverse learning patterns, real-time collaboration, and data-intensive operations. Through my work with OpenRockets, I've learned that the key lies in thoughtful architecture decisions made early in the development process. -

- -

- One of our most successful implementations involved creating a microservices architecture that separates user management, content delivery, and assessment systems. By using Node.js for real-time features, Python for data processing, and Redis for caching, we achieved a 60% improvement in response times while supporting 10x more concurrent users than our previous monolithic system. -

- -

- The database optimization strategies we implemented include intelligent indexing, query optimization, and strategic use of read replicas for content that doesn't change frequently. These optimizations, combined with proper monitoring and alerting systems, ensure that learners always have a smooth experience regardless of platform load. -

- -

- Build for today, architect for tomorrow. -

-
-
- -
-
- - - -
-
- - - diff --git a/i/sketches/1616718303773.jpg b/i/sketches/1616718303773.jpg deleted file mode 100644 index f9f7f82..0000000 Binary files a/i/sketches/1616718303773.jpg and /dev/null differ diff --git a/i/sketches/216719080.png b/i/sketches/216719080.png deleted file mode 100644 index ace3cd1..0000000 Binary files a/i/sketches/216719080.png and /dev/null differ diff --git a/i/sketches/454-4543412_for-practitioners-supporting-young-people-moving-to-school.png b/i/sketches/454-4543412_for-practitioners-supporting-young-people-moving-to-school.png deleted file mode 100644 index 8cdd646..0000000 Binary files a/i/sketches/454-4543412_for-practitioners-supporting-young-people-moving-to-school.png and /dev/null differ diff --git a/i/sketches/design-guidelines.md b/i/sketches/design-guidelines.md deleted file mode 100644 index 947c9a4..0000000 --- a/i/sketches/design-guidelines.md +++ /dev/null @@ -1,41 +0,0 @@ -# OpenRockets Design Guidelines - -## Brand Identity -- **Primary Colors**: - - Rocket Blue: #1E3A8A - - Launch Orange: #F97316 - - Space Black: #0F172A - - Star White: #FFFFFF - -## Typography -- **Primary Font**: Space Grotesk (headers, titles) -- **Secondary Font**: Inter (body text, descriptions) -- **Accent Font**: Michroma (logos, special elements) - -## Logo Usage -- Maintain clear space around logo (minimum 24px) -- Use high contrast backgrounds -- Ensure accessibility with alt text - -## Illustration Style -- **Theme**: Educational technology, space exploration, community building -- **Style**: Modern, friendly, inclusive -- **Colors**: Consistent with brand palette -- **Subjects**: Students, developers, rockets, technology, collaboration - -## Image Assets -- **Profile Images**: 400x400px minimum -- **Hero Images**: 1920x1080px for full-width sections -- **Icons**: SVG format preferred, 24x24px base size -- **Illustrations**: PNG with transparency, high resolution - -## Accessibility Requirements -- All images must have descriptive alt text -- Maintain WCAG 2.1 AA color contrast ratios -- Support dark/light mode where applicable -- Ensure scalability for different screen sizes - -## File Naming Convention -- Use lowercase with hyphens: `student-coding-illustration.png` -- Include dimensions: `hero-banner-1920x1080.jpg` -- Version control: `logo-v2.svg` diff --git a/i/sketches/original-3fa57b946259b381c92d2fc9a819b746.gif b/i/sketches/original-3fa57b946259b381c92d2fc9a819b746.gif deleted file mode 100644 index eff6bdd..0000000 Binary files a/i/sketches/original-3fa57b946259b381c92d2fc9a819b746.gif and /dev/null differ diff --git a/i/sketches/pngtree-illustration-of-a-girl-wearing-a-hat-coloring-pages-vector-png-image_6781618.png b/i/sketches/pngtree-illustration-of-a-girl-wearing-a-hat-coloring-pages-vector-png-image_6781618.png deleted file mode 100644 index 5fc9290..0000000 Binary files a/i/sketches/pngtree-illustration-of-a-girl-wearing-a-hat-coloring-pages-vector-png-image_6781618.png and /dev/null differ diff --git a/i/tharusha.html b/i/tharusha.html deleted file mode 100644 index c666a28..0000000 --- a/i/tharusha.html +++ /dev/null @@ -1,184 +0,0 @@ - - - - - - Tharusha Rasanjana, Community Ambassador | OpenRockets Open-source Software Foundation - - - - - -
- -
-
-
- profile_image -
-
-
-

Tharusha Rasanjana

- verified -
- AMB - - - -
-
-
-

Community Ambassador, Content Creator

-
-
-
-
-
- -
-
-
-
-

Profile

-
-
- - -

1,247

-
-
-
-

- Tharusha Rasanjana serves as a Community Ambassador for OpenRockets Open-source Software Foundation, where they play a crucial role in building bridges between the platform and its diverse global community of learners. With exceptional interpersonal skills and a genuine passion for education, Tharusha creates welcoming spaces where newcomers can thrive and experienced developers can share their knowledge. - - Tharusha's expertise in community management and content creation has been instrumental in growing OpenRockets' social media presence and organizing virtual events that bring together thousands of learners from around the world. Their ability to simplify complex technical concepts makes programming accessible to beginners while maintaining depth for advanced learners. - - As an ambassador, Tharusha advocates for inclusive practices in tech education and works tirelessly to ensure that OpenRockets remains a platform where everyone feels valued and supported. Their initiatives include mentorship programs, diversity outreach efforts, and creating educational content that celebrates the achievements of underrepresented groups in technology. -

-
- -
- - -
-
-
-
-

Recent Articles

-
-
- - - -

Latest

-
-
-
-
-

Building Inclusive Communities in Tech Education

-

- Creating truly inclusive learning environments goes beyond just having diverse representation - it requires intentional effort to build systems and cultures that celebrate different perspectives, learning styles, and backgrounds. Through my work as a Community Ambassador at OpenRockets, I've seen firsthand how the right community dynamics can transform someone's entire relationship with technology. -

- -

- One of our most successful initiatives has been the "First Code Friday" program, where experienced developers share the story of their very first piece of code and what they learned from it. These sessions humanize the coding journey and show newcomers that everyone starts somewhere. The response has been overwhelming - we've had participants from 47 countries share their stories and connect with mentors. -

- -

- The key insight from our community building efforts is that representation matters at every level. When learners see people who look like them, share similar backgrounds, or have overcome similar challenges succeeding in tech, it breaks down barriers and builds confidence. This is why we've prioritized amplifying diverse voices in our content, events, and leadership opportunities. -

- -

- Every voice matters, every story counts. -

-
-
- -
-
- - - -
-
- - - diff --git a/i/thisitha.html b/i/thisitha.html deleted file mode 100644 index 8c39a94..0000000 --- a/i/thisitha.html +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - Thisitha Atapattu, DevOps Engineer | OpenRockets Open-source Software Foundation - - - - - -
- -
-
-
- profile_image -
-
-
-

Thisitha Atapattu

- verified -
- EXT - - - -
-
-
-

DevOps Engineer, Infrastructure Specialist

-
-
-
-
-
- -
-
-
-
-

Profile

-
-
- - -

428

-
-
-
-

- Thisitha Atapattu is a DevOps Engineer and Infrastructure Specialist serving as an External Contributor to OpenRockets Open-source Software Foundation. With deep expertise in cloud technologies, automation, and system reliability, Thisitha ensures that the educational platform operates seamlessly, providing uninterrupted learning experiences for thousands of students worldwide. - - Thisitha's mastery of Docker, Kubernetes, and CI/CD pipelines has been crucial in modernizing OpenRockets' deployment infrastructure. Their implementation of automated testing and deployment workflows has reduced deployment time by 75% while significantly improving system reliability and uptime. - - As an advocate for Infrastructure as Code and monitoring best practices, Thisitha has created comprehensive documentation and training materials that help other contributors understand and maintain the platform's complex infrastructure. Their proactive approach to system monitoring and incident response ensures that technical issues are resolved quickly, minimizing impact on learners. -

-
- -
- - -
-
-
-
-

Recent Articles

-
-
- - - -

Latest

-
-
-
-
-

Democratizing DevOps: Making Infrastructure Accessible to All Developers

-

- The traditional barrier between development and operations has been slowly dissolving, and nowhere is this more evident than in educational technology platforms. At OpenRockets, we've embraced a philosophy that every developer should understand and contribute to the infrastructure that runs their applications. This isn't just about breaking down silos - it's about empowering everyone to build more reliable, scalable solutions. -

- -

- Our journey toward infrastructure democracy began with containerizing our entire application stack using Docker. By creating standardized development environments that mirror production exactly, we eliminated the "it works on my machine" problem that had been plaguing our contributors. Now, whether you're a frontend developer or a backend specialist, you can spin up the entire platform locally with a single command. -

- -

- The real transformation came when we implemented GitOps practices using Kubernetes and Argo CD. Now, every code change triggers automated testing, building, and deployment processes that are transparent to all team members. This visibility has led to better collaboration and has helped developers understand the full lifecycle of their code from commit to production. -

- -

- Infrastructure is not just ops - it's everyone's responsibility. -

-
-
- -
-
- - - -
-
- - - diff --git a/i/thulana.html b/i/thulana.html deleted file mode 100644 index 708d3c5..0000000 --- a/i/thulana.html +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - Thulana Upek Damsiha, Frontend Developer | OpenRockets Open-source Software Foundation - - - - - -
- -
-
-
- profile_image -
-
-
-

Thulana Upek Damsiha

- verified -
- FDN - - - -
-
-
-

Frontend Developer, UX Designer

-
-
-
-
-
- -
-
-
-
-

Profile

-
-
- - -

692

-
-
-
-

- Thulana Upek Damsiha is a Frontend Developer and UX Designer at OpenRockets Open-source Software Foundation, bringing creativity and technical precision to every project. With a keen eye for detail and a deep understanding of user experience principles, Thulana creates intuitive interfaces that make learning programming accessible and enjoyable. - - Thulana's expertise in React, Vue.js, and modern CSS frameworks enables them to build responsive, performant web applications that work seamlessly across all devices. Their work on OpenRockets' interactive coding environments has revolutionized how students practice and learn programming concepts through hands-on exercises. - - As a passionate advocate for inclusive design, Thulana ensures that all interfaces are accessible to users with diverse needs and backgrounds. Their contributions to the open-source community include developing reusable UI components, creating design systems, and sharing best practices for frontend development through educational content and workshops. -

-
- -
- - -
-
-
-
-

Recent Articles

-
-
- - - -

Latest

-
-
-
-
-

The Art of Inclusive Design in Educational Technology

-

- Creating user interfaces for educational platforms requires more than just aesthetic appeal - it demands a deep understanding of how different learners interact with technology. Through my work at OpenRockets, I've discovered that inclusive design isn't just about accessibility compliance; it's about creating experiences that empower every learner to reach their full potential. -

- -

- One of our recent projects involved redesigning the code editor interface to support visual, auditory, and kinesthetic learning styles. By implementing features like syntax highlighting with customizable color schemes, audio feedback for code completion, and gesture-based navigation, we've seen a 40% improvement in user engagement across diverse learning groups. -

- -

- The React component library we've open-sourced includes over 50 accessible components that other educational platforms can use freely. Each component follows WCAG 2.1 guidelines and includes comprehensive documentation with real-world examples. This contribution has helped standardize inclusive design practices across the educational technology ecosystem. -

- -

- Design with empathy, code with purpose. -

-
-
- -
-
- - - -
-
- - - diff --git a/i/vidul.html b/i/vidul.html deleted file mode 100644 index 01c9c41..0000000 --- a/i/vidul.html +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - Vidul, Lead Contributor | OpenRockets Open-source Software Foundation - - - - - - -
- -
-
-
- profile_image -
-
-
-

Vidul Batawala

-
- FDN - - - - -
-
-
- -

Lead Contributor

- -
-
- -
-
-
- -
-
-
-
-

Profile

-
-
-
-
-
-

- Vidul is a lead contributor and technical architect at OpenRockets Open-source Software Foundation. With a strong background in system design and software engineering, Vidul excels at creating scalable solutions that power the foundation's most ambitious projects. - Known for their analytical problem-solving approach and deep understanding of distributed systems, Vidul has been instrumental in optimizing performance and reliability across the foundation's infrastructure. Their expertise spans multiple programming languages and modern development frameworks. - As a mentor and technical leader, Vidul champions best practices in software development while encouraging experimentation and learning. Their dedication to clean code architecture and comprehensive testing has established new standards that benefit the entire development community. -

-
- -
- - - -
-
- - diff --git a/i/viruna.html b/i/viruna.html deleted file mode 100644 index 73725c1..0000000 --- a/i/viruna.html +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - Viruna Cooray, Quality Assurance Engineer | OpenRockets Open-source Software Foundation - - - - - -
- -
-
-
- profile_image -
-
-
-

Viruna Cooray

- verified -
- EXT - - - -
-
-
-

QA Engineer, Testing Specialist

-
-
-
-
-
- -
-
-
-
-

Profile

-
-
- - -

613

-
-
-
-

- Viruna Cooray is a Quality Assurance Engineer and Testing Specialist serving as an External Contributor to OpenRockets Open-source Software Foundation. With a meticulous attention to detail and deep understanding of user experience principles, Viruna ensures that every feature and function of the educational platform meets the highest standards of quality and reliability. - - Viruna's expertise in automated testing frameworks, performance testing, and accessibility validation has been instrumental in maintaining OpenRockets' reputation for excellence. Their comprehensive testing strategies encompass everything from unit tests to end-to-end user journey validation, ensuring that learners have consistently smooth and reliable experiences across all platform features. - - As a champion for quality-first development practices, Viruna has established testing protocols that have reduced production bugs by 80% and improved user satisfaction scores significantly. Their work on accessibility testing ensures that OpenRockets' educational content is truly inclusive and usable by learners with diverse abilities and needs. -

-
- -
- - -
-
-
-
-

Recent Articles

-
-
- - - -

Latest

-
-
-
-
-

Quality is Not a Feature, It's a Foundation: Lessons in Educational Platform Testing

-

- In the world of educational technology, quality assurance isn't just about finding bugs - it's about ensuring that every interaction a learner has with the platform empowers them to achieve their goals. Through my work with OpenRockets, I've learned that effective QA for educational platforms requires a unique blend of technical rigor and pedagogical understanding. -

- -

- One of our most impactful testing initiatives involved creating user personas that represent different learning styles, technical backgrounds, and accessibility needs. By testing every feature against these diverse personas, we uncovered usability issues that traditional testing approaches might have missed. This human-centered testing approach has resulted in a 45% increase in course completion rates. -

- -

- Our automated testing suite now includes over 2,000 test cases covering everything from basic functionality to complex learning scenarios. But what sets our approach apart is the integration of learning analytics into our testing process. We don't just test that features work - we test that they contribute to positive learning outcomes. This means validating that interactive exercises actually help students understand concepts, not just that they execute without errors. -

- -

- Quality enables learning, learning changes lives. -

-
-
- -
-
- - - -
-
- - - diff --git a/in.html b/in.html deleted file mode 100644 index 14dfc2f..0000000 --- a/in.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - OpenRockets India - Open Source Programming Community - - - - - - - - - - - - - - -
-
-
-
-
- -
-
- -
-
- -
-
-
-
-

- OpenRockets India -

-

Empowering India's Open Source Future.

- -
-
-
-
- - -
-
- - - - - \ No newline at end of file diff --git a/index-new.html b/index-new.html deleted file mode 100644 index e69de29..0000000 diff --git a/index-old.html b/index-old.html deleted file mode 100644 index 9f0bce9..0000000 --- a/index-old.html +++ /dev/null @@ -1,862 +0,0 @@ - - - - - - OpenRockets - Build the Future Together - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - Solve issues in our GitHub repositories and earn rewards. -
- -

- We build the future - together -

- - - -
- - -
- Student builder - Young developers -
-
- - -
-
-
-
- -
-

HackClub DayDream Sri Lanka

- Coming September 27th -
-
LIVE EVENT
-
- -
-

- 🎮 Create amazing games in just 24 hours! Join developers across Colombo and various locations - throughout Sri Lanka for an epic game development hackathon inspired by HackClub's global maker community. -

- -
-

What is HackClub DayDream?

-

DayDream is a 24-hour game development jam where teenage developers and makers come together to create incredible games from scratch. Inspired by HackClub's philosophy of learning by building, this event encourages participants to explore game development, experiment with new tools, and collaborate with fellow builders.

- -

What You'll Do:

-
    -
  • 🎯 Form teams with other developers (or work solo)
  • -
  • 🎨 Brainstorm and design your game concept
  • -
  • ⚡ Build your game using Unity, Unreal Engine, Scratch, JavaScript, or any tool you prefer
  • -
  • 🎵 Create graphics, sounds, and gameplay mechanics
  • -
  • 🏆 Present your creation to judges and fellow participants
  • -
  • 🎉 Win awesome prizes and gain recognition in the community
  • -
- -

Perfect For:

-
    -
  • 🔰 Beginners: New to coding? We'll provide tutorials and mentors!
  • -
  • 📈 Intermediate: Ready to try new frameworks and tools
  • -
  • 🚀 Advanced: Push the boundaries with complex game mechanics
  • -
-
- -
-
- - Colombo & Multiple Locations, Sri Lanka -
-
- - 24-Hour Game Jam Challenge -
-
- - Open to All Skill Levels -
-
- - Unity, Unreal, JavaScript & Any Game Engine -
-
- - Prizes, Recognition & Portfolio Building -
-
- - Live Community Support & Updates -
-
- - -
-
-
-
- - -
-
-
- 31+ - GitHub Followers -
-
- 18+ - Open Repositories -
-
- 2.9k+ - GitHub Stars -
-
- 24/7 - Active Community -
-
-
- - -
- - -
-
-
-

I'M A BUILDER

-

Qualifications which unlock an amazing future in the tech world through open-source contributions and collaborative development.

- -
-
-
-
-

I'M AN EMPLOYER

-

Support young people by hiring contributors, volunteering, or making partnerships with OpenRockets community.

- -
-
-
- - -
-
-

With a network of contributors from

-

Universities, High schools and Secondary schools from Sri Lanka, India and Maldives.

-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
-
-

Build Amazing Things

-

- Whether you're just starting out or you're a seasoned developer, - OpenRockets provides everything you need to create and collaborate on open-source projects. -

-
- -
-
-
- Modern Development -
-
-
- DEVELOPMENT -
-

Open Source Projects

-

- Contribute to real-world projects like OpenSocial, our comprehensive social media platform, - and web-dev-error-solutions with 2.9k+ GitHub stars. -

- - View Projects - -
-
- -
-
- -
-
-
- COMMUNITY -
-

Global Community

-

- Connect with builders from Sri Lanka and around the world. Share knowledge, - get feedback, and collaborate on innovative solutions. -

- - Join Community - -
-
- -
-
- Learning Resources -
-
-
- LEARNING -
-

Learn by Building

-

- Master new technologies through hands-on projects. From JavaScript to complex - web applications, learn with real-world experience. -

- - Start Learning - -
-
- -
-
- -
-
-
- EVENTS -
-

Hackathons & Events

-

- Participate in coding competitions, workshops, and hackathons. Win prizes, - gain recognition, and showcase your skills to the community. -

- - View Events - -
-
- -
-
- Developer Tools -
-
-
- TOOLS -
-

Modern Tools

-

- Access cutting-edge development tools, GitHub integration, and - infrastructure to bring your innovative ideas to life. -

- - Explore Tools - -
-
- -
-
- -
-
-
- REWARDS -
-

Recognition & Rewards

-

- Top contributors receive free domain names, full project ownership, - and media recognition. Build your reputation in the open-source community. -

- - Get Recognized - -
-
-
-
-
-
-
-

- In partnetship with, -

-
-
- - - - -
-
-
- -
-
-
-
-
- -
-
- "OpenRockets is cool, I'm wodering how they could pitch this innovative idea in Sri Lanka, This is a whole new ecosystem. When I leave Sri Lanka at 12, it was so different. I hope to visit Sri Lanka again." -
-
-

Ranithu Raddolugama

-

Seattle, Washington, United States

-
-
-
- Community -
-
-
-
- - -
- - -
-
-
-

LATEST NEWS AND FEATURES

-

- Check out some of the incredible open-source projects our community has built and maintains. -

-
- View all -
- -
-
-
- OpenSocial Platform -
-
-
- PROJECT - • SEPTEMBER 21 2025 -
-

OpenSocial Platform Remains Top for 4th Year in a row!

-

- Build Sri Lanka's most comprehensive open-source social media platform! - Top 3 contributors receive free domain names and full platform ownership. -

- - Open Source - -
-
- -
-
- Web Development -
-
-
- COMMUNITY STORY - • SEPTEMBER 18 2025 -
-

Web Dev Error Solutions – Community Spotlight

-

- A community-curated handbook with 2,948+ GitHub stars for common web development - errors. Find fixes for JavaScript, React, Node.js, CSS, and more! -

- - Community - -
-
- -
-
- CityofGits Game -
-
-
- PROJECT - • AUGUST 15 2025 -
-

CityOfGits

-

- CityofGits was created by Neksha DeSilva, with partnership with OpenRockets Founadation. You must play it! -

- - Projects - -
-
-
-
- - -
-
-

Upcoming Events

-

- Join our exciting events, hackathons, and game jams happening around Sri Lanka and online. -

-
- -
- - -
-
- -
-

Weekly Coding Sessions

-

- Join our weekly coding sessions where we work on open-source projects, - share knowledge, and help each other grow as developers. -

- - View Calendar - -
- -
-
- -
-

Contribution Challenges

-

- Take part in our monthly contribution challenges. Solve GitHub issues, - win prizes, and get recognition in the developer community. -

- - View Challenges - -
-
-
- - -
-
-

Join Our Community

-

- Connect with builders from around the world and collaborate on amazing open-source projects. -

-
- -
-
-
- -
-

Discord Server

-

- Join our active Discord community where developers collaborate, share knowledge, - and get help with coding challenges 24/7. -

- - Join Discord - -
- -
-
- -
-

GitHub Organization

-

- Contribute to our 18+ repositories, create pull requests, and collaborate - with developers worldwide on innovative projects. -

- - View Organization - -
- -
-
- -
-

Official Contact

-

- Reach out for partnerships, collaborations, or general inquiries. - We're always open to new opportunities and connections. -

- - Contact Us - -
-
-
- - -
-
-

Latest Updates

-

- Stay up to date with OpenRockets announcements, project updates, and community highlights. -

-
- -
-
- - - -
-
-
- - -
- - - - - -
-
-

Ready to Start Building?

-

- Join 31+ followers on GitHub and contribute to innovative open-source projects. - Level up, Challenge, Grow, Contribute with OpenRockets. -

- -
-
- - -
-
- - -
-
- - - - - - -
-
- Parrot Assistant -
-
- Welcome to OpenRockets! 🚀 -
-
-
-
Tips
-
- -
- - - - - - - diff --git a/index.html b/index.html index 0cc9955..860ea0f 100644 --- a/index.html +++ b/index.html @@ -15,10 +15,9 @@
- -

OpenRockets Foundation

+

A registered 501(c)(3) nonprofit organization advancing open source software, education, and innovation.

- Join Us + Join Us
@@ -64,8 +63,8 @@

Our Partners

+ +
+
+

The Pioneers

+

Learn about our founding story

+
+ +
+
+
+
@@ -85,30 +95,28 @@

Committee

2026

- Alex Chen (Chair) - Priya Sharma (Vice Chair) - James Wilson (Secretary) - Maria Santos (Treasurer) + Vinuk Gamaarachige (Executive Director) + Thisal Nethnidu (Technical Host) + Navindu Jayawardene (Director of Operations) + M. Wickramasinghe (Financials)

2025

- Neksha De Silva (Chair) - David Park (Vice Chair) - Sarah Johnson (Secretary) - Raj Patel (Treasurer) + Neksha DeSilva (Executive Director) + Chethina Rnadidu (Lead Contributor) + Navindu Jayawardene (Lead Contributor) + Vidul Batawala (Lead Contributor) + Menula de Silva (Lead Contributor)

2024

- Michael Brown (Chair) - Emma Davis (Vice Chair) - Chris Lee (Secretary) - Anna Kim (Treasurer) + Neksha DeSilva (Founder)
diff --git a/landing.html b/landing.html deleted file mode 100644 index b142928..0000000 --- a/landing.html +++ /dev/null @@ -1,312 +0,0 @@ - - - - - - OpenRockets LMS - Learn. Code. Grow. - - - - - - - - - -
- - - - -
-
-
-

Welcome to the Future of Programming Education

-

A comprehensive Learning Management System designed for programming education. Connect with expert lecturers, join interactive classes, and grow your coding skills in a collaborative environment.

-
- - Watch Demo -
-
-
- 10K+ - Active Students -
-
- 500+ - Expert Lecturers -
-
- 1K+ - Programming Courses -
-
-
-
-
-
-
- - Upcoming Classes -
-
-
09:00 AM
-
-
Advanced JavaScript
-
Dr. Sarah Chen
-
-
-
-
02:00 PM
-
-
Python for AI
-
Prof. Alex Kumar
-
-
-
- -
-
- - Community -
-
- -
Just completed the React course! 🚀
-
-
- -
-
- - Your Progress -
-
-
-
-
JavaScript Fundamentals - 75%
-
-
- - -
-
-
-
-
-
-
-
-
-
-
-
-
-
- - -
-
-
-

Powerful Features for Modern Learning

-

Everything you need to learn, teach, and collaborate in programming education.

-
-
-
-
- -
-

Smart Calendar

-

Microsoft Teams-inspired calendar system for scheduling and joining programming classes seamlessly.

-
-
-
- -
-

Interactive Classes

-

Join live coding sessions, workshops, and lectures with screen sharing and real-time collaboration.

-
-
-
- -
-

Vibrant Community

-

Connect with fellow learners, share projects, ask questions, and collaborate on coding challenges.

-
-
-
- -
-

Code Editor

-

Built-in code editor with syntax highlighting, real-time collaboration, and instant feedback.

-
-
-
- -
-

Progress Tracking

-

Track your learning journey with detailed analytics, achievements, and personalized recommendations.

-
-
-
- -
-

Expert Instructors

-

Learn from industry professionals and experienced educators in various programming languages.

-
-
-
-
- - -
-
-
-

Master Popular Programming Languages

-

Comprehensive courses in the most in-demand programming languages and technologies.

-
-
-
-
- -
-

JavaScript

-

Frontend and backend development

-
-
-
- -
-

Python

-

Data science and AI development

-
-
-
- -
-

C++

-

Systems programming and algorithms

-
-
-
- -
-

React

-

Modern web application development

-
-
-
- -
-

Node.js

-

Server-side JavaScript development

-
-
-
- -
-

AI/ML

-

Artificial Intelligence and Machine Learning

-
-
-
-
- - -
-
-
-

Ready to Start Your Programming Journey?

-

Join thousands of students and instructors in our vibrant learning community.

- -
-
-
- - - -
- - - - - - diff --git a/learn.html b/learn.html deleted file mode 100644 index 59d1acc..0000000 --- a/learn.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - OpenRockets | Upcoming Events - - - - - - \ No newline at end of file diff --git a/opennetwork-banner-demo.html b/opennetwork-banner-demo.html deleted file mode 100644 index e36c270..0000000 --- a/opennetwork-banner-demo.html +++ /dev/null @@ -1,231 +0,0 @@ - - - - - - OpenNetwork Banner Demo - - - -
-

🚀 OpenNetwork Banner Demo

- -
-

Welcome to the OpenNetwork Collective Banner demonstration!

- -

This advanced JavaScript banner automatically displays at the bottom of any website that includes it, showing registration information and system status.

- -

Features:

-
    -
  • Auto-detects the website domain name
  • -
  • Displays USA flag logo from assets
  • -
  • Links to OpenNetwork Collective affiliate page
  • -
  • Real-time system status indicator
  • -
  • Intelligent marquee scrolling for text overflow
  • -
  • Responsive design for all screen sizes
  • -
  • Smooth animations and hover effects
  • -
  • Backdrop blur and modern glassmorphism design
  • -
  • Auto-initialization on page load
  • -
  • Public API for programmatic control
  • -
- -

How to use:

-

Simply include the script in any website:

- -
-<script src="https://openrockets.com/scripts/opennetwork-banner.js"></script> -
- -

The banner will automatically appear and detect the current domain. It's that simple!

- -

Demo Controls:

-
- - - - - -
- -
- Current Status: All Systems Operational -
- -

Advanced Configuration:

-
-// Access the banner API -window.OpenNetworkBanner.config.position = 'top'; // or 'bottom' -window.OpenNetworkBanner.config.animationDuration = 10000; // milliseconds -window.OpenNetworkBanner.updateStatus('operational', 'Custom Status Message'); -
- -

The banner below is live and functional! Try resizing your window to see the responsive behavior and text overflow handling.

-
-
- - - - - - - diff --git a/opennetwork-injector-demo.html b/opennetwork-injector-demo.html deleted file mode 100644 index e69de29..0000000 diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index b7c8415..0000000 --- a/package-lock.json +++ /dev/null @@ -1,1629 +0,0 @@ -{ - "name": "openrockets-lms", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "openrockets-lms", - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@datastax/astra-db-ts": "^1.5.0", - "bcryptjs": "^2.4.3", - "cors": "^2.8.5", - "dotenv": "^16.6.1", - "express": "^4.21.2", - "jsonwebtoken": "^9.0.2", - "multer": "^1.4.5-lts.1", - "socket.io": "^4.8.1", - "uuid": "^11.1.0" - }, - "devDependencies": { - "nodemon": "^3.0.2" - } - }, - "node_modules/@datastax/astra-db-ts": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@datastax/astra-db-ts/-/astra-db-ts-1.5.0.tgz", - "integrity": "sha512-Z9pEVyyHfglh8XAKrIASxdvORdei4pLUKDDGarqYvBkA9B9rKdqqdN+4I42Dz8paU5uscu8FwM5mc+Ly/U6jfA==", - "dependencies": { - "fetch-h2": "^3.0.2", - "safe-stable-stringify": "^2.4.3", - "typed-emitter": "^2.1.0", - "uuidv7": "^0.6.3" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "license": "MIT" - }, - "node_modules/@types/cors": { - "version": "2.8.19", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "24.2.1", - "license": "MIT", - "dependencies": { - "undici-types": "~7.10.0" - } - }, - "node_modules/@types/tough-cookie": { - "version": "4.0.5", - "license": "MIT" - }, - "node_modules/accepts": { - "version": "1.3.8", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/already": { - "version": "2.2.1", - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/append-field": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "license": "MIT" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/base64id": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, - "node_modules/bcryptjs": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/body-parser": { - "version": "1.20.3", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "license": "BSD-3-Clause" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "license": "MIT" - }, - "node_modules/busboy": { - "version": "1.6.0", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callguard": { - "version": "2.0.0", - "license": "MIT" - }, - "node_modules/chokidar": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "engines": [ - "node >= 0.8" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.1", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "license": "MIT" - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/engine.io": { - "version": "6.6.4", - "license": "MIT", - "dependencies": { - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.7.2", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/cookie": { - "version": "0.7.2", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/debug": { - "version": "4.3.7", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io/node_modules/ms": { - "version": "2.1.3", - "license": "MIT" - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/fetch-h2": { - "version": "3.0.2", - "license": "MIT", - "dependencies": { - "@types/tough-cookie": "^4.0.0", - "already": "^2.2.1", - "callguard": "^2.0.0", - "get-stream": "^6.0.1", - "through2": "^4.0.2", - "to-arraybuffer": "^1.0.1", - "tough-cookie": "^4.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "dev": true, - "license": "ISC" - }, - "node_modules/inherits": { - "version": "2.0.4", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jsonwebtoken/node_modules/ms": { - "version": "2.1.3", - "license": "MIT" - }, - "node_modules/jwa": { - "version": "1.4.2", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "license": "MIT", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "license": "MIT" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "license": "MIT" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "license": "MIT" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "license": "MIT" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "license": "MIT" - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "license": "MIT" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "license": "MIT" - }, - "node_modules/multer": { - "version": "1.4.5-lts.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", - "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==", - "deprecated": "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/negotiator": { - "version": "0.6.3", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/nodemon": { - "version": "3.1.10", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^4", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^7.5.3", - "simple-update-notifier": "^2.0.0", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/nodemon/node_modules/debug": { - "version": "4.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/nodemon/node_modules/ms": { - "version": "2.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "license": "MIT" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "license": "MIT" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/psl": { - "version": "1.15.0", - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "funding": { - "url": "https://github.com/sponsors/lupomontero" - } - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "dev": true, - "license": "MIT" - }, - "node_modules/punycode": { - "version": "2.3.1", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.13.0", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "license": "MIT" - }, - "node_modules/range-parser": { - "version": "1.2.1", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/readable-stream": { - "version": "2.3.8", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "license": "MIT" - }, - "node_modules/readdirp": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/rxjs": { - "version": "7.8.2", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safe-stable-stringify": { - "version": "2.5.0", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.2", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.0", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.16.2", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "license": "ISC" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/simple-update-notifier": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/socket.io": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", - "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.6.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.5.5", - "license": "MIT", - "dependencies": { - "debug": "~4.3.4", - "ws": "~8.17.1" - } - }, - "node_modules/socket.io-adapter/node_modules/debug": { - "version": "4.3.7", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-adapter/node_modules/ms": { - "version": "2.1.3", - "license": "MIT" - }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.7", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-parser/node_modules/ms": { - "version": "2.1.3", - "license": "MIT" - }, - "node_modules/socket.io/node_modules/debug": { - "version": "4.3.7", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io/node_modules/ms": { - "version": "2.1.3", - "license": "MIT" - }, - "node_modules/statuses": { - "version": "2.0.1", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "license": "MIT" - }, - "node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/through2": { - "version": "4.0.2", - "license": "MIT", - "dependencies": { - "readable-stream": "3" - } - }, - "node_modules/through2/node_modules/readable-stream": { - "version": "3.6.2", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/to-arraybuffer": { - "version": "1.0.1", - "license": "MIT" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/touch": { - "version": "3.1.1", - "dev": true, - "license": "ISC", - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, - "node_modules/tough-cookie": { - "version": "4.1.4", - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "license": "0BSD", - "optional": true - }, - "node_modules/type-is": { - "version": "1.6.18", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typed-emitter": { - "version": "2.1.0", - "license": "MIT", - "optionalDependencies": { - "rxjs": "*" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "license": "MIT" - }, - "node_modules/undefsafe": { - "version": "2.0.5", - "dev": true, - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "7.10.0", - "license": "MIT" - }, - "node_modules/universalify": { - "version": "0.2.0", - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/url-parse": { - "version": "1.5.10", - "license": "MIT", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/uuidv7": { - "version": "0.6.3", - "license": "Apache-2.0", - "bin": { - "uuidv7": "cli.js" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/ws": { - "version": "8.17.1", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 2605e16..0000000 --- a/package.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "openrockets-lms", - "version": "1.0.0", - "description": "OpenRockets Learning Management System with AstraDB backend", - "main": "server.js", - "scripts": { - "start": "node server.js", - "dev": "nodemon server.js", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "dependencies": { - "@datastax/astra-db-ts": "^1.5.0", - "bcryptjs": "^2.4.3", - "cors": "^2.8.5", - "dotenv": "^16.6.1", - "express": "^4.21.2", - "jsonwebtoken": "^9.0.2", - "multer": "^1.4.5-lts.1", - "socket.io": "^4.8.1", - "uuid": "^11.1.0" - }, - "devDependencies": { - "nodemon": "^3.0.2" - }, - "keywords": [ - "lms", - "education", - "nodejs", - "astradb", - "programming" - ], - "author": "OpenRockets Team", - "license": "MIT", - "repository": { - "type": "git", - "url": "git+https://github.com/NekshaDeSilva/openrockets.com.git" - }, - "bugs": { - "url": "https://github.com/NekshaDeSilva/openrockets.com/issues" - }, - "homepage": "https://github.com/NekshaDeSilva/openrockets.com#readme" -} diff --git a/privacy/transcripts/daydream-srilanka-2025.pdf b/privacy/transcripts/daydream-srilanka-2025.pdf deleted file mode 100644 index 1a3419e..0000000 Binary files a/privacy/transcripts/daydream-srilanka-2025.pdf and /dev/null differ diff --git a/privacy/transcripts/readme.md b/privacy/transcripts/readme.md deleted file mode 100644 index 988ece5..0000000 --- a/privacy/transcripts/readme.md +++ /dev/null @@ -1 +0,0 @@ -https://openrockets.com/privacy/transcripts/daydream-srilanka-2025.pdf diff --git a/reg/21.html b/reg/21.html deleted file mode 100644 index 1cfbce3..0000000 --- a/reg/21.html +++ /dev/null @@ -1,525 +0,0 @@ - - - - - - POML Hands-on by OpenRockets | OpenRockets - - - - - - - - -
- -
- OpenRockets Logo -

POML Hands-on by OpenRockets

-

Prompt like a Pro. Get into the latest cutting edge research by Microsoft

-

Join us on Microsoft Teams, LIVE from Colombo, Sri Lanka.

-
- - -
-

📅 Session Details

-
-
-
📅
- Date: September 5th, 2025 -
-
-
🕒
- Time: 2:00 PM - 5:00 PM GMT+5:30 -
-
-
🌐
- Mode: Online (Teams) -
-
-
💰
- Fee: Free for everyone. -
-
-

- The joining link will be sent to your WhatsApp and email. -

-
- - -
-

Register for Microsoft POML Session

- -
- -
- -
- - -
-
- - -
- - -
-
- - -
- - - Enter your GitHub username (without @) -
-
- - - -
-
- - - - - - - - - -
- - - - - diff --git a/rsvp-srilanka.html b/rsvp-srilanka.html deleted file mode 100644 index f0e97c5..0000000 --- a/rsvp-srilanka.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - Redirecting... - - - - Copyrights (c) 2025 OpenRockets Open-source Software Foundation. All Rights Reserved. -
- Redirecting.. - - \ No newline at end of file diff --git a/scripts/api.js b/scripts/api.js deleted file mode 100644 index 451424a..0000000 --- a/scripts/api.js +++ /dev/null @@ -1,608 +0,0 @@ -// API Configuration and Helper Functions -class OpenRocketsAPI { - constructor() { - this.baseURL = window.location.origin; - this.token = localStorage.getItem('authToken'); - this.currentUser = JSON.parse(localStorage.getItem('currentUser') || 'null'); - } - - // Set authentication token - setToken(token) { - this.token = token; - localStorage.setItem('authToken', token); - } - - // Remove authentication token - removeToken() { - this.token = null; - localStorage.removeItem('authToken'); - localStorage.removeItem('currentUser'); - } - - // Set current user - setCurrentUser(user) { - this.currentUser = user; - localStorage.setItem('currentUser', JSON.stringify(user)); - } - - // Get authorization headers - getAuthHeaders() { - const headers = { - 'Content-Type': 'application/json' - }; - - if (this.token) { - headers['Authorization'] = `Bearer ${this.token}`; - } - - return headers; - } - - // Make authenticated API request - async request(endpoint, options = {}) { - const url = `${this.baseURL}${endpoint}`; - const config = { - headers: this.getAuthHeaders(), - ...options - }; - - try { - const response = await fetch(url, config); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.error || `HTTP ${response.status}`); - } - - const contentType = response.headers.get('content-type'); - if (contentType && contentType.includes('application/json')) { - return await response.json(); - } - - return await response.text(); - } catch (error) { - console.error('API Request Error:', error); - throw error; - } - } - - // Authentication methods - async register(userData, profileImage = null) { - const formData = new FormData(); - - Object.keys(userData).forEach(key => { - formData.append(key, userData[key]); - }); - - if (profileImage) { - formData.append('profileImage', profileImage); - } - - const response = await fetch(`${this.baseURL}/api/auth/register`, { - method: 'POST', - body: formData - }); - - if (!response.ok) { - const errorData = await response.json(); - throw new Error(errorData.error || 'Registration failed'); - } - - const data = await response.json(); - this.setToken(data.token); - this.setCurrentUser(data.user); - - return data; - } - - async login(email, password) { - const response = await this.request('/api/auth/login', { - method: 'POST', - body: JSON.stringify({ email, password }) - }); - - this.setToken(response.token); - this.setCurrentUser(response.user); - - return response; - } - - async logout() { - this.removeToken(); - window.location.href = '/'; - } - - async getProfile() { - return await this.request('/api/auth/profile'); - } - - async updateProfile(profileData, profileImage = null) { - const formData = new FormData(); - - Object.keys(profileData).forEach(key => { - if (profileData[key] !== undefined && profileData[key] !== null) { - formData.append(key, profileData[key]); - } - }); - - if (profileImage) { - formData.append('profileImage', profileImage); - } - - const response = await fetch(`${this.baseURL}/api/auth/profile`, { - method: 'PUT', - headers: { - 'Authorization': `Bearer ${this.token}` - }, - body: formData - }); - - if (!response.ok) { - const errorData = await response.json(); - throw new Error(errorData.error || 'Profile update failed'); - } - - return await response.json(); - } - - // Posts methods - async createPost(postData, images = []) { - const formData = new FormData(); - - Object.keys(postData).forEach(key => { - formData.append(key, postData[key]); - }); - - images.forEach((image, index) => { - formData.append('images', image); - }); - - const response = await fetch(`${this.baseURL}/api/posts`, { - method: 'POST', - headers: { - 'Authorization': `Bearer ${this.token}` - }, - body: formData - }); - - if (!response.ok) { - const errorData = await response.json(); - throw new Error(errorData.error || 'Post creation failed'); - } - - return await response.json(); - } - - async getPosts(filters = {}) { - const params = new URLSearchParams(filters); - return await this.request(`/api/posts?${params}`); - } - - async getPost(postId) { - return await this.request(`/api/posts/${postId}`); - } - - async likePost(postId) { - return await this.request(`/api/posts/${postId}/like`, { - method: 'POST' - }); - } - - async addComment(postId, content) { - return await this.request(`/api/posts/${postId}/comments`, { - method: 'POST', - body: JSON.stringify({ content }) - }); - } - - async getComments(postId) { - return await this.request(`/api/posts/${postId}/comments`); - } - - // Events methods - async createEvent(eventData) { - return await this.request('/api/events', { - method: 'POST', - body: JSON.stringify(eventData) - }); - } - - async getEvents(filters = {}) { - const params = new URLSearchParams(filters); - return await this.request(`/api/events?${params}`); - } - - async joinEvent(eventId) { - return await this.request(`/api/events/${eventId}/join`, { - method: 'POST' - }); - } - - // Messages methods - async sendMessage(recipientId, content, type = 'text') { - return await this.request('/api/messages', { - method: 'POST', - body: JSON.stringify({ recipientId, content, type }) - }); - } - - async getMessages(userId) { - return await this.request(`/api/messages/${userId}`); - } - - // Notifications methods - async getNotifications() { - return await this.request('/api/notifications'); - } - - async markNotificationRead(notificationId) { - return await this.request(`/api/notifications/${notificationId}/read`, { - method: 'PATCH' - }); - } - - // Search methods - async search(query, type = 'all') { - const params = new URLSearchParams({ q: query, type }); - return await this.request(`/api/search?${params}`); - } - - // Community API Methods - async getPosts(filters = {}) { - const params = new URLSearchParams(filters); - return await this.request(`/api/community/posts?${params}`); - } - - async createPost(postData, imageFiles = []) { - const formData = new FormData(); - formData.append('data', JSON.stringify(postData)); - - imageFiles.forEach((file, index) => { - formData.append(`image_${index}`, file); - }); - - return await this.request('/api/community/posts', { - method: 'POST', - body: formData, - headers: { - 'Authorization': this.token ? `Bearer ${this.token}` : '' - } - }); - } - - async likePost(postId) { - return await this.request(`/api/community/posts/${postId}/like`, { - method: 'POST' - }); - } - - async getComments(postId) { - return await this.request(`/api/community/posts/${postId}/comments`); - } - - async addComment(postId, content) { - return await this.request(`/api/community/posts/${postId}/comments`, { - method: 'POST', - body: JSON.stringify({ content }) - }); - } - - // Utility methods - isAuthenticated() { - return !!this.token && !!this.currentUser; - } - - requireAuth() { - if (!this.isAuthenticated()) { - window.location.href = '/'; - return false; - } - return true; - } - - formatDate(dateString) { - const date = new Date(dateString); - const now = new Date(); - const diffInSeconds = Math.floor((now - date) / 1000); - - if (diffInSeconds < 60) { - return 'Just now'; - } else if (diffInSeconds < 3600) { - const minutes = Math.floor(diffInSeconds / 60); - return `${minutes} minute${minutes > 1 ? 's' : ''} ago`; - } else if (diffInSeconds < 86400) { - const hours = Math.floor(diffInSeconds / 3600); - return `${hours} hour${hours > 1 ? 's' : ''} ago`; - } else if (diffInSeconds < 604800) { - const days = Math.floor(diffInSeconds / 86400); - return `${days} day${days > 1 ? 's' : ''} ago`; - } else { - return date.toLocaleDateString('en-US', { - year: 'numeric', - month: 'short', - day: 'numeric' - }); - } - } - - getAvatarInitials(name) { - if (!name) return 'UN'; - return name.split(' ') - .map(word => word.charAt(0).toUpperCase()) - .slice(0, 2) - .join(''); - } - - validateEmail(email) { - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - return emailRegex.test(email); - } - - showNotification(message, type = 'info') { - console.log(`[${type.toUpperCase()}] ${message}`); - - // Create notification element - const notification = document.createElement('div'); - notification.className = `notification notification-${type}`; - notification.innerHTML = ` -
- - ${message} -
- - `; - - // Style the notification - Object.assign(notification.style, { - position: 'fixed', - top: '20px', - right: '20px', - padding: '12px 16px', - backgroundColor: this.getNotificationColor(type), - color: 'white', - borderRadius: '8px', - boxShadow: '0 4px 12px rgba(0,0,0,0.15)', - zIndex: '10000', - display: 'flex', - alignItems: 'center', - gap: '12px', - maxWidth: '400px', - animation: 'slideInRight 0.3s ease', - fontFamily: 'Inter, sans-serif' - }); - - document.body.appendChild(notification); - - // Auto-remove after 4 seconds - setTimeout(() => { - if (notification.parentNode) { - notification.style.animation = 'slideOutRight 0.3s ease'; - setTimeout(() => notification.remove(), 300); - } - }, 4000); - } - - getNotificationColor(type) { - switch (type) { - case 'success': return '#10b981'; - case 'error': return '#ef4444'; - case 'warning': return '#f59e0b'; - default: return '#3b82f6'; - } - } - - showLoadingSpinner(element, show = true) { - if (!element) return; - - if (show) { - element.style.position = 'relative'; - element.style.opacity = '0.6'; - - const spinner = document.createElement('div'); - spinner.className = 'loading-spinner-overlay'; - spinner.innerHTML = ` -
- -
- `; - - Object.assign(spinner.style, { - position: 'absolute', - top: '0', - left: '0', - right: '0', - bottom: '0', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - backgroundColor: 'rgba(255,255,255,0.8)', - zIndex: '100' - }); - - element.appendChild(spinner); - } else { - element.style.opacity = ''; - const spinner = element.querySelector('.loading-spinner-overlay'); - if (spinner) spinner.remove(); - } - } - - validatePassword(password) { - return password.length >= 6; - } - - showNotification(message, type = 'info', duration = 3000) { - const notification = document.createElement('div'); - notification.className = `notification ${type}`; - notification.innerHTML = ` -
- - ${message} -
- - `; - - notification.style.cssText = ` - position: fixed; - top: 20px; - right: 20px; - background: ${type === 'success' ? '#4CAF50' : - type === 'error' ? '#f44336' : - type === 'warning' ? '#ff9800' : - 'var(--accent-primary)'}; - color: white; - padding: 16px 20px; - border-radius: 8px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); - z-index: 10000; - transform: translateX(400px); - transition: transform 0.3s ease; - max-width: 400px; - display: flex; - align-items: center; - gap: 12px; - `; - - document.body.appendChild(notification); - - // Animate in - setTimeout(() => { - notification.style.transform = 'translateX(0)'; - }, 100); - - // Auto remove - setTimeout(() => { - notification.style.transform = 'translateX(400px)'; - setTimeout(() => { - if (notification.parentElement) { - notification.parentElement.removeChild(notification); - } - }, 300); - }, duration); - - return notification; - } - - showLoadingSpinner(element, show = true) { - if (show) { - element.style.opacity = '0.6'; - element.style.pointerEvents = 'none'; - - if (!element.querySelector('.loading-spinner')) { - const spinner = document.createElement('div'); - spinner.className = 'loading-spinner'; - spinner.innerHTML = ''; - spinner.style.cssText = ` - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - z-index: 1000; - color: var(--accent-primary); - `; - element.style.position = 'relative'; - element.appendChild(spinner); - } - } else { - element.style.opacity = '1'; - element.style.pointerEvents = 'auto'; - - const spinner = element.querySelector('.loading-spinner'); - if (spinner) { - spinner.remove(); - } - } - } - - // File handling utilities - async fileToDataURI(file) { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = () => resolve(reader.result); - reader.onerror = reject; - reader.readAsDataURL(file); - }); - } - - compressImage(file, maxWidth = 800, quality = 0.8) { - return new Promise((resolve) => { - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); - const img = new Image(); - - img.onload = () => { - const ratio = Math.min(maxWidth / img.width, maxWidth / img.height); - canvas.width = img.width * ratio; - canvas.height = img.height * ratio; - - ctx.drawImage(img, 0, 0, canvas.width, canvas.height); - - canvas.toBlob(resolve, 'image/jpeg', quality); - }; - - img.src = URL.createObjectURL(file); - }); - } -} - -// Create global API instance -window.api = new OpenRocketsAPI(); - -// Auto-redirect to dashboard if authenticated and on landing page -document.addEventListener('DOMContentLoaded', () => { - const currentPath = window.location.pathname; - - if (currentPath === '/' && api.isAuthenticated()) { - window.location.href = '/dashboard'; - } - - // Update UI based on authentication status - updateAuthUI(); -}); - -function updateAuthUI() { - const authButtons = document.querySelectorAll('.auth-btn'); - const userMenu = document.querySelector('.user-menu'); - const profileImage = document.querySelector('.profile-image'); - - if (api.isAuthenticated()) { - authButtons.forEach(btn => btn.style.display = 'none'); - - if (userMenu) { - userMenu.style.display = 'block'; - } - - if (profileImage && api.currentUser.profileImage) { - profileImage.src = api.currentUser.profileImage; - } - - // Update user info in sidebar/header - const userNameElements = document.querySelectorAll('.user-name'); - userNameElements.forEach(element => { - element.textContent = api.currentUser.fullName || api.currentUser.username; - }); - - const userEmailElements = document.querySelectorAll('.user-email'); - userEmailElements.forEach(element => { - element.textContent = api.currentUser.email; - }); - } else { - authButtons.forEach(btn => btn.style.display = 'block'); - - if (userMenu) { - userMenu.style.display = 'none'; - } - } -} - -// Global logout function -function logout() { - api.logout(); -} diff --git a/scripts/auth.js b/scripts/auth.js deleted file mode 100644 index 3ff46ed..0000000 --- a/scripts/auth.js +++ /dev/null @@ -1,1093 +0,0 @@ -// Authentication Modal and Forms -document.addEventListener('DOMContentLoaded', function() { - createAuthModals(); - setupAuthForms(); -}); - -function createAuthModals() { - const authModalsHTML = ` - -
- -
- - -
- -
- - -
- -
- `; - - // Add modals to body - document.body.insertAdjacentHTML('beforeend', authModalsHTML); - - // Add auth modal styles - const authStyles = ` - - `; - - document.head.insertAdjacentHTML('beforeend', authStyles); -} - -function setupAuthForms() { - // Login form handler - document.getElementById('loginForm').addEventListener('submit', async (e) => { - e.preventDefault(); - - const formData = new FormData(e.target); - const email = formData.get('email'); - const password = formData.get('password'); - - const submitBtn = e.target.querySelector('.auth-submit-btn'); - const originalText = submitBtn.innerHTML; - - try { - submitBtn.innerHTML = ' Signing In...'; - submitBtn.disabled = true; - - await api.login(email, password); - - api.showNotification('Welcome back!', 'success'); - closeAuthModal('loginModal'); - - // Redirect to dashboard - window.location.href = '/dashboard'; - - } catch (error) { - api.showNotification(error.message, 'error'); - } finally { - submitBtn.innerHTML = originalText; - submitBtn.disabled = false; - } - }); - - // Register form handler - document.getElementById('registerForm').addEventListener('submit', async (e) => { - e.preventDefault(); - - const formData = new FormData(e.target); - const password = formData.get('password'); - const confirmPassword = formData.get('confirmPassword'); - - // Validate passwords match - if (password !== confirmPassword) { - api.showNotification('Passwords do not match', 'error'); - return; - } - - // Validate password strength - if (!api.validatePassword(password)) { - api.showNotification('Password must be at least 6 characters long', 'error'); - return; - } - - const submitBtn = e.target.querySelector('.auth-submit-btn'); - const originalText = submitBtn.innerHTML; - - try { - submitBtn.innerHTML = ' Creating Account...'; - submitBtn.disabled = true; - - const userData = { - fullName: formData.get('fullName'), - username: formData.get('username'), - email: formData.get('email'), - password: formData.get('password'), - userType: formData.get('userType'), - bio: '', - skills: '' - }; - - const profileImage = formData.get('profileImage'); - - await api.register(userData, profileImage); - - api.showNotification('Account created successfully! Welcome to OpenRockets!', 'success'); - closeAuthModal('registerModal'); - - // Redirect to dashboard - window.location.href = '/dashboard'; - - } catch (error) { - api.showNotification(error.message, 'error'); - } finally { - submitBtn.innerHTML = originalText; - submitBtn.disabled = false; - } - }); - - // Profile form handler - document.getElementById('profileForm').addEventListener('submit', async (e) => { - e.preventDefault(); - - const formData = new FormData(e.target); - const submitBtn = e.target.querySelector('.auth-submit-btn'); - const originalText = submitBtn.innerHTML; - - try { - submitBtn.innerHTML = ' Saving...'; - submitBtn.disabled = true; - - const profileData = { - fullName: formData.get('fullName'), - bio: formData.get('bio'), - skills: formData.get('skills'), - status: formData.get('status') - }; - - const profileImage = document.getElementById('profileImageUpdate').files[0]; - - await api.updateProfile(profileData, profileImage); - - // Update current user data - const updatedProfile = await api.getProfile(); - api.setCurrentUser(updatedProfile.user); - - api.showNotification('Profile updated successfully!', 'success'); - closeAuthModal('profileModal'); - - // Update UI - updateAuthUI(); - - } catch (error) { - api.showNotification(error.message, 'error'); - } finally { - submitBtn.innerHTML = originalText; - submitBtn.disabled = false; - } - }); - - // Password strength checker - document.getElementById('registerPassword').addEventListener('input', (e) => { - checkPasswordStrength(e.target.value); - }); - - // Profile image upload handlers - document.getElementById('profileImageUpload').addEventListener('change', (e) => { - handleProfileImageUpload(e, 'profileImagePreview'); - }); - - document.getElementById('profileImageUpdate').addEventListener('change', (e) => { - handleProfileImageUpdate(e); - }); -} - -function checkPasswordStrength(password) { - const strengthBar = document.querySelector('.strength-bar'); - const strengthText = document.querySelector('.strength-text'); - - let strength = 0; - let text = 'Weak'; - - if (password.length >= 6) strength++; - if (password.match(/[a-z]/)) strength++; - if (password.match(/[A-Z]/)) strength++; - if (password.match(/[0-9]/)) strength++; - if (password.match(/[^a-zA-Z0-9]/)) strength++; - - strengthBar.className = 'strength-bar'; - - if (strength >= 4) { - strengthBar.classList.add('strong'); - text = 'Strong'; - } else if (strength >= 2) { - strengthBar.classList.add('medium'); - text = 'Medium'; - } else if (strength >= 1) { - strengthBar.classList.add('weak'); - text = 'Weak'; - } - - strengthText.textContent = password.length === 0 ? 'Enter a password' : text; -} - -function handleProfileImageUpload(event, previewId) { - const file = event.target.files[0]; - const preview = document.getElementById(previewId); - - if (file) { - const reader = new FileReader(); - reader.onload = (e) => { - preview.querySelector('img').src = e.target.result; - preview.style.display = 'block'; - }; - reader.readAsDataURL(file); - } -} - -function handleProfileImageUpdate(event) { - const file = event.target.files[0]; - const currentImage = document.getElementById('currentProfileImage'); - - if (file) { - const reader = new FileReader(); - reader.onload = (e) => { - currentImage.src = e.target.result; - }; - reader.readAsDataURL(file); - } -} - -function removeProfileImage() { - document.getElementById('profileImageUpload').value = ''; - document.getElementById('profileImagePreview').style.display = 'none'; -} - -function togglePassword(inputId) { - const input = document.getElementById(inputId); - const button = input.parentElement.querySelector('.toggle-password i'); - - if (input.type === 'password') { - input.type = 'text'; - button.className = 'fas fa-eye-slash'; - } else { - input.type = 'password'; - button.className = 'fas fa-eye'; - } -} - -function openAuthModal(modalId) { - const modal = document.getElementById(modalId); - modal.classList.add('show'); - document.body.style.overflow = 'hidden'; - - // Load profile data if opening profile modal - if (modalId === 'profileModal' && api.isAuthenticated()) { - loadProfileData(); - } -} - -function closeAuthModal(modalId) { - const modal = document.getElementById(modalId); - modal.classList.remove('show'); - document.body.style.overflow = ''; - - // Reset forms - const form = modal.querySelector('form'); - if (form) { - form.reset(); - - // Reset password strength indicator - const strengthBar = modal.querySelector('.strength-bar'); - const strengthText = modal.querySelector('.strength-text'); - if (strengthBar) { - strengthBar.className = 'strength-bar'; - strengthText.textContent = 'Enter a password'; - } - - // Hide file previews - const previews = modal.querySelectorAll('.file-preview'); - previews.forEach(preview => preview.style.display = 'none'); - } -} - -function switchAuthModal(targetModalId) { - // Close all auth modals - document.querySelectorAll('.auth-modal').forEach(modal => { - modal.classList.remove('show'); - }); - - // Open target modal after a short delay - setTimeout(() => { - openAuthModal(targetModalId); - }, 300); -} - -async function loadProfileData() { - if (!api.isAuthenticated()) return; - - try { - const response = await api.getProfile(); - const user = response.user; - - // Populate form fields - document.getElementById('profileFullName').value = user.fullName || ''; - document.getElementById('profileBio').value = user.bio || ''; - document.getElementById('profileSkills').value = user.skills ? user.skills.join(', ') : ''; - document.getElementById('profileStatus').value = user.status || 'active'; - - // Set profile image - const currentImage = document.getElementById('currentProfileImage'); - if (user.profileImage) { - currentImage.src = user.profileImage; - } else { - // Use default avatar with initials - const initials = api.getAvatarInitials(user.fullName || user.username); - currentImage.src = `data:image/svg+xml,${encodeURIComponent(` - - - ${initials} - - `)}`; - } - - } catch (error) { - console.error('Failed to load profile data:', error); - api.showNotification('Failed to load profile data', 'error'); - } -} - -// Global functions for HTML onclick handlers -function showLoginModal() { - openAuthModal('loginModal'); -} - -function showRegisterModal() { - openAuthModal('registerModal'); -} - -function showProfileModal() { - openAuthModal('profileModal'); -} - -// Close modals when clicking outside -document.addEventListener('click', (e) => { - if (e.target.classList.contains('auth-modal')) { - e.target.classList.remove('show'); - document.body.style.overflow = ''; - } -}); - -// Close modals with Escape key -document.addEventListener('keydown', (e) => { - if (e.key === 'Escape') { - document.querySelectorAll('.auth-modal.show').forEach(modal => { - modal.classList.remove('show'); - document.body.style.overflow = ''; - }); - } -}); diff --git a/scripts/calendar-integration.js b/scripts/calendar-integration.js deleted file mode 100644 index 36e611b..0000000 --- a/scripts/calendar-integration.js +++ /dev/null @@ -1,592 +0,0 @@ -// Calendar Page Integration with Backend -document.addEventListener('DOMContentLoaded', async function() { - // Ensure user is authenticated - if (!api.requireAuth()) return; - - // Load calendar data - await loadCalendarData(); - - // Set up event creation for instructors - setupEventCreation(); - - // Set up real-time updates - setupCalendarUpdates(); -}); - -// Global events storage -let allEvents = []; - -async function loadCalendarData() { - try { - // Show loading state - showCalendarLoading(true); - - // Load events from backend - const response = await api.getEvents(); - allEvents = response.events || []; - - // Render calendar components - renderMiniCalendar(); - renderWeekView(); - renderUpcomingEvents(); - - } catch (error) { - console.error('Failed to load calendar data:', error); - api.showNotification('Failed to load calendar data', 'error'); - } finally { - showCalendarLoading(false); - } -} - -function renderMiniCalendar() { - const calendarGrid = document.querySelector('.mini-calendar-grid'); - if (!calendarGrid) return; - - const today = new Date(); - const currentMonth = today.getMonth(); - const currentYear = today.getFullYear(); - - // Update month/year display - const monthYearDisplay = document.getElementById('monthYear'); - if (monthYearDisplay) { - const monthNames = [ - 'January', 'February', 'March', 'April', 'May', 'June', - 'July', 'August', 'September', 'October', 'November', 'December' - ]; - monthYearDisplay.textContent = `${monthNames[currentMonth]} ${currentYear}`; - } - - // Clear existing calendar - calendarGrid.innerHTML = ''; - - // Get first day of month and number of days - const firstDay = new Date(currentYear, currentMonth, 1); - const lastDay = new Date(currentYear, currentMonth + 1, 0); - const daysInMonth = lastDay.getDate(); - const startingDayOfWeek = firstDay.getDay(); - - // Add empty cells for days before month starts - for (let i = 0; i < startingDayOfWeek; i++) { - const emptyDay = document.createElement('div'); - emptyDay.className = 'calendar-day empty'; - calendarGrid.appendChild(emptyDay); - } - - // Add days of the month - for (let day = 1; day <= daysInMonth; day++) { - const dayElement = document.createElement('div'); - dayElement.className = 'calendar-day'; - dayElement.textContent = day; - - // Highlight today - if (currentYear === today.getFullYear() && - currentMonth === today.getMonth() && - day === today.getDate()) { - dayElement.classList.add('today'); - } - - // Add events indicator - const dayDate = new Date(currentYear, currentMonth, day); - const dayEvents = allEvents.filter(event => { - const eventDate = new Date(event.date); - return eventDate.toDateString() === dayDate.toDateString(); - }); - - if (dayEvents.length > 0) { - dayElement.classList.add('has-events'); - const indicator = document.createElement('div'); - indicator.className = 'event-indicator'; - indicator.textContent = dayEvents.length; - dayElement.appendChild(indicator); - } - - dayElement.addEventListener('click', () => { - selectDay(dayDate); - }); - - calendarGrid.appendChild(dayElement); - } -} - -function renderWeekView() { - const weekGrid = document.querySelector('.week-grid'); - if (!weekGrid) return; - - const weekStart = getCurrentWeekStart(); - updateWeekRange(weekStart); - - // Clear existing events - weekGrid.querySelectorAll('.event-block').forEach(block => block.remove()); - - // Render events for the week - for (let i = 0; i < 7; i++) { - const dayDate = new Date(weekStart); - dayDate.setDate(weekStart.getDate() + i); - - const dayEvents = allEvents.filter(event => { - const eventDate = new Date(event.date); - return eventDate.toDateString() === dayDate.toDateString(); - }); - - dayEvents.forEach((event, index) => { - const eventBlock = createEventBlock(event, i, index); - weekGrid.appendChild(eventBlock); - }); - } -} - -function createEventBlock(event, dayIndex, eventIndex) { - const eventBlock = document.createElement('div'); - eventBlock.className = `event-block ${event.type}`; - eventBlock.style.gridColumn = dayIndex + 2; // +2 because first column is time - - // Calculate position based on time - const timeSlot = getTimeSlot(event.startTime); - if (timeSlot) { - eventBlock.style.gridRow = timeSlot; - } else { - eventBlock.style.gridRow = eventIndex + 2; // Fallback positioning - } - - const isJoined = event.attendees && event.attendees.includes(api.currentUser.id); - const isFull = event.attendees && event.attendees.length >= event.maxAttendees; - - eventBlock.innerHTML = ` -
${event.startTime} - ${event.endTime}
-
${event.title}
-
with ${event.instructor.fullName}
-
- ${event.attendees ? event.attendees.length : 0}/${event.maxAttendees} - ${isJoined ? '' : ''} -
-
- ${!isJoined && !isFull ? - `` : - isJoined ? - `` : - `` - } -
- `; - - eventBlock.addEventListener('click', (e) => { - // Don't show details if clicking on join button - if (!e.target.classList.contains('join-btn')) { - showEventDetails(event); - } - }); - - return eventBlock; -} - -function getTimeSlot(timeString) { - try { - // Parse time string (e.g., "10:00 AM" or "2:30 PM") - const [time, period] = timeString.split(' '); - const [hours, minutes] = time.split(':').map(Number); - - let hour24 = hours; - if (period === 'PM' && hours !== 12) { - hour24 += 12; - } else if (period === 'AM' && hours === 12) { - hour24 = 0; - } - - // Grid starts at 8 AM (row 2), each hour is one row - const gridRow = hour24 - 7; // 8 AM = row 2, 9 AM = row 3, etc. - - if (gridRow >= 2 && gridRow <= 24) { - return gridRow; - } - } catch (error) { - console.error('Error parsing time:', timeString); - } - - return null; -} - -function renderUpcomingEvents() { - const upcomingList = document.querySelector('.upcoming-events-list'); - if (!upcomingList) return; - - // Filter and sort upcoming events - const now = new Date(); - const upcoming = allEvents - .filter(event => { - const eventDate = new Date(`${event.date} ${event.startTime}`); - return eventDate >= now; - }) - .sort((a, b) => { - const dateA = new Date(`${a.date} ${a.startTime}`); - const dateB = new Date(`${b.date} ${b.startTime}`); - return dateA - dateB; - }) - .slice(0, 5); - - upcomingList.innerHTML = ''; - - if (upcoming.length === 0) { - upcomingList.innerHTML = ` -
- -

No upcoming events

- ${api.currentUser.userType === 'instructor' ? - '' : - 'Browse Classes' - } -
- `; - return; - } - - upcoming.forEach(event => { - const eventItem = document.createElement('div'); - eventItem.className = 'upcoming-event-item'; - - const isJoined = event.attendees && event.attendees.includes(api.currentUser.id); - const isFull = event.attendees && event.attendees.length >= event.maxAttendees; - - eventItem.innerHTML = ` -
-
${event.title}
-
- ${event.startTime} - ${event.endTime} - with ${event.instructor.fullName} -
-
${formatEventDate(event.date)}
-
- ${event.tags ? event.tags.map(tag => `${tag}`).join('') : ''} -
-
-
- ${!isJoined && !isFull ? - `` : - isJoined ? - `` : - `` - } -
- `; - - upcomingList.appendChild(eventItem); - }); -} - -function setupEventCreation() { - // Only show create event button for instructors - const createEventBtn = document.getElementById('createEventBtn'); - if (createEventBtn) { - if (api.currentUser.userType === 'instructor') { - createEventBtn.style.display = 'block'; - } else { - createEventBtn.style.display = 'none'; - } - } - - // Set up event creation form - const eventForm = document.getElementById('eventForm'); - if (eventForm) { - eventForm.addEventListener('submit', async (e) => { - e.preventDefault(); - - const formData = new FormData(e.target); - const submitBtn = e.target.querySelector('.submit-btn'); - const originalText = submitBtn.innerHTML; - - try { - submitBtn.innerHTML = ' Creating...'; - submitBtn.disabled = true; - - const eventData = { - title: formData.get('title'), - description: formData.get('description'), - type: formData.get('type'), - startTime: formData.get('startTime'), - endTime: formData.get('endTime'), - date: formData.get('date'), - maxAttendees: parseInt(formData.get('maxAttendees')), - tags: formData.get('tags') - }; - - await api.createEvent(eventData); - - api.showNotification('Event created successfully!', 'success'); - closeEventModal(); - - // Reload calendar data - await loadCalendarData(); - - } catch (error) { - api.showNotification(error.message, 'error'); - } finally { - submitBtn.innerHTML = originalText; - submitBtn.disabled = false; - } - }); - } -} - -function setupCalendarUpdates() { - // Set up filter buttons - const filterBtns = document.querySelectorAll('.filter-btn'); - filterBtns.forEach(btn => { - btn.addEventListener('click', function() { - // Update active state - filterBtns.forEach(b => b.classList.remove('active')); - this.classList.add('active'); - - // Filter events - const filterType = this.dataset.filter; - filterEvents(filterType); - }); - }); - - // Set up search - const searchInput = document.querySelector('.search-box input'); - if (searchInput) { - let searchTimeout; - - searchInput.addEventListener('input', function() { - clearTimeout(searchTimeout); - const query = this.value.toLowerCase(); - - searchTimeout = setTimeout(() => { - searchEvents(query); - }, 300); - }); - } - - // Set up periodic updates - setInterval(async () => { - try { - await loadCalendarData(); - } catch (error) { - console.error('Failed to update calendar:', error); - } - }, 60000); // Update every minute -} - -function filterEvents(type) { - const eventBlocks = document.querySelectorAll('.event-block'); - - eventBlocks.forEach(block => { - if (type === 'all' || block.classList.contains(type)) { - block.style.display = 'block'; - } else { - block.style.display = 'none'; - } - }); - - // Also filter upcoming events - const upcomingItems = document.querySelectorAll('.upcoming-event-item'); - upcomingItems.forEach(item => { - const eventTitle = item.querySelector('.event-title').textContent.toLowerCase(); - const shouldShow = type === 'all' || - (type === 'lecture' && eventTitle.includes('lecture')) || - (type === 'workshop' && eventTitle.includes('workshop')) || - (type === 'lab' && eventTitle.includes('lab')); - - item.style.display = shouldShow ? 'block' : 'none'; - }); -} - -function searchEvents(query) { - const eventBlocks = document.querySelectorAll('.event-block'); - - eventBlocks.forEach(block => { - const title = block.querySelector('.event-title').textContent.toLowerCase(); - const instructor = block.querySelector('.event-instructor').textContent.toLowerCase(); - - if (query === '' || title.includes(query) || instructor.includes(query)) { - block.style.display = 'block'; - } else { - block.style.display = 'none'; - } - }); -} - -// Global functions for calendar interactions -window.joinCalendarEvent = async (eventId) => { - try { - await api.joinEvent(eventId); - api.showNotification('Successfully joined event!', 'success'); - - // Reload calendar data - await loadCalendarData(); - - } catch (error) { - api.showNotification(error.message, 'error'); - } -}; - -window.showEventDetails = (event) => { - const modal = document.getElementById('eventModal'); - if (!modal) return; - - // Populate modal with event details - const modalTitle = document.getElementById('modalEventTitle'); - const modalInstructor = document.getElementById('modalEventInstructor'); - const modalTime = document.getElementById('modalEventTime'); - const modalDate = document.getElementById('modalEventDate'); - const modalDescription = document.getElementById('modalEventDescription'); - const modalAttendees = document.getElementById('modalEventAttendees'); - const modalTags = document.getElementById('modalEventTags'); - - if (modalTitle) modalTitle.textContent = event.title; - if (modalInstructor) modalInstructor.textContent = event.instructor.fullName; - if (modalTime) modalTime.textContent = `${event.startTime} - ${event.endTime}`; - if (modalDate) modalDate.textContent = formatEventDate(event.date); - if (modalDescription) modalDescription.textContent = event.description; - if (modalAttendees) modalAttendees.textContent = `${event.attendees ? event.attendees.length : 0}/${event.maxAttendees} attendees`; - - if (modalTags) { - modalTags.innerHTML = ''; - if (event.tags) { - event.tags.forEach(tag => { - const tagElement = document.createElement('span'); - tagElement.className = 'event-tag'; - tagElement.textContent = tag; - modalTags.appendChild(tagElement); - }); - } - } - - // Show modal - modal.classList.add('show'); - document.body.style.overflow = 'hidden'; -}; - -window.openCreateEventModal = () => { - const modal = document.getElementById('eventModal'); - if (modal) { - // Clear existing content and show create form - const modalContent = modal.querySelector('.modal-content'); - modalContent.innerHTML = ` - -
-
- - -
-
- - -
-
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
- - -
-
-
- - -
-
- - -
-
- `; - - // Re-setup form handler - setupEventCreation(); - - modal.classList.add('show'); - document.body.style.overflow = 'hidden'; - } -}; - -window.closeEventModal = () => { - const modal = document.getElementById('eventModal'); - if (modal) { - modal.classList.remove('show'); - document.body.style.overflow = ''; - } -}; - -// Helper functions -function getCurrentWeekStart() { - const today = new Date(); - const dayOfWeek = today.getDay(); - const weekStart = new Date(today); - weekStart.setDate(today.getDate() - dayOfWeek); - return weekStart; -} - -function updateWeekRange(weekStart) { - const weekRangeDisplay = document.getElementById('weekRange'); - if (!weekRangeDisplay) return; - - const weekEnd = new Date(weekStart); - weekEnd.setDate(weekStart.getDate() + 6); - - const options = { month: 'short', day: 'numeric' }; - const startStr = weekStart.toLocaleDateString('en-US', options); - const endStr = weekEnd.toLocaleDateString('en-US', options); - - weekRangeDisplay.textContent = `${startStr} - ${endStr}`; -} - -function selectDay(date) { - console.log('Selected day:', date); - // Could implement day view or filter events for selected day -} - -function formatEventDate(dateString) { - const date = new Date(dateString); - return date.toLocaleDateString('en-US', { - weekday: 'short', - month: 'short', - day: 'numeric' - }); -} - -function showCalendarLoading(show) { - const loadingElements = document.querySelectorAll('.week-grid, .upcoming-events-list, .mini-calendar-grid'); - - loadingElements.forEach(element => { - if (show) { - api.showLoadingSpinner(element, true); - } else { - api.showLoadingSpinner(element, false); - } - }); -} diff --git a/scripts/calendar.js b/scripts/calendar.js deleted file mode 100644 index 2cfab4b..0000000 --- a/scripts/calendar.js +++ /dev/null @@ -1,703 +0,0 @@ -// Calendar Page JavaScript -document.addEventListener('DOMContentLoaded', function() { - // Initialize calendar data - const currentDate = new Date(); - let currentMonth = currentDate.getMonth(); - let currentYear = currentDate.getFullYear(); - - // Calendar navigation - const prevMonthBtn = document.getElementById('prevMonth'); - const nextMonthBtn = document.getElementById('nextMonth'); - const monthYearDisplay = document.getElementById('monthYear'); - - // Week view navigation - const prevWeekBtn = document.getElementById('prevWeek'); - const nextWeekBtn = document.getElementById('nextWeek'); - const weekRangeDisplay = document.getElementById('weekRange'); - - // View toggles - const viewToggles = document.querySelectorAll('.view-toggle'); - let currentView = 'week'; - - // Event modal - const eventModal = document.getElementById('eventModal'); - const closeEventModal = document.getElementById('closeEventModal'); - const eventForm = document.getElementById('eventForm'); - - // Sample events data - using relative dates from the current date - const baseDate = new Date(); - const getDateOffset = (daysOffset) => { - const date = new Date(baseDate); - date.setDate(baseDate.getDate() + daysOffset); - return date; - }; - - const sampleEvents = [ - { - id: 1, - title: 'Advanced React Patterns', - type: 'lecture', - instructor: 'Sarah Chen', - time: '10:00 AM - 11:30 AM', - date: getDateOffset(0), - description: 'Deep dive into advanced React patterns including HOCs, render props, and custom hooks.', - attendees: 45, - maxAttendees: 50, - tags: ['React', 'JavaScript', 'Frontend'] - }, - { - id: 2, - title: 'Python Data Structures', - type: 'workshop', - instructor: 'Mike Johnson', - time: '2:00 PM - 4:00 PM', - date: getDateOffset(0), - description: 'Hands-on workshop covering lists, dictionaries, sets, and tuples in Python.', - attendees: 32, - maxAttendees: 40, - tags: ['Python', 'Data Structures', 'Backend'] - }, - { - id: 3, - title: 'Node.js Performance', - type: 'lecture', - instructor: 'Alex Rodriguez', - time: '11:00 AM - 12:30 PM', - date: getDateOffset(1), - description: 'Optimization techniques for Node.js applications including clustering and caching.', - attendees: 28, - maxAttendees: 35, - tags: ['Node.js', 'Performance', 'Backend'] - }, - { - id: 4, - title: 'CSS Grid Masterclass', - type: 'workshop', - instructor: 'Emma Davis', - time: '1:00 PM - 3:30 PM', - date: getDateOffset(2), - description: 'Complete guide to CSS Grid layout with practical examples and exercises.', - attendees: 38, - maxAttendees: 45, - tags: ['CSS', 'Layout', 'Frontend'] - }, - { - id: 5, - title: 'Database Design Principles', - type: 'lecture', - instructor: 'Dr. James Wilson', - time: '9:00 AM - 10:30 AM', - date: getDateOffset(3), - description: 'Fundamental principles of database design, normalization, and optimization.', - attendees: 52, - maxAttendees: 60, - tags: ['Database', 'SQL', 'Backend'] - } - ]; - - // Initialize calendar - initializeCalendar(); - renderMiniCalendar(); - renderWeekView(); - renderUpcomingEvents(); - - // Event listeners - if (prevMonthBtn) { - prevMonthBtn.addEventListener('click', () => { - currentMonth--; - if (currentMonth < 0) { - currentMonth = 11; - currentYear--; - } - renderMiniCalendar(); - }); - } - - if (nextMonthBtn) { - nextMonthBtn.addEventListener('click', () => { - currentMonth++; - if (currentMonth > 11) { - currentMonth = 0; - currentYear++; - } - renderMiniCalendar(); - }); - } - - if (prevWeekBtn) { - prevWeekBtn.addEventListener('click', () => { - // Navigate to previous week - const currentWeekStart = getCurrentWeekStart(); - currentWeekStart.setDate(currentWeekStart.getDate() - 7); - updateWeekView(currentWeekStart); - }); - } - - if (nextWeekBtn) { - nextWeekBtn.addEventListener('click', () => { - // Navigate to next week - const currentWeekStart = getCurrentWeekStart(); - currentWeekStart.setDate(currentWeekStart.getDate() + 7); - updateWeekView(currentWeekStart); - }); - } - - // View toggle functionality - viewToggles.forEach(toggle => { - toggle.addEventListener('click', () => { - // Remove active from all toggles - viewToggles.forEach(t => t.classList.remove('active')); - - // Add active to clicked toggle - toggle.classList.add('active'); - - // Update current view - currentView = toggle.dataset.view; - - // Show/hide appropriate view - toggleView(currentView); - }); - }); - - // Event modal functionality - if (closeEventModal) { - closeEventModal.addEventListener('click', closeModal); - } - - if (eventModal) { - eventModal.addEventListener('click', (e) => { - if (e.target === eventModal) { - closeModal(); - } - }); - } - - if (eventForm) { - eventForm.addEventListener('submit', handleEventSubmit); - } - - function initializeCalendar() { - const today = new Date(); - updateTodayDisplay(today); - } - - function updateTodayDisplay(date) { - const options = { - weekday: 'long', - year: 'numeric', - month: 'long', - day: 'numeric' - }; - - const todayElement = document.querySelector('.today-date'); - if (todayElement) { - todayElement.textContent = date.toLocaleDateString('en-US', options); - } - } - - function renderMiniCalendar() { - const calendarGrid = document.querySelector('.mini-calendar-grid'); - if (!calendarGrid) return; - - // Update month/year display - if (monthYearDisplay) { - const monthNames = [ - 'January', 'February', 'March', 'April', 'May', 'June', - 'July', 'August', 'September', 'October', 'November', 'December' - ]; - monthYearDisplay.textContent = `${monthNames[currentMonth]} ${currentYear}`; - } - - // Clear existing calendar - calendarGrid.innerHTML = ''; - - // Get first day of month and number of days - const firstDay = new Date(currentYear, currentMonth, 1); - const lastDay = new Date(currentYear, currentMonth + 1, 0); - const daysInMonth = lastDay.getDate(); - const startingDayOfWeek = firstDay.getDay(); - - // Add empty cells for days before month starts - for (let i = 0; i < startingDayOfWeek; i++) { - const emptyDay = document.createElement('div'); - emptyDay.className = 'calendar-day empty'; - calendarGrid.appendChild(emptyDay); - } - - // Add days of the month - for (let day = 1; day <= daysInMonth; day++) { - const dayElement = document.createElement('div'); - dayElement.className = 'calendar-day'; - dayElement.textContent = day; - - // Highlight today - const today = new Date(); - if (currentYear === today.getFullYear() && - currentMonth === today.getMonth() && - day === today.getDate()) { - dayElement.classList.add('today'); - } - - // Add events indicator - const dayDate = new Date(currentYear, currentMonth, day); - const dayEvents = sampleEvents.filter(event => - event.date.toDateString() === dayDate.toDateString() - ); - - if (dayEvents.length > 0) { - dayElement.classList.add('has-events'); - const indicator = document.createElement('div'); - indicator.className = 'event-indicator'; - indicator.textContent = dayEvents.length; - dayElement.appendChild(indicator); - } - - dayElement.addEventListener('click', () => { - // Navigate to selected day - selectDay(dayDate); - }); - - calendarGrid.appendChild(dayElement); - } - } - - function renderWeekView() { - const weekGrid = document.querySelector('.week-grid'); - if (!weekGrid) return; - - const weekStart = getCurrentWeekStart(); - updateWeekRange(weekStart); - - // Clear existing events - weekGrid.querySelectorAll('.event-block').forEach(block => block.remove()); - - // Render events for the week - for (let i = 0; i < 7; i++) { - const dayDate = new Date(weekStart); - dayDate.setDate(weekStart.getDate() + i); - - const dayEvents = sampleEvents.filter(event => - event.date.toDateString() === dayDate.toDateString() - ); - - dayEvents.forEach((event, index) => { - const eventBlock = createEventBlock(event, i, index); - weekGrid.appendChild(eventBlock); - }); - } - } - - function createEventBlock(event, dayIndex, eventIndex) { - const eventBlock = document.createElement('div'); - eventBlock.className = `event-block ${event.type}`; - eventBlock.style.gridColumn = dayIndex + 2; // +2 because first column is time - eventBlock.style.gridRow = eventIndex + 2; // +2 to account for header - - const timeSlot = getTimeSlot(event.time); - if (timeSlot) { - eventBlock.style.gridRow = timeSlot; - } - - eventBlock.innerHTML = ` -
${event.time}
-
${event.title}
-
${event.instructor}
-
${event.attendees}/${event.maxAttendees}
- `; - - eventBlock.addEventListener('click', () => { - showEventDetails(event); - }); - - return eventBlock; - } - - function getTimeSlot(timeString) { - // Convert time string to grid row number - // This is a simplified implementation - const hour = parseInt(timeString.split(':')[0]); - const ampm = timeString.includes('PM') ? 'PM' : 'AM'; - - let gridHour = hour; - if (ampm === 'PM' && hour !== 12) { - gridHour += 12; - } else if (ampm === 'AM' && hour === 12) { - gridHour = 0; - } - - // Assuming grid starts at 8 AM - return gridHour - 7; - } - - function renderUpcomingEvents() { - const upcomingList = document.querySelector('.upcoming-events-list'); - if (!upcomingList) return; - - // Sort events by date and take next 5 - const upcoming = sampleEvents - .filter(event => event.date >= new Date()) - .sort((a, b) => a.date - b.date) - .slice(0, 5); - - upcomingList.innerHTML = ''; - - upcoming.forEach(event => { - const eventItem = document.createElement('div'); - eventItem.className = 'upcoming-event-item'; - eventItem.innerHTML = ` -
-
${event.title}
-
- ${event.time} - with ${event.instructor} -
-
${formatDate(event.date)}
-
- - `; - - upcomingList.appendChild(eventItem); - }); - } - - function getCurrentWeekStart() { - const today = new Date(); - const dayOfWeek = today.getDay(); - const weekStart = new Date(today); - weekStart.setDate(today.getDate() - dayOfWeek); - return weekStart; - } - - function updateWeekRange(weekStart) { - if (!weekRangeDisplay) return; - - const weekEnd = new Date(weekStart); - weekEnd.setDate(weekStart.getDate() + 6); - - const options = { month: 'short', day: 'numeric' }; - const startStr = weekStart.toLocaleDateString('en-US', options); - const endStr = weekEnd.toLocaleDateString('en-US', options); - - weekRangeDisplay.textContent = `${startStr} - ${endStr}`; - } - - function updateWeekView(newWeekStart) { - updateWeekRange(newWeekStart); - renderWeekView(); - } - - function selectDay(date) { - console.log('Selected day:', date); - // Update week view to show selected day - const weekStart = new Date(date); - weekStart.setDate(date.getDate() - date.getDay()); - updateWeekView(weekStart); - } - - function toggleView(view) { - const weekView = document.querySelector('.week-view'); - const monthView = document.querySelector('.month-view'); - - if (view === 'week') { - if (weekView) weekView.style.display = 'block'; - if (monthView) monthView.style.display = 'none'; - } else if (view === 'month') { - if (weekView) weekView.style.display = 'none'; - if (monthView) monthView.style.display = 'block'; - renderMonthView(); - } - } - - function renderMonthView() { - // Implement month view rendering - console.log('Rendering month view'); - } - - function showEventDetails(event) { - const modalTitle = document.getElementById('modalEventTitle'); - const modalInstructor = document.getElementById('modalEventInstructor'); - const modalTime = document.getElementById('modalEventTime'); - const modalDate = document.getElementById('modalEventDate'); - const modalDescription = document.getElementById('modalEventDescription'); - const modalAttendees = document.getElementById('modalEventAttendees'); - const modalTags = document.getElementById('modalEventTags'); - - if (modalTitle) modalTitle.textContent = event.title; - if (modalInstructor) modalInstructor.textContent = event.instructor; - if (modalTime) modalTime.textContent = event.time; - if (modalDate) modalDate.textContent = formatDate(event.date); - if (modalDescription) modalDescription.textContent = event.description; - if (modalAttendees) modalAttendees.textContent = `${event.attendees}/${event.maxAttendees} attendees`; - - if (modalTags) { - modalTags.innerHTML = ''; - event.tags.forEach(tag => { - const tagElement = document.createElement('span'); - tagElement.className = 'event-tag'; - tagElement.textContent = tag; - modalTags.appendChild(tagElement); - }); - } - - openModal(); - } - - function openModal() { - if (eventModal) { - eventModal.classList.add('show'); - document.body.style.overflow = 'hidden'; - } - } - - function closeModal() { - if (eventModal) { - eventModal.classList.remove('show'); - document.body.style.overflow = ''; - } - } - - function handleEventSubmit(e) { - e.preventDefault(); - - const formData = new FormData(e.target); - const eventData = { - title: formData.get('title'), - type: formData.get('type'), - instructor: formData.get('instructor'), - time: formData.get('time'), - date: new Date(formData.get('date')), - description: formData.get('description'), - maxAttendees: parseInt(formData.get('maxAttendees')), - attendees: 0, - tags: formData.get('tags').split(',').map(tag => tag.trim()) - }; - - console.log('Creating new event:', eventData); - - // Add to sample events (in real app, this would be an API call) - sampleEvents.push({ - ...eventData, - id: sampleEvents.length + 1 - }); - - // Refresh views - renderWeekView(); - renderUpcomingEvents(); - - // Close modal and reset form - closeModal(); - e.target.reset(); - - // Show success message - showNotification('Event created successfully!', 'success'); - } - - function formatDate(date) { - const options = { - weekday: 'short', - month: 'short', - day: 'numeric' - }; - return date.toLocaleDateString('en-US', options); - } - - function showNotification(message, type = 'info') { - const notification = document.createElement('div'); - notification.className = `notification ${type}`; - notification.textContent = message; - notification.style.cssText = ` - position: fixed; - top: 20px; - right: 20px; - background: var(--accent-primary); - color: white; - padding: 12px 20px; - border-radius: 8px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); - z-index: 10000; - transform: translateX(400px); - transition: transform 0.3s ease; - `; - - document.body.appendChild(notification); - - // Animate in - setTimeout(() => { - notification.style.transform = 'translateX(0)'; - }, 100); - - // Animate out and remove - setTimeout(() => { - notification.style.transform = 'translateX(400px)'; - setTimeout(() => { - document.body.removeChild(notification); - }, 300); - }, 3000); - } - - // Filter functionality - document.querySelectorAll('.filter-btn').forEach(btn => { - btn.addEventListener('click', function() { - // Remove active from all filter buttons - document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active')); - - // Add active to clicked button - this.classList.add('active'); - - // Filter events - const filterType = this.dataset.filter; - filterEvents(filterType); - }); - }); - - function filterEvents(type) { - const eventBlocks = document.querySelectorAll('.event-block'); - - eventBlocks.forEach(block => { - if (type === 'all' || block.classList.contains(type)) { - block.style.display = 'block'; - } else { - block.style.display = 'none'; - } - }); - } - - // Search functionality - const searchInput = document.querySelector('.search-box input'); - if (searchInput) { - let searchTimeout; - - searchInput.addEventListener('input', function() { - clearTimeout(searchTimeout); - const query = this.value.toLowerCase(); - - searchTimeout = setTimeout(() => { - searchEvents(query); - }, 300); - }); - } - - function searchEvents(query) { - const eventBlocks = document.querySelectorAll('.event-block'); - - eventBlocks.forEach(block => { - const title = block.querySelector('.event-title').textContent.toLowerCase(); - const instructor = block.querySelector('.event-instructor').textContent.toLowerCase(); - - if (query === '' || title.includes(query) || instructor.includes(query)) { - block.style.display = 'block'; - } else { - block.style.display = 'none'; - } - }); - } - - // Create new event button - const createEventBtn = document.getElementById('createEventBtn'); - if (createEventBtn) { - createEventBtn.addEventListener('click', () => { - // Clear form and open modal for new event - if (eventForm) { - eventForm.reset(); - } - openModal(); - }); - } - - // Time slot highlighting - function highlightCurrentTimeSlot() { - const now = new Date(); - const currentHour = now.getHours(); - - // Remove previous highlights - document.querySelectorAll('.time-slot.current').forEach(slot => { - slot.classList.remove('current'); - }); - - // Highlight current time slot - const currentSlot = document.querySelector(`[data-hour="${currentHour}"]`); - if (currentSlot) { - currentSlot.classList.add('current'); - } - } - - // Update current time slot every minute - setInterval(highlightCurrentTimeSlot, 60000); - highlightCurrentTimeSlot(); - - // Keyboard shortcuts - document.addEventListener('keydown', function(e) { - if (e.ctrlKey || e.metaKey) { - switch(e.key) { - case 'n': - e.preventDefault(); - if (createEventBtn) createEventBtn.click(); - break; - case 'ArrowLeft': - e.preventDefault(); - if (currentView === 'week' && prevWeekBtn) prevWeekBtn.click(); - break; - case 'ArrowRight': - e.preventDefault(); - if (currentView === 'week' && nextWeekBtn) nextWeekBtn.click(); - break; - } - } - - if (e.key === 'Escape') { - closeModal(); - } - }); -}); - -// Global functions for HTML onclick handlers -function joinEvent(eventId) { - console.log('Joining event:', eventId); - - const event = sampleEvents.find(e => e.id === eventId); - if (event && event.attendees < event.maxAttendees) { - event.attendees++; - - // Update UI - renderUpcomingEvents(); - renderWeekView(); - - showNotification(`Successfully joined "${event.title}"!`, 'success'); - } else { - showNotification('Event is full or not found', 'error'); - } -} - -function showNotification(message, type = 'info') { - const notification = document.createElement('div'); - notification.className = `notification ${type}`; - notification.textContent = message; - notification.style.cssText = ` - position: fixed; - top: 20px; - right: 20px; - background: ${type === 'success' ? '#4CAF50' : type === 'error' ? '#f44336' : 'var(--accent-primary)'}; - color: white; - padding: 12px 20px; - border-radius: 8px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); - z-index: 10000; - transform: translateX(400px); - transition: transform 0.3s ease; - `; - - document.body.appendChild(notification); - - setTimeout(() => { - notification.style.transform = 'translateX(0)'; - }, 100); - - setTimeout(() => { - notification.style.transform = 'translateX(400px)'; - setTimeout(() => { - document.body.removeChild(notification); - }, 300); - }, 3000); -} diff --git a/scripts/community-integration.js b/scripts/community-integration.js deleted file mode 100644 index e69de29..0000000 diff --git a/scripts/community-new.js b/scripts/community-new.js deleted file mode 100644 index e85af9f..0000000 --- a/scripts/community-new.js +++ /dev/null @@ -1,978 +0,0 @@ -// Enhanced Community Page JavaScript with Real-time Features -class CommunityManager { - constructor() { - this.socket = null; - this.currentUser = null; - this.chatVisible = false; - this.typingTimeout = null; - this.tags = new Set(); - - this.init(); - } - - async init() { - // Get current user from API - this.currentUser = api.currentUser || { id: 'guest', username: 'Guest User' }; - - // Initialize WebSocket connection - this.initializeSocket(); - - // Set up UI components - this.setupModalHandlers(); - this.setupCreatePostForm(); - this.setupChatFeatures(); - this.setupTagsInput(); - this.setupContentEditor(); - - // Load initial data - await this.loadCommunityData(); - - console.log('🚀 Community Manager initialized'); - } - - initializeSocket() { - try { - this.socket = io('http://localhost:3000', { - autoConnect: true, - reconnection: true, - reconnectionAttempts: 5, - reconnectionDelay: 1000 - }); - - this.socket.on('connect', () => { - console.log('🔌 Connected to community server'); - - // Join as current user - this.socket.emit('join-user', { - userId: this.currentUser.id, - username: this.currentUser.fullName || this.currentUser.username || 'Guest User' - }); - }); - - this.socket.on('disconnect', () => { - console.log('🔌 Disconnected from community server'); - }); - - this.socket.on('new-message', (message) => { - this.addChatMessage(message); - }); - - this.socket.on('new-post', (post) => { - this.addNewPost(post); - }); - - this.socket.on('user-joined', (user) => { - console.log('User joined:', user.username); - }); - - this.socket.on('user-left', (user) => { - console.log('User left:', user.username); - }); - - this.socket.on('typing', (data) => { - this.showTypingIndicator(data); - }); - - this.socket.on('stop-typing', (data) => { - this.hideTypingIndicator(data); - }); - } catch (error) { - console.warn('Socket.io connection failed:', error); - // Continue without real-time features - } - } - - setupModalHandlers() { - // Create Post Modal - const createPostBtn = document.getElementById('createPostBtn'); - const createPostModal = document.getElementById('createPostModal'); - const closeModalBtns = document.querySelectorAll('.close-modal, .cancel-btn'); - - if (createPostBtn) { - createPostBtn.addEventListener('click', () => { - if (api.isAuthenticated()) { - this.openCreatePostModal(); - } else { - this.showAuthPrompt('Please log in to create posts!'); - } - }); - } - - closeModalBtns.forEach(btn => { - btn.addEventListener('click', () => { - this.closeCreatePostModal(); - }); - }); - - // Close modal when clicking outside - if (createPostModal) { - createPostModal.addEventListener('click', (e) => { - if (e.target === createPostModal) { - this.closeCreatePostModal(); - } - }); - } - } - - openCreatePostModal() { - const modal = document.getElementById('createPostModal'); - if (modal) { - modal.style.display = 'flex'; - document.body.style.overflow = 'hidden'; - - // Focus on title input - const titleInput = modal.querySelector('#postTitle'); - if (titleInput) { - setTimeout(() => titleInput.focus(), 100); - } - } - } - - closeCreatePostModal() { - const modal = document.getElementById('createPostModal'); - if (modal) { - modal.style.display = 'none'; - document.body.style.overflow = ''; - - // Reset form - const form = modal.querySelector('form'); - if (form) form.reset(); - this.tags.clear(); - this.updateTagsDisplay(); - } - } - - setupCreatePostForm() { - const createPostForm = document.getElementById('createPostForm'); - if (createPostForm) { - createPostForm.addEventListener('submit', (e) => this.handlePostSubmission(e)); - } - - // Image upload preview - const imageInput = document.getElementById('postImages'); - if (imageInput) { - imageInput.addEventListener('change', (e) => this.handleImagePreview(e)); - } - } - - handleImagePreview(e) { - const files = Array.from(e.target.files); - const previewContainer = document.getElementById('imagePreview'); - - if (!previewContainer) return; - - previewContainer.innerHTML = ''; - - files.forEach((file, index) => { - if (file.type.startsWith('image/')) { - const reader = new FileReader(); - reader.onload = (e) => { - const preview = document.createElement('div'); - preview.className = 'image-preview-item'; - preview.innerHTML = ` - Preview ${index + 1} - - `; - previewContainer.appendChild(preview); - }; - reader.readAsDataURL(file); - } - }); - } - - setupTagsInput() { - const tagsInput = document.getElementById('tagsInput'); - const tagsContainer = document.getElementById('tagsContainer'); - - if (tagsInput) { - tagsInput.addEventListener('keypress', (e) => { - if (e.key === 'Enter' || e.key === ',') { - e.preventDefault(); - const tag = e.target.value.trim().replace(/[^a-zA-Z0-9\s]/g, ''); - if (tag && !this.tags.has(tag)) { - this.addTag(tag); - e.target.value = ''; - this.updateTagsDisplay(); - } - } - }); - } - } - - addTag(tag) { - this.tags.add(tag); - this.updateTagsDisplay(); - } - - removeTag(tag) { - this.tags.delete(tag); - this.updateTagsDisplay(); - } - - updateTagsDisplay() { - const tagsContainer = document.getElementById('tagsContainer'); - if (!tagsContainer) return; - - tagsContainer.innerHTML = ''; - - this.tags.forEach(tag => { - const tagElement = document.createElement('span'); - tagElement.className = 'tag'; - tagElement.innerHTML = ` - ${tag} - - `; - tagsContainer.appendChild(tagElement); - }); - } - - setupContentEditor() { - const contentTextarea = document.getElementById('postContent'); - const formatButtons = document.querySelectorAll('.format-btn'); - - formatButtons.forEach(btn => { - btn.addEventListener('click', (e) => { - e.preventDefault(); - const action = btn.dataset.format; - this.applyTextFormatting(action, contentTextarea); - }); - }); - } - - applyTextFormatting(action, textarea) { - if (!textarea) return; - - const start = textarea.selectionStart; - const end = textarea.selectionEnd; - const selectedText = textarea.value.substring(start, end); - const beforeText = textarea.value.substring(0, start); - const afterText = textarea.value.substring(end); - - let replacement = selectedText; - - switch (action) { - case 'bold': - replacement = `**${selectedText || 'bold text'}**`; - break; - case 'italic': - replacement = `*${selectedText || 'italic text'}*`; - break; - case 'code': - replacement = `\`${selectedText || 'code'}\``; - break; - case 'link': - replacement = `[${selectedText || 'link text'}](url)`; - break; - case 'list': - replacement = `\n- ${selectedText || 'list item'}`; - break; - } - - textarea.value = beforeText + replacement + afterText; - - // Set cursor position - const newPosition = start + replacement.length; - textarea.setSelectionRange(newPosition, newPosition); - textarea.focus(); - } - - setupChatFeatures() { - const chatInput = document.getElementById('chatInput'); - const chatSendBtn = document.getElementById('chatSendBtn'); - const chatToggle = document.getElementById('chatToggle'); - const chatContainer = document.querySelector('.live-chat-container'); - - if (chatToggle && chatContainer) { - chatToggle.addEventListener('click', () => { - this.chatVisible = !this.chatVisible; - chatContainer.classList.toggle('minimized', !this.chatVisible); - chatToggle.innerHTML = this.chatVisible ? - '' : ''; - }); - } - - if (chatInput) { - chatInput.addEventListener('keypress', (e) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - this.sendChatMessage(); - } - }); - - chatInput.addEventListener('input', () => { - this.handleTyping(); - }); - } - - if (chatSendBtn) { - chatSendBtn.addEventListener('click', () => { - this.sendChatMessage(); - }); - } - } - - handleTyping() { - if (!this.socket || !api.isAuthenticated()) return; - - this.socket.emit('typing', { - userId: this.currentUser.id, - username: this.currentUser.fullName || this.currentUser.username - }); - - // Clear previous timeout - clearTimeout(this.typingTimeout); - - // Set new timeout to stop typing - this.typingTimeout = setTimeout(() => { - this.socket.emit('stop-typing', { - userId: this.currentUser.id - }); - }, 1000); - } - - sendChatMessage() { - const chatInput = document.getElementById('chatInput'); - if (!chatInput || !chatInput.value.trim()) return; - - const message = chatInput.value.trim(); - - if (!api.isAuthenticated()) { - this.showAuthPrompt('Please log in to participate in chat!'); - return; - } - - // Add message locally first for immediate feedback - const localMessage = { - id: Date.now(), - user: this.currentUser.fullName || this.currentUser.username, - message: message, - timestamp: new Date().toISOString(), - userId: this.currentUser.id - }; - - this.addChatMessage(localMessage, true); - - // Send to server - if (this.socket) { - this.socket.emit('chat-message', localMessage); - } - - chatInput.value = ''; - } - - addChatMessage(message, isLocal = false) { - const chatMessages = document.getElementById('chatMessages'); - if (!chatMessages) return; - - const messageElement = document.createElement('div'); - messageElement.className = `chat-message ${isLocal ? 'local' : ''}`; - messageElement.innerHTML = ` -
${message.user.charAt(0).toUpperCase()}
-
-
${message.user}
-
${this.formatChatMessage(message.message)}
-
${this.formatTime(message.timestamp)}
-
- `; - - chatMessages.appendChild(messageElement); - - // Scroll to bottom - chatMessages.scrollTop = chatMessages.scrollHeight; - - // Remove oldest messages if too many - while (chatMessages.children.length > 100) { - chatMessages.removeChild(chatMessages.firstChild); - } - } - - formatChatMessage(text) { - return text - .replace(/\*\*(.*?)\*\*/g, '$1') - .replace(/\*(.*?)\*/g, '$1') - .replace(/`(.*?)`/g, '$1') - .replace(/(https?:\/\/[^\s]+)/g, '$1'); - } - - formatTime(timestamp) { - const date = new Date(timestamp); - const now = new Date(); - const diffMs = now - date; - const diffMins = Math.floor(diffMs / 60000); - - if (diffMins < 1) return 'now'; - if (diffMins < 60) return `${diffMins}m ago`; - if (diffMins < 1440) return `${Math.floor(diffMins / 60)}h ago`; - return date.toLocaleDateString(); - } - - showTypingIndicator(data) { - // Show typing indicator for other users - if (data.userId !== this.currentUser.id) { - console.log(`${data.username} is typing...`); - } - } - - hideTypingIndicator(data) { - // Hide typing indicator - console.log(`${data.username} stopped typing`); - } - - async handlePostSubmission(e) { - e.preventDefault(); - - if (!api.isAuthenticated()) { - this.showAuthPrompt('Please log in to create posts!'); - return; - } - - const formData = new FormData(e.target); - const submitBtn = e.target.querySelector('.publish-btn'); - const originalText = submitBtn.innerHTML; - - try { - submitBtn.innerHTML = ' Publishing...'; - submitBtn.disabled = true; - - const postData = { - title: formData.get('title') || '', - content: formData.get('content'), - category: formData.get('category') || 'general', - tags: Array.from(this.tags), - author: { - id: this.currentUser.id, - fullName: this.currentUser.fullName || this.currentUser.username, - profileImage: this.currentUser.profileImage - } - }; - - // Handle images - const imageFiles = Array.from(e.target.querySelectorAll('input[type="file"]')) - .map(input => input.files[0]) - .filter(file => file); - - // Create post (mock implementation for now) - const newPost = await this.createPost(postData, imageFiles); - - if (this.socket) { - this.socket.emit('new-post', newPost); - } - - // Show success and close modal - this.showNotification('Post published successfully!', 'success'); - this.closeCreatePostModal(); - - // Reload posts - await this.loadPosts(); - - } catch (error) { - console.error('Failed to create post:', error); - this.showNotification(error.message || 'Failed to create post', 'error'); - } finally { - submitBtn.innerHTML = originalText; - submitBtn.disabled = false; - } - } - - async createPost(postData, imageFiles = []) { - // Mock implementation - replace with actual API call - const post = { - id: Date.now().toString(), - ...postData, - createdAt: new Date().toISOString(), - likes: 0, - shares: 0, - images: [], // Would upload images and get URLs - verified: false - }; - - return post; - } - - async loadCommunityData() { - try { - // Load posts - await this.loadPosts(); - - // Load trending topics - await this.loadTrendingTopics(); - - // Load active users - await this.loadActiveUsers(); - - // Load study groups - await this.loadStudyGroups(); - - // Load initial chat messages - this.loadInitialChatMessages(); - - } catch (error) { - console.error('Failed to load community data:', error); - this.showNotification('Some features may not work properly', 'warning'); - } - } - - async loadPosts() { - const postsContainer = document.querySelector('.posts-feed'); - if (!postsContainer) return; - - // Mock posts for now - replace with API call - const mockPosts = [ - { - id: '1', - title: 'Getting Started with React Hooks', - content: 'Just finished learning about useState and useEffect. The way React handles state management is amazing! Here\'s what I learned...', - author: { - id: 'user1', - fullName: 'Sarah Chen', - profileImage: null, - verified: true - }, - category: 'React', - tags: ['React', 'JavaScript', 'Frontend'], - createdAt: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(), - likes: 24, - shares: 5, - images: [] - }, - { - id: '2', - title: '', - content: 'Anyone else excited about the new Python 3.12 features? The performance improvements are incredible! 🚀\n\nJust migrated our backend and saw 15% speed improvement.', - author: { - id: 'user2', - fullName: 'Mike Johnson', - profileImage: null, - verified: false - }, - category: 'Python', - tags: ['Python', 'Backend', 'Performance'], - createdAt: new Date(Date.now() - 4 * 60 * 60 * 1000).toISOString(), - likes: 18, - shares: 3, - images: [] - }, - { - id: '3', - title: 'My First Machine Learning Project', - content: 'Built a sentiment analysis model using TensorFlow! It can classify movie reviews as positive or negative with 89% accuracy. Super proud of this milestone 💪', - author: { - id: 'user3', - fullName: 'Emma Davis', - profileImage: null, - verified: false - }, - category: 'Machine Learning', - tags: ['ML', 'TensorFlow', 'Python', 'NLP'], - createdAt: new Date(Date.now() - 6 * 60 * 60 * 1000).toISOString(), - likes: 42, - shares: 12, - images: [] - } - ]; - - postsContainer.innerHTML = ''; - - for (const post of mockPosts) { - const postElement = this.createPostElement(post); - postsContainer.appendChild(postElement); - } - } - - createPostElement(post) { - const postDiv = document.createElement('article'); - postDiv.className = 'post'; - postDiv.id = `post-${post.id}`; - - const isLiked = false; // TODO: Check if current user liked this post - - postDiv.innerHTML = ` -
- -
- -
-
- -
- ${post.title ? `

${post.title}

` : ''} -
${this.formatPostContent(post.content)}
- ${post.images && post.images.length > 0 ? this.createImageGallery(post.images) : ''} - ${post.tags && post.tags.length > 0 ? this.createTagsHTML(post.tags) : ''} -
- - - - - `; - - return postDiv; - } - - formatPostContent(content) { - return content - .replace(/```([\s\S]*?)```/g, '
$1
') - .replace(/`([^`]+)`/g, '$1') - .replace(/\*\*(.*?)\*\*/g, '$1') - .replace(/\*(.*?)\*/g, '$1') - .replace(/(https?:\/\/[^\s]+)/g, '$1') - .replace(/\n/g, '
'); - } - - createImageGallery(images) { - if (images.length === 1) { - return `
Post image
`; - } else { - const imageHTML = images.map((image, index) => - `Post image ${index + 1}` - ).join(''); - return `
${imageHTML}
`; - } - } - - createTagsHTML(tags) { - const tagsHTML = tags.map(tag => `${tag}`).join(''); - return ``; - } - - async loadTrendingTopics() { - const trendingContainer = document.querySelector('.trending-list'); - if (!trendingContainer) return; - - const trendingTopics = [ - { topic: '#ReactJS', posts: 156 }, - { topic: '#JavaScript', posts: 142 }, - { topic: '#Python', posts: 128 }, - { topic: '#WebDev', posts: 98 }, - { topic: '#AI', posts: 87 }, - { topic: '#OpenSource', posts: 76 } - ]; - - trendingContainer.innerHTML = ''; - - trendingTopics.forEach(item => { - const trendingDiv = document.createElement('div'); - trendingDiv.className = 'trending-item'; - trendingDiv.innerHTML = ` - - - - `; - trendingDiv.addEventListener('click', () => { - this.filterPostsByTag(item.topic); - }); - trendingContainer.appendChild(trendingDiv); - }); - } - - async loadActiveUsers() { - const usersContainer = document.querySelector('.users-list'); - if (!usersContainer) return; - - const activeUsers = [ - { id: '1', name: 'Sarah Chen', status: 'coding', avatar: null }, - { id: '2', name: 'Mike Johnson', status: 'online', avatar: null }, - { id: '3', name: 'Emma Davis', status: 'studying', avatar: null }, - { id: '4', name: 'Alex Rodriguez', status: 'debugging', avatar: null }, - { id: '5', name: 'Lisa Wang', status: 'online', avatar: null } - ]; - - usersContainer.innerHTML = ''; - - activeUsers.forEach(user => { - const userDiv = document.createElement('div'); - userDiv.className = 'active-user'; - userDiv.innerHTML = ` -
- ${user.avatar ? - `${user.name}` : - `${user.name.charAt(0)}` - } -
-
- - - `; - usersContainer.appendChild(userDiv); - }); - - // Update online count - const onlineCount = document.getElementById('onlineCount'); - if (onlineCount) { - onlineCount.textContent = `${activeUsers.length} online`; - } - } - - async loadStudyGroups() { - const groupsContainer = document.querySelector('.groups-list'); - if (!groupsContainer) return; - - const studyGroups = [ - { id: '1', name: 'React Masters', members: 234, topic: 'React', activity: 'high' }, - { id: '2', name: 'Python Wizards', members: 189, topic: 'Python', activity: 'high' }, - { id: '3', name: 'Full Stack Warriors', members: 156, topic: 'Full Stack', activity: 'medium' }, - { id: '4', name: 'AI Enthusiasts', members: 298, topic: 'AI/ML', activity: 'high' }, - { id: '5', name: 'Open Source Contributors', members: 142, topic: 'Open Source', activity: 'medium' } - ]; - - groupsContainer.innerHTML = ''; - - studyGroups.forEach(group => { - const groupDiv = document.createElement('div'); - groupDiv.className = 'study-group'; - groupDiv.innerHTML = ` -
-
${group.name}
-
- ${group.members} members - ${group.topic} -
-
-
-
-
- - `; - groupsContainer.appendChild(groupDiv); - }); - } - - loadInitialChatMessages() { - const chatMessages = document.getElementById('chatMessages'); - if (!chatMessages) return; - - const initialMessages = [ - { user: 'Sarah Chen', message: 'Welcome everyone to the OpenRockets community! 🚀', timestamp: new Date(Date.now() - 10 * 60000).toISOString() }, - { user: 'Mike Johnson', message: 'Just deployed my first React app! Thanks for all the help!', timestamp: new Date(Date.now() - 8 * 60000).toISOString() }, - { user: 'Emma Davis', message: 'Anyone working on machine learning projects? Would love to collaborate!', timestamp: new Date(Date.now() - 5 * 60000).toISOString() }, - { user: 'Alex Rodriguez', message: 'Check out this cool Python library I found: https://github.com/example/library', timestamp: new Date(Date.now() - 2 * 60000).toISOString() } - ]; - - initialMessages.forEach(message => { - this.addChatMessage(message); - }); - } - - // Interaction Methods - toggleLike(postId, button) { - if (!api.isAuthenticated()) { - this.showAuthPrompt('Please log in to like posts!'); - return; - } - - button.classList.toggle('liked'); - const span = button.querySelector('span'); - const icon = button.querySelector('i'); - - if (button.classList.contains('liked')) { - span.textContent = 'Liked'; - icon.style.color = '#ff6b6b'; - // TODO: Send like to server - } else { - span.textContent = 'Like'; - icon.style.color = ''; - // TODO: Send unlike to server - } - } - - toggleComments(postId) { - const commentsSection = document.getElementById(`comments-${postId}`); - const isVisible = commentsSection.style.display !== 'none'; - - commentsSection.style.display = isVisible ? 'none' : 'block'; - - if (!isVisible) { - const commentInput = commentsSection.querySelector('.comment-input'); - if (commentInput) { - commentInput.focus(); - } - } - } - - sharePost(postId) { - if (navigator.share) { - navigator.share({ - title: 'Check out this post on OpenRockets', - url: `${window.location.origin}/community.html#post-${postId}` - }); - } else { - // Fallback: copy to clipboard - navigator.clipboard.writeText(`${window.location.origin}/community.html#post-${postId}`); - this.showNotification('Link copied to clipboard!', 'success'); - } - } - - submitComment(postId) { - if (!api.isAuthenticated()) { - this.showAuthPrompt('Please log in to comment!'); - return; - } - - const commentsSection = document.getElementById(`comments-${postId}`); - const commentInput = commentsSection.querySelector('.comment-input'); - const content = commentInput.value.trim(); - - if (!content) return; - - // TODO: Send comment to server - commentInput.value = ''; - this.showNotification('Comment added!', 'success'); - } - - handleCommentKeypress(event, postId) { - if (event.key === 'Enter') { - event.preventDefault(); - this.submitComment(postId); - } - } - - showPostMenu(postId) { - // TODO: Implement post menu (edit, delete, report, etc.) - console.log('Show menu for post:', postId); - } - - joinStudyGroup(groupId) { - if (!api.isAuthenticated()) { - this.showAuthPrompt('Please log in to join study groups!'); - return; - } - // TODO: Implement join study group - this.showNotification('Joined study group!', 'success'); - } - - startDirectMessage(userId) { - if (!api.isAuthenticated()) { - this.showAuthPrompt('Please log in to send messages!'); - return; - } - // TODO: Implement direct messaging - console.log('Start DM with user:', userId); - } - - filterPostsByTag(tag) { - // TODO: Implement tag filtering - console.log('Filter posts by tag:', tag); - } - - openImageModal(imageSrc) { - // TODO: Implement image modal - console.log('Open image modal:', imageSrc); - } - - addNewPost(post) { - const postsContainer = document.querySelector('.posts-feed'); - if (postsContainer) { - const postElement = this.createPostElement(post); - postsContainer.insertBefore(postElement, postsContainer.firstChild); - } - } - - showAuthPrompt(message) { - // Use the auth prompt from community-integration.js - if (typeof showAuthPrompt === 'function') { - showAuthPrompt(message); - } else { - alert(message); // Fallback - } - } - - showNotification(message, type = 'info') { - // Simple notification implementation - const notification = document.createElement('div'); - notification.className = `notification ${type}`; - notification.textContent = message; - - Object.assign(notification.style, { - position: 'fixed', - top: '20px', - right: '20px', - padding: '12px 24px', - backgroundColor: type === 'success' ? '#4CAF50' : type === 'error' ? '#f44336' : '#2196F3', - color: 'white', - borderRadius: '4px', - zIndex: '10000', - animation: 'slideInRight 0.3s ease' - }); - - document.body.appendChild(notification); - - setTimeout(() => { - notification.remove(); - }, 3000); - } -} - -// Initialize community manager when DOM is loaded -let communityManager; - -document.addEventListener('DOMContentLoaded', () => { - // Only initialize if we're on the community page - if (document.querySelector('.community-page')) { - communityManager = new CommunityManager(); - } -}); - -// Make it globally accessible for onclick handlers -window.communityManager = communityManager; diff --git a/scripts/community.js b/scripts/community.js deleted file mode 100644 index af06ca9..0000000 --- a/scripts/community.js +++ /dev/null @@ -1,908 +0,0 @@ -// Enhanced Community Page JavaScript with Real-time Features -class CommunityManager { - constructor() { - this.socket = null; - this.currentUser = null; - this.chatVisible = false; - this.typingTimeout = null; - this.tags = new Set(); - - this.init(); - } - - async init() { - // Initialize WebSocket connection - this.initializeSocket(); - - // Set up UI components - this.setupModalHandlers(); - this.setupCreatePostForm(); - this.setupChatFeatures(); - this.setupRealTimeUpdates(); - this.setupTagsInput(); - this.setupContentEditor(); - - // Load initial data - await this.loadCommunityData(); - - console.log('🚀 Community Manager initialized'); - } - - initializeSocket() { - this.socket = io('ws://localhost:3000', { - autoConnect: true, - reconnection: true, - reconnectionAttempts: 5, - reconnectionDelay: 1000 - }); - - this.socket.on('connect', () => { - console.log('🔌 Connected to community server'); - - // Join as current user - const user = api.currentUser || { id: 'guest', username: 'Guest User' }; - this.socket.emit('join-user', { - userId: user.id, - username: user.fullName || user.username || 'Guest User' - }); - }); - - this.socket.on('disconnect', () => { - console.log('🔌 Disconnected from community server'); - }); - - this.socket.on('new-message', (message) => { - this.addChatMessage(message); - }); - - this.socket.on('new-post', (post) => { - this.addNewPost(post); - }); - - this.socket.on('user-joined', (user) => { - console.log('User joined:', user.username); - }); - - this.socket.on('user-left', (user) => { - console.log('User left:', user.username); - }); - - this.socket.on('typing', (data) => { - this.showTypingIndicator(data); - }); - - this.socket.on('stop-typing', (data) => { - this.hideTypingIndicator(data); - }); - - // Real-time message handlers - this.socket.on('new-message', (message) => { - this.addChatMessage(message); - }); - - this.socket.on('user-typing', (data) => { - this.showTypingIndicator(data); - }); - - this.socket.on('user-stop-typing', (data) => { - this.hideTypingIndicator(data); - }); - - this.socket.on('online-count-update', (count) => { - this.updateOnlineCount(count); - }); - - this.socket.on('new-post', (post) => { - this.addNewPostToFeed(post); - }); - - this.socket.on('post-like-update', (data) => { - this.updatePostLikes(data); - }); - } - - setupModalHandlers() { - // Create Post Modal - const createPostBtn = document.getElementById('createPostBtn'); - const createPostModal = document.getElementById('createPostModal'); - const closeModal = document.getElementById('closeModal'); - const cancelPost = document.getElementById('cancelPost'); - - createPostBtn?.addEventListener('click', () => this.openCreatePostModal()); - closeModal?.addEventListener('click', () => this.closeCreatePostModal()); - cancelPost?.addEventListener('click', () => this.closeCreatePostModal()); - - // Chat Modal - const chatToggleBtn = document.getElementById('chatToggleBtn'); - const chatModal = document.getElementById('chatModal'); - const closeChatModal = document.getElementById('closeChatModal'); - const chatMinimizeBtn = document.getElementById('chatMinimizeBtn'); - - chatToggleBtn?.addEventListener('click', () => this.toggleChatModal()); - closeChatModal?.addEventListener('click', () => this.closeChatModal()); - chatMinimizeBtn?.addEventListener('click', () => this.minimizeChatModal()); - - // Close modals on outside click - window.addEventListener('click', (e) => { - if (e.target === createPostModal) { - this.closeCreatePostModal(); - } - if (e.target === chatModal) { - this.closeChatModal(); - } - }); - } - - setupCreatePostForm() { - const form = document.getElementById('createPostForm'); - const publishBtn = document.getElementById('publishPost'); - const saveDraftBtn = document.getElementById('saveDraftBtn'); - - form?.addEventListener('submit', (e) => this.handlePostSubmission(e)); - publishBtn?.addEventListener('click', (e) => this.publishPost(e)); - saveDraftBtn?.addEventListener('click', () => this.saveDraft()); - - // Image upload handler - const imageUpload = document.getElementById('imageUpload'); - imageUpload?.addEventListener('change', (e) => this.handleImageUpload(e)); - - // Character counter - const contentTextarea = document.getElementById('postContent'); - const charCount = document.getElementById('charCount'); - - contentTextarea?.addEventListener('input', () => { - const count = contentTextarea.value.length; - charCount.textContent = count; - - if (count > 1800) { - charCount.style.color = '#EF4444'; - } else if (count > 1500) { - charCount.style.color = '#F59E0B'; - } else { - charCount.style.color = 'var(--text-muted)'; - } - }); - } - - setupTagsInput() { - const tagsInput = document.getElementById('postTags'); - const tagsDisplay = document.getElementById('tagsDisplay'); - const suggestions = document.querySelectorAll('.tag-suggestion'); - - tagsInput?.addEventListener('keydown', (e) => { - if (e.key === 'Enter' || e.key === ',') { - e.preventDefault(); - this.addTag(tagsInput.value.trim()); - tagsInput.value = ''; - } - }); - - tagsInput?.addEventListener('blur', () => { - if (tagsInput.value.trim()) { - this.addTag(tagsInput.value.trim()); - tagsInput.value = ''; - } - }); - - suggestions.forEach(suggestion => { - suggestion.addEventListener('click', () => { - this.addTag(suggestion.dataset.tag); - }); - }); - } - - addTag(tag) { - if (!tag || this.tags.has(tag)) return; - - this.tags.add(tag); - this.updateTagsDisplay(); - } - - removeTag(tag) { - this.tags.delete(tag); - this.updateTagsDisplay(); - } - - updateTagsDisplay() { - const tagsDisplay = document.getElementById('tagsDisplay'); - if (!tagsDisplay) return; - - tagsDisplay.innerHTML = ''; - - this.tags.forEach(tag => { - const tagElement = document.createElement('div'); - tagElement.className = 'tag-item'; - tagElement.innerHTML = ` - ${tag.startsWith('#') ? tag : '#' + tag} - × - `; - tagsDisplay.appendChild(tagElement); - }); - } - - setupContentEditor() { - const toolbar = document.querySelectorAll('.toolbar-btn'); - const contentTextarea = document.getElementById('postContent'); - - toolbar.forEach(btn => { - btn.addEventListener('click', () => { - const action = btn.dataset.action; - this.applyTextFormatting(action, contentTextarea); - }); - }); - } - - applyTextFormatting(action, textarea) { - const start = textarea.selectionStart; - const end = textarea.selectionEnd; - const selectedText = textarea.value.substring(start, end); - let replacement = ''; - - switch (action) { - case 'bold': - replacement = `**${selectedText || 'bold text'}**`; - break; - case 'italic': - replacement = `*${selectedText || 'italic text'}*`; - break; - case 'code': - replacement = `\`${selectedText || 'code'}\``; - break; - case 'code-block': - replacement = `\`\`\`javascript\n${selectedText || 'your code here'}\n\`\`\``; - break; - case 'quote': - replacement = `> ${selectedText || 'quote text'}`; - break; - case 'list': - replacement = `- ${selectedText || 'list item'}`; - break; - case 'link': - const url = prompt('Enter URL:'); - if (url) { - replacement = `[${selectedText || 'link text'}](${url})`; - } - break; - } - - if (replacement) { - textarea.value = textarea.value.substring(0, start) + replacement + textarea.value.substring(end); - textarea.focus(); - - // Update character counter - const charCount = document.getElementById('charCount'); - if (charCount) { - charCount.textContent = textarea.value.length; - } - } - } - - setupChatFeatures() { - const chatInput = document.getElementById('chatInput'); - const chatSendBtn = document.getElementById('chatSendBtn'); - const emojiToggleBtn = document.getElementById('emojiToggleBtn'); - const emojiPicker = document.getElementById('chatEmojiPicker'); - - // Chat input handlers - chatInput?.addEventListener('input', (e) => { - chatSendBtn.disabled = !e.target.value.trim(); - - // Show typing indicator - this.handleTyping(); - }); - - chatInput?.addEventListener('keypress', (e) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - this.sendChatMessage(); - } - }); - - chatSendBtn?.addEventListener('click', () => this.sendChatMessage()); - - // Emoji picker - emojiToggleBtn?.addEventListener('click', () => { - emojiPicker.style.display = emojiPicker.style.display === 'flex' ? 'none' : 'flex'; - }); - - document.querySelectorAll('.emoji-btn').forEach(btn => { - btn.addEventListener('click', () => { - const emoji = btn.dataset.emoji; - chatInput.value += emoji; - chatInput.focus(); - chatSendBtn.disabled = !chatInput.value.trim(); - }); - }); - - // Auto-scroll chat messages - this.setupChatAutoScroll(); - } - - handleTyping() { - if (this.socket) { - this.socket.emit('typing-start', { channel: 'general' }); - - clearTimeout(this.typingTimeout); - this.typingTimeout = setTimeout(() => { - this.socket.emit('typing-stop', { channel: 'general' }); - }, 1000); - } - } - - sendChatMessage() { - const chatInput = document.getElementById('chatInput'); - const message = chatInput.value.trim(); - - if (!message || !this.socket) return; - - this.socket.emit('send-message', { - message, - channel: 'general' - }); - - chatInput.value = ''; - document.getElementById('chatSendBtn').disabled = true; - } - - addChatMessage(message) { - const chatMessages = document.getElementById('chatMessages'); - if (!chatMessages) return; - - const messageElement = document.createElement('div'); - messageElement.className = 'chat-message'; - - const timeAgo = this.getTimeAgo(new Date(message.timestamp)); - - messageElement.innerHTML = ` -
${this.getAvatarInitials(message.username)}
-
-
${message.username}
-
${this.formatChatMessage(message.message)}
-
${timeAgo}
-
- `; - - chatMessages.appendChild(messageElement); - - // Animate message appearance - requestAnimationFrame(() => { - messageElement.style.opacity = '1'; - }); - - // Auto-scroll to bottom - chatMessages.scrollTop = chatMessages.scrollHeight; - - // Play notification sound (optional) - this.playNotificationSound(); - } - - formatChatMessage(text) { - return text - .replace(/\*\*(.*?)\*\*/g, '$1') - .replace(/\*(.*?)\*/g, '$1') - .replace(/`(.*?)`/g, '$1') - .replace(/(https?:\/\/[^\s]+)/g, '$1'); - } - - showTypingIndicator(data) { - const typingIndicator = document.getElementById('typingIndicator'); - if (typingIndicator) { - typingIndicator.innerHTML = `${data.username} is typing...`; - typingIndicator.style.display = 'block'; - } - } - - hideTypingIndicator(data) { - const typingIndicator = document.getElementById('typingIndicator'); - if (typingIndicator) { - typingIndicator.style.display = 'none'; - } - } - - updateOnlineCount(count) { - const onlineCount = document.getElementById('onlineCount'); - if (onlineCount) { - onlineCount.textContent = `${count} online`; - } - } - - async handlePostSubmission(e) { - e.preventDefault(); - return this.publishPost(e); - } - - async publishPost(e) { - e.preventDefault(); - - const form = document.getElementById('createPostForm'); - const publishBtn = document.getElementById('publishPost'); - const formData = new FormData(form); - - // Add tags - formData.set('tags', Array.from(this.tags).join(',')); - - // Add images - const imageUpload = document.getElementById('imageUpload'); - if (imageUpload.files.length > 0) { - for (let i = 0; i < imageUpload.files.length; i++) { - formData.append('images', imageUpload.files[i]); - } - } - - try { - publishBtn.innerHTML = ' Publishing...'; - publishBtn.disabled = true; - - const response = await fetch('/api/posts', { - method: 'POST', - headers: { - 'Authorization': `Bearer ${localStorage.getItem('token')}` - }, - body: formData - }); - - if (response.ok) { - const result = await response.json(); - this.showNotification('Post published successfully! 🎉', 'success'); - this.closeCreatePostModal(); - this.resetForm(); - } else { - throw new Error('Failed to publish post'); - } - } catch (error) { - console.error('Error publishing post:', error); - this.showNotification('Failed to publish post. Please try again.', 'error'); - } finally { - publishBtn.innerHTML = ' Publish Post'; - publishBtn.disabled = false; - } - } - - handleImageUpload(e) { - const files = Array.from(e.target.files); - const previewArea = document.getElementById('imagePreviewArea'); - const previews = document.getElementById('imagePreviews'); - - if (files.length === 0) { - previewArea.style.display = 'none'; - return; - } - - previewArea.style.display = 'block'; - previews.innerHTML = ''; - - files.forEach((file, index) => { - const reader = new FileReader(); - reader.onload = (e) => { - const preview = document.createElement('div'); - preview.className = 'image-preview'; - preview.innerHTML = ` - Preview ${index + 1} - - `; - previews.appendChild(preview); - }; - reader.readAsDataURL(file); - }); - } - - async loadCommunityData() { - try { - // Load trending topics - await this.loadTrendingTopics(); - - // Load active users - await this.loadActiveUsers(); - - // Load posts - await this.loadPosts(); - - } catch (error) { - console.error('Failed to load community data:', error); - } - } - - async loadTrendingTopics() { - try { - const response = await fetch('/api/trending', { - headers: { - 'Authorization': `Bearer ${localStorage.getItem('token')}` - } - }); - - if (response.ok) { - const data = await response.json(); - this.updateTrendingTopics(data.trending); - } - } catch (error) { - console.error('Failed to load trending topics:', error); - } - } - - updateTrendingTopics(trending) { - // Update trending topics display - trending.slice(0, 4).forEach((item, index) => { - const trendingItem = document.querySelector(`[data-trend="${item.topic}"]`); - if (trendingItem) { - const postsSpan = trendingItem.querySelector('.trending-posts'); - const trendIcon = postsSpan.querySelector('i'); - - postsSpan.innerHTML = ` - - ${item.posts} posts today - `; - } - }); - } - - openCreatePostModal() { - const modal = document.getElementById('createPostModal'); - modal.classList.add('show'); - document.body.style.overflow = 'hidden'; - - // Focus on content textarea - setTimeout(() => { - document.getElementById('postContent')?.focus(); - }, 100); - } - - closeCreatePostModal() { - const modal = document.getElementById('createPostModal'); - modal.classList.remove('show'); - document.body.style.overflow = ''; - this.resetForm(); - } - - toggleChatModal() { - const modal = document.getElementById('chatModal'); - if (modal.classList.contains('show')) { - this.closeChatModal(); - } else { - modal.classList.add('show'); - document.body.style.overflow = 'hidden'; - } - } - - closeChatModal() { - const modal = document.getElementById('chatModal'); - modal.classList.remove('show'); - document.body.style.overflow = ''; - } - - minimizeChatModal() { - this.closeChatModal(); - // Could implement a minimized chat window here - } - - resetForm() { - const form = document.getElementById('createPostForm'); - form?.reset(); - this.tags.clear(); - this.updateTagsDisplay(); - - const previewArea = document.getElementById('imagePreviewArea'); - if (previewArea) previewArea.style.display = 'none'; - - const charCount = document.getElementById('charCount'); - if (charCount) charCount.textContent = '0'; - } - - showNotification(message, type = 'info') { - const notification = document.createElement('div'); - notification.className = `notification notification-${type}`; - notification.innerHTML = ` -
- - ${message} -
- - `; - - document.body.appendChild(notification); - - // Auto-remove after 5 seconds - setTimeout(() => { - notification.remove(); - }, 5000); - - // Close button handler - notification.querySelector('.notification-close').addEventListener('click', () => { - notification.remove(); - }); - } - - getAvatarInitials(name) { - return name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2); - } - - getTimeAgo(date) { - const now = new Date(); - const diffInSeconds = Math.floor((now - date) / 1000); - - if (diffInSeconds < 60) return 'Just now'; - if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)} min ago`; - if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)} hours ago`; - return `${Math.floor(diffInSeconds / 86400)} days ago`; - } - - playNotificationSound() { - // Create a subtle notification sound - const audioContext = new (window.AudioContext || window.webkitAudioContext)(); - const oscillator = audioContext.createOscillator(); - const gainNode = audioContext.createGain(); - - oscillator.connect(gainNode); - gainNode.connect(audioContext.destination); - - oscillator.frequency.value = 800; - oscillator.type = 'sine'; - gainNode.gain.value = 0.1; - - oscillator.start(); - oscillator.stop(audioContext.currentTime + 0.1); - } - - setupRealTimeUpdates() { - // Set up real-time post interactions - document.addEventListener('click', (e) => { - if (e.target.closest('.action-btn')) { - const btn = e.target.closest('.action-btn'); - const post = btn.closest('.post'); - const postId = post?.id?.replace('post-', ''); - - if (btn.querySelector('.fa-heart')) { - this.handlePostLike(postId, btn); - } - } - }); - - // Set up trending topic clicks - document.addEventListener('click', (e) => { - if (e.target.closest('.trending-item')) { - const trendItem = e.target.closest('.trending-item'); - const trend = trendItem.dataset.trend; - this.filterPostsByTrend(trend); - } - }); - } - - async handlePostLike(postId, button) { - try { - const response = await fetch(`/api/posts/${postId}/like`, { - method: 'POST', - headers: { - 'Authorization': `Bearer ${localStorage.getItem('token')}`, - 'Content-Type': 'application/json' - } - }); - - if (response.ok) { - const result = await response.json(); - button.classList.add('liked'); - - // Update like count - const post = button.closest('.post'); - const likesSpan = post.querySelector('.post-stats span:first-child'); - if (likesSpan) { - likesSpan.textContent = `${result.likes} likes`; - } - } - } catch (error) { - console.error('Failed to like post:', error); - this.showNotification('Failed to like post', 'error'); - } - } - - setupChatAutoScroll() { - const chatMessages = document.getElementById('chatMessages'); - if (chatMessages) { - // Smooth scrolling behavior - chatMessages.style.scrollBehavior = 'smooth'; - - // Auto-scroll when new messages arrive - const observer = new MutationObserver(() => { - chatMessages.scrollTop = chatMessages.scrollHeight; - }); - - observer.observe(chatMessages, { childList: true }); - } - } -} - -// Initialize Community Manager when DOM is loaded -document.addEventListener('DOMContentLoaded', () => { - window.communityManager = new CommunityManager(); -}); - -// Legacy function support for backward compatibility -function openCreatePostModal() { - window.communityManager?.openCreatePostModal(); -} - -function closeCreatePostModal() { - window.communityManager?.closeCreatePostModal(); -} -// Legacy function support for backward compatibility -function openCreatePostModal() { - window.communityManager?.openCreatePostModal(); -} - -function closeCreatePostModal() { - window.communityManager?.closeCreatePostModal(); -} - -// Add notification styles to head -const notificationStyles = ` - -`; - -// Inject styles into head -if (!document.head.querySelector('#community-dynamic-styles')) { - const styleElement = document.createElement('div'); - styleElement.id = 'community-dynamic-styles'; - styleElement.innerHTML = notificationStyles; - document.head.appendChild(styleElement); -} diff --git a/scripts/dashboard-integration.js b/scripts/dashboard-integration.js deleted file mode 100644 index 9c613dc..0000000 --- a/scripts/dashboard-integration.js +++ /dev/null @@ -1,560 +0,0 @@ -// Dashboard Integration with Backend -document.addEventListener('DOMContentLoaded', async function() { - // Ensure user is authenticated - if (!api.requireAuth()) return; - - // Load dashboard data - loadDashboardData(); - - // Set up real-time updates - setupRealTimeUpdates(); -}); - -async function loadDashboardData() { - try { - // Show loading state - showDashboardLoading(true); - - // Load user profile data - await loadUserProfile(); - - // Load upcoming events - await loadUpcomingEvents(); - - // Load recent posts - await loadRecentPosts(); - - // Load user progress - await loadUserProgress(); - - // Load notifications - await loadNotifications(); - - } catch (error) { - console.error('Failed to load dashboard data:', error); - api.showNotification('Failed to load dashboard data', 'error'); - } finally { - showDashboardLoading(false); - } -} - -async function loadUserProfile() { - try { - const response = await api.getProfile(); - const user = response.user; - - // Update user info in sidebar - const userAvatarElements = document.querySelectorAll('.user-avatar'); - const userNameElements = document.querySelectorAll('.user-name'); - const userTypeElements = document.querySelectorAll('.user-type'); - - userNameElements.forEach(element => { - element.textContent = user.fullName || user.username; - }); - - userTypeElements.forEach(element => { - element.textContent = user.userType === 'instructor' ? 'Student Lecturer' : 'Student Learner'; - }); - - userAvatarElements.forEach(element => { - if (user.profileImage) { - if (element.tagName === 'IMG') { - element.src = user.profileImage; - } else { - element.style.backgroundImage = `url(${user.profileImage})`; - } - } else { - // Use initials - const initials = api.getAvatarInitials(user.fullName || user.username); - element.textContent = initials; - } - }); - - // Update user stats - updateUserStats(user); - - } catch (error) { - console.error('Failed to load user profile:', error); - } -} - -async function loadUpcomingEvents() { - try { - const today = new Date(); - const nextWeek = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000); - - const response = await api.getEvents({ - date: today.toISOString().split('T')[0] - }); - - const events = response.events || []; - - // Update schedule section - const scheduleContainer = document.querySelector('.schedule-list'); - if (scheduleContainer) { - scheduleContainer.innerHTML = ''; - - if (events.length === 0) { - scheduleContainer.innerHTML = ` -
- -

No upcoming classes today

- Browse Classes -
- `; - } else { - events.slice(0, 5).forEach(event => { - const eventElement = createEventElement(event); - scheduleContainer.appendChild(eventElement); - }); - } - } - - } catch (error) { - console.error('Failed to load events:', error); - } -} - -async function loadRecentPosts() { - try { - const response = await api.getPosts({ limit: 3 }); - const posts = response.posts || []; - - // Update community highlights section - const communityContainer = document.querySelector('.community-highlights .highlights-list'); - if (communityContainer) { - communityContainer.innerHTML = ''; - - if (posts.length === 0) { - communityContainer.innerHTML = ` -
- -

No recent posts

- Explore Community -
- `; - } else { - posts.forEach(post => { - const postElement = createPostHighlight(post); - communityContainer.appendChild(postElement); - }); - } - } - - } catch (error) { - console.error('Failed to load recent posts:', error); - } -} - -async function loadUserProgress() { - try { - // For now, use mock data - // In a real app, this would come from the backend - const progressData = { - coursesCompleted: api.currentUser.coursesCompleted || 0, - totalCourses: 12, - studyStreak: api.currentUser.studyStreak || 0, - hoursThisWeek: 14, - skillsLearned: api.currentUser.skills ? api.currentUser.skills.length : 0 - }; - - updateProgressStats(progressData); - - } catch (error) { - console.error('Failed to load user progress:', error); - } -} - -async function loadNotifications() { - try { - const response = await api.getNotifications(); - const notifications = response.notifications || []; - - // Update notification count - const notificationBadge = document.querySelector('.notification-badge'); - const unreadCount = notifications.filter(n => !n.read).length; - - if (notificationBadge) { - if (unreadCount > 0) { - notificationBadge.textContent = unreadCount > 99 ? '99+' : unreadCount; - notificationBadge.style.display = 'block'; - } else { - notificationBadge.style.display = 'none'; - } - } - - // Update recent activity - const activityContainer = document.querySelector('.activity-list'); - if (activityContainer) { - activityContainer.innerHTML = ''; - - if (notifications.length === 0) { - activityContainer.innerHTML = ` -
- -

No recent activity

-
- `; - } else { - notifications.slice(0, 5).forEach(notification => { - const activityElement = createActivityElement(notification); - activityContainer.appendChild(activityElement); - }); - } - } - - } catch (error) { - console.error('Failed to load notifications:', error); - } -} - -function createEventElement(event) { - const eventDiv = document.createElement('div'); - eventDiv.className = 'schedule-item'; - - const startTime = new Date(`${event.date} ${event.startTime}`); - const endTime = new Date(`${event.date} ${event.endTime}`); - - eventDiv.innerHTML = ` -
- ${formatTime(startTime)} - ${calculateDuration(startTime, endTime)} -
-
-

${event.title}

-

with ${event.instructor.fullName}

-
- ${event.type} - ${event.attendees.length}/${event.maxAttendees} -
-
-
- -
- `; - - return eventDiv; -} - -function createPostHighlight(post) { - const postDiv = document.createElement('div'); - postDiv.className = 'highlight-item'; - - postDiv.innerHTML = ` -
-
-
- ${post.author.profileImage ? - `${post.author.fullName}` : - `${api.getAvatarInitials(post.author.fullName)}` - } -
-
- ${post.author.fullName} - ${api.formatDate(post.createdAt)} -
-
-

${post.title}

-

${post.content.substring(0, 120)}${post.content.length > 120 ? '...' : ''}

-
- ${post.likes} - ${post.comments} -
-
-
- -
- `; - - return postDiv; -} - -function createActivityElement(notification) { - const activityDiv = document.createElement('div'); - activityDiv.className = 'activity-item'; - - const iconMap = { - 'grade': 'fa-star', - 'message': 'fa-comment', - 'event': 'fa-calendar', - 'assignment': 'fa-tasks', - 'achievement': 'fa-trophy' - }; - - const icon = iconMap[notification.type] || 'fa-bell'; - - activityDiv.innerHTML = ` -
- -
-
-

${notification.message}

- ${api.formatDate(notification.timestamp)} -
- ${notification.type === 'grade' ? `
${notification.grade || 'A+'}
` : ''} - `; - - return activityDiv; -} - -function updateUserStats(user) { - // Update reputation - const reputationElement = document.querySelector('.user-reputation'); - if (reputationElement) { - reputationElement.textContent = user.reputation || 0; - } - - // Update verification status - const verifiedElements = document.querySelectorAll('.verified-badge'); - verifiedElements.forEach(element => { - element.style.display = user.verified ? 'inline' : 'none'; - }); - - // Update status indicator - const statusElements = document.querySelectorAll('.status-indicator'); - statusElements.forEach(element => { - element.className = `status-indicator ${user.status || 'active'}`; - element.title = user.status || 'active'; - }); -} - -function updateProgressStats(progressData) { - // Update course progress - const courseProgressElement = document.querySelector('.course-progress'); - if (courseProgressElement) { - const percentage = (progressData.coursesCompleted / progressData.totalCourses) * 100; - courseProgressElement.style.width = `${percentage}%`; - } - - // Update progress numbers - const progressStats = { - 'courses-completed': progressData.coursesCompleted, - 'total-courses': progressData.totalCourses, - 'study-streak': progressData.studyStreak, - 'hours-week': progressData.hoursThisWeek, - 'skills-learned': progressData.skillsLearned - }; - - Object.entries(progressStats).forEach(([key, value]) => { - const element = document.querySelector(`[data-stat="${key}"]`); - if (element) { - element.textContent = value; - } - }); -} - -function setupRealTimeUpdates() { - // Set up periodic updates - setInterval(async () => { - try { - await loadNotifications(); - } catch (error) { - console.error('Failed to update notifications:', error); - } - }, 30000); // Update every 30 seconds - - // Set up event listeners for quick actions - setupQuickActions(); -} - -function setupQuickActions() { - // Quick join event functionality - window.joinEventFromDashboard = async (eventId) => { - try { - await api.joinEvent(eventId); - api.showNotification('Successfully joined event!', 'success'); - - // Refresh events - await loadUpcomingEvents(); - - } catch (error) { - api.showNotification(error.message, 'error'); - } - }; - - // Quick create post - const createPostBtn = document.querySelector('#quickCreatePost'); - if (createPostBtn) { - createPostBtn.addEventListener('click', () => { - window.location.href = '/community'; - }); - } - - // Quick schedule event (for instructors) - const scheduleEventBtn = document.querySelector('#quickScheduleEvent'); - if (scheduleEventBtn && api.currentUser.userType === 'instructor') { - scheduleEventBtn.addEventListener('click', () => { - window.location.href = '/calendar'; - }); - } -} - -function showDashboardLoading(show) { - const loadingElements = document.querySelectorAll('.dashboard-card'); - - loadingElements.forEach(element => { - if (show) { - api.showLoadingSpinner(element, true); - } else { - api.showLoadingSpinner(element, false); - } - }); -} - -function formatTime(date) { - return date.toLocaleTimeString('en-US', { - hour: 'numeric', - minute: '2-digit', - hour12: true - }); -} - -function calculateDuration(startTime, endTime) { - const diffMs = endTime - startTime; - const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); - const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); - - if (diffHours > 0) { - return `${diffHours}h ${diffMinutes}m`; - } else { - return `${diffMinutes}m`; - } -} - -// Search functionality -const searchInput = document.querySelector('.search-box input'); -if (searchInput) { - let searchTimeout; - - searchInput.addEventListener('input', function() { - clearTimeout(searchTimeout); - const query = this.value.trim(); - - if (query.length < 2) return; - - searchTimeout = setTimeout(async () => { - try { - const results = await api.search(query); - showSearchResults(results); - } catch (error) { - console.error('Search failed:', error); - } - }, 500); - }); -} - -function showSearchResults(results) { - // Create or update search results dropdown - let resultsContainer = document.querySelector('.search-results'); - - if (!resultsContainer) { - resultsContainer = document.createElement('div'); - resultsContainer.className = 'search-results'; - resultsContainer.style.cssText = ` - position: absolute; - top: 100%; - left: 0; - right: 0; - background: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: 8px; - max-height: 400px; - overflow-y: auto; - z-index: 1000; - display: none; - `; - - const searchBox = document.querySelector('.search-box'); - searchBox.style.position = 'relative'; - searchBox.appendChild(resultsContainer); - } - - resultsContainer.innerHTML = ''; - - const totalResults = (results.posts?.length || 0) + - (results.events?.length || 0) + - (results.users?.length || 0); - - if (totalResults === 0) { - resultsContainer.innerHTML = ` -
- -

No results found

-
- `; - } else { - // Add posts - if (results.posts?.length > 0) { - const postsSection = document.createElement('div'); - postsSection.className = 'search-section'; - postsSection.innerHTML = ` -
- - Posts (${results.posts.length}) -
- `; - - results.posts.slice(0, 3).forEach(post => { - const postItem = document.createElement('div'); - postItem.className = 'search-item'; - postItem.innerHTML = ` -
-

${post.title}

-

${post.content.substring(0, 80)}...

-
- `; - postItem.addEventListener('click', () => { - window.location.href = `/community#post-${post.id}`; - }); - postsSection.appendChild(postItem); - }); - - resultsContainer.appendChild(postsSection); - } - - // Add events - if (results.events?.length > 0) { - const eventsSection = document.createElement('div'); - eventsSection.className = 'search-section'; - eventsSection.innerHTML = ` -
- - Events (${results.events.length}) -
- `; - - results.events.slice(0, 3).forEach(event => { - const eventItem = document.createElement('div'); - eventItem.className = 'search-item'; - eventItem.innerHTML = ` -
-

${event.title}

-

${event.description.substring(0, 80)}...

-
- `; - eventItem.addEventListener('click', () => { - window.location.href = `/calendar#event-${event.id}`; - }); - eventsSection.appendChild(eventItem); - }); - - resultsContainer.appendChild(eventsSection); - } - } - - resultsContainer.style.display = 'block'; - - // Hide results when clicking outside - document.addEventListener('click', function hideResults(e) { - if (!searchInput.contains(e.target) && !resultsContainer.contains(e.target)) { - resultsContainer.style.display = 'none'; - document.removeEventListener('click', hideResults); - } - }); -} diff --git a/scripts/dashboard.js b/scripts/dashboard.js deleted file mode 100644 index 5e543f2..0000000 --- a/scripts/dashboard.js +++ /dev/null @@ -1,248 +0,0 @@ -// Dashboard JavaScript -document.addEventListener('DOMContentLoaded', function() { - // Sidebar toggle for mobile - const sidebarToggle = document.querySelector('.sidebar-toggle'); - const sidebar = document.querySelector('.sidebar'); - const mainContent = document.querySelector('.main-content'); - - if (sidebarToggle) { - sidebarToggle.addEventListener('click', function() { - sidebar.classList.toggle('open'); - }); - } - - // Close sidebar when clicking outside on mobile - document.addEventListener('click', function(e) { - if (window.innerWidth <= 768) { - if (!sidebar.contains(e.target) && !sidebarToggle.contains(e.target)) { - sidebar.classList.remove('open'); - } - } - }); - - // Progress bar animations - function animateProgressBars() { - const progressBars = document.querySelectorAll('.progress-fill'); - progressBars.forEach(bar => { - const progress = bar.getAttribute('data-progress'); - if (progress) { - setTimeout(() => { - bar.style.width = progress + '%'; - }, 500); - } - }); - } - - // Call animation on page load - animateProgressBars(); - - // Search functionality - const searchInput = document.querySelector('.search-box input'); - if (searchInput) { - searchInput.addEventListener('input', function(e) { - const query = e.target.value.toLowerCase(); - // Implement search functionality here - console.log('Searching for:', query); - }); - } - - // Notification button - const notificationBtn = document.querySelector('.notification-btn'); - if (notificationBtn) { - notificationBtn.addEventListener('click', function() { - // Toggle notification panel - console.log('Notifications clicked'); - }); - } - - // User menu - const userMenu = document.querySelector('.user-menu'); - if (userMenu) { - userMenu.addEventListener('click', function() { - // Toggle user menu dropdown - console.log('User menu clicked'); - }); - } - - // Join buttons - document.querySelectorAll('.btn-join').forEach(btn => { - btn.addEventListener('click', function() { - if (!this.classList.contains('disabled')) { - // Join class functionality - console.log('Joining class...'); - this.textContent = 'Joining...'; - this.disabled = true; - - setTimeout(() => { - this.textContent = 'Joined'; - this.style.background = 'var(--success-color)'; - }, 2000); - } - }); - }); - - // Submit buttons for assignments - document.querySelectorAll('.submit-btn').forEach(btn => { - btn.addEventListener('click', function() { - // Submit assignment functionality - console.log('Submitting assignment...'); - }); - }); - - // Quick action buttons - document.querySelectorAll('.quick-action-btn').forEach(btn => { - btn.addEventListener('click', function() { - const action = this.querySelector('span').textContent; - console.log('Quick action:', action); - }); - }); - - // Activity items click - document.querySelectorAll('.activity-item').forEach(item => { - item.addEventListener('click', function() { - // Navigate to activity details - console.log('Activity clicked'); - }); - }); - - // Stats animation - function animateStats() { - const statNumbers = document.querySelectorAll('.stat-number'); - statNumbers.forEach(stat => { - const target = parseInt(stat.textContent); - const duration = 2000; - const step = target / (duration / 16); - let current = 0; - - const timer = setInterval(() => { - current += step; - if (current >= target) { - stat.textContent = target + (stat.textContent.includes('h') ? 'h' : ''); - clearInterval(timer); - } else { - stat.textContent = Math.floor(current) + (stat.textContent.includes('h') ? 'h' : ''); - } - }, 16); - }); - } - - // Animate stats on page load - setTimeout(animateStats, 1000); - - // Comment functionality - const commentInputs = document.querySelectorAll('.comment-input'); - commentInputs.forEach(input => { - const sendBtn = input.parentElement.querySelector('.send-comment'); - - input.addEventListener('input', function() { - sendBtn.disabled = this.value.trim() === ''; - }); - - input.addEventListener('keypress', function(e) { - if (e.key === 'Enter' && !sendBtn.disabled) { - sendComment(this); - } - }); - - if (sendBtn) { - sendBtn.addEventListener('click', function() { - sendComment(input); - }); - } - }); - - function sendComment(input) { - const comment = input.value.trim(); - if (comment) { - console.log('Sending comment:', comment); - input.value = ''; - input.parentElement.querySelector('.send-comment').disabled = true; - } - } - - // Post actions - document.querySelectorAll('.post-action').forEach(btn => { - btn.addEventListener('click', function() { - const action = this.querySelector('span').textContent; - const count = parseInt(action); - - if (this.querySelector('i').classList.contains('fa-heart')) { - this.classList.toggle('liked'); - if (this.classList.contains('liked')) { - this.querySelector('span').textContent = count + 1; - this.style.color = '#ff6b6b'; - } else { - this.querySelector('span').textContent = count; - this.style.color = ''; - } - } - }); - }); - - // Real-time updates simulation - function simulateRealTimeUpdates() { - // Simulate new notifications - const notificationDot = document.querySelector('.notification-dot'); - if (notificationDot && Math.random() > 0.7) { - notificationDot.style.display = 'block'; - } - - // Update activity feed - if (Math.random() > 0.8) { - console.log('New activity received'); - } - } - - // Check for updates every 30 seconds - setInterval(simulateRealTimeUpdates, 30000); - - // Responsive adjustments - function handleResize() { - if (window.innerWidth <= 768) { - sidebar.classList.remove('open'); - } - } - - window.addEventListener('resize', handleResize); - - // Initialize tooltips - function initTooltips() { - const tooltipElements = document.querySelectorAll('[title]'); - tooltipElements.forEach(element => { - element.addEventListener('mouseenter', function() { - const tooltip = document.createElement('div'); - tooltip.className = 'tooltip'; - tooltip.textContent = this.getAttribute('title'); - tooltip.style.cssText = ` - position: absolute; - background: var(--primary-bg); - color: var(--text-primary); - padding: 4px 8px; - border-radius: 4px; - font-size: 0.8rem; - z-index: 1000; - pointer-events: none; - box-shadow: var(--shadow-md); - border: 1px solid var(--border-color); - `; - - document.body.appendChild(tooltip); - this.tooltipElement = tooltip; - - this.addEventListener('mousemove', function(e) { - tooltip.style.left = e.pageX + 10 + 'px'; - tooltip.style.top = e.pageY - 30 + 'px'; - }); - }); - - element.addEventListener('mouseleave', function() { - if (this.tooltipElement) { - this.tooltipElement.remove(); - this.tooltipElement = null; - } - }); - }); - } - - initTooltips(); -}); diff --git a/scripts/hack-main.js b/scripts/hack-main.js deleted file mode 100644 index e69de29..0000000 diff --git a/scripts/hackclub-interactions.js b/scripts/hackclub-interactions.js deleted file mode 100644 index 9860137..0000000 --- a/scripts/hackclub-interactions.js +++ /dev/null @@ -1,121 +0,0 @@ -/** - * HackClub Style Interactions - * Mobile menu and smooth scrolling functionality - */ - -document.addEventListener('DOMContentLoaded', function() { - // Mobile Navigation Toggle - const navToggle = document.querySelector('.nav-toggle'); - const navMenu = document.querySelector('.nav-menu'); - const navbar = document.querySelector('.navbar'); - - if (navToggle && navMenu) { - navToggle.addEventListener('click', function() { - navMenu.classList.toggle('active'); - navToggle.classList.toggle('active'); - - // Update icon - const icon = navToggle.querySelector('i'); - if (icon) { - if (navMenu.classList.contains('active')) { - icon.className = 'fas fa-times'; - } else { - icon.className = 'fas fa-bars'; - } - } - - // Prevent body scroll when menu is open - document.body.classList.toggle('nav-open'); - }); - - // Close menu when clicking on nav links - const navLinks = document.querySelectorAll('.nav-link'); - navLinks.forEach(link => { - link.addEventListener('click', function() { - navMenu.classList.remove('active'); - navToggle.classList.remove('active'); - document.body.classList.remove('nav-open'); - - const icon = navToggle.querySelector('i'); - if (icon) { - icon.className = 'fas fa-bars'; - } - }); - }); - - // Close menu when clicking outside - document.addEventListener('click', function(e) { - if (!navbar.contains(e.target)) { - navMenu.classList.remove('active'); - navToggle.classList.remove('active'); - document.body.classList.remove('nav-open'); - - const icon = navToggle.querySelector('i'); - if (icon) { - icon.className = 'fas fa-bars'; - } - } - }); - } - - // Smooth scrolling for internal links - const internalLinks = document.querySelectorAll('a[href^="#"]'); - internalLinks.forEach(link => { - link.addEventListener('click', function(e) { - const targetId = this.getAttribute('href'); - const targetElement = document.querySelector(targetId); - - if (targetElement) { - e.preventDefault(); - targetElement.scrollIntoView({ - behavior: 'smooth', - block: 'start' - }); - } - }); - }); - - // Navbar scroll effect - window.addEventListener('scroll', function() { - if (window.scrollY > 100) { - navbar.classList.add('scrolled'); - } else { - navbar.classList.remove('scrolled'); - } - }); - - // Active nav link highlighting - const sections = document.querySelectorAll('section[id]'); - const navLinksActive = document.querySelectorAll('.nav-link'); - - window.addEventListener('scroll', function() { - let current = ''; - sections.forEach(section => { - const sectionTop = section.offsetTop; - const sectionHeight = section.clientHeight; - if (window.scrollY >= (sectionTop - 200)) { - current = section.getAttribute('id'); - } - }); - - navLinksActive.forEach(link => { - link.classList.remove('active'); - if (link.getAttribute('href') === `#${current}`) { - link.classList.add('active'); - } - }); - }); - - // HackClub DayDream floating button functionality - const hackclubBtn = document.querySelector('.hackclub-floating-btn'); - if (hackclubBtn) { - // Add tooltip on hover - hackclubBtn.setAttribute('title', 'Join HackClub DayDream Sri Lanka WhatsApp Channel'); - - // Add click analytics (if needed) - hackclubBtn.addEventListener('click', function() { - console.log('HackClub DayDream button clicked'); - // Add analytics tracking here if needed - }); - } -}); \ No newline at end of file diff --git a/scripts/landing.js b/scripts/landing.js deleted file mode 100644 index cd609b0..0000000 --- a/scripts/landing.js +++ /dev/null @@ -1,179 +0,0 @@ -// Landing Page JavaScript -document.addEventListener('DOMContentLoaded', function() { - // Mobile menu toggle - const mobileMenuToggle = document.querySelector('.mobile-menu-toggle'); - const navLinks = document.querySelector('.nav-links'); - - if (mobileMenuToggle) { - mobileMenuToggle.addEventListener('click', function() { - navLinks.classList.toggle('active'); - mobileMenuToggle.classList.toggle('active'); - }); - } - - // Smooth scrolling for anchor links - document.querySelectorAll('a[href^="#"]').forEach(anchor => { - anchor.addEventListener('click', function(e) { - e.preventDefault(); - const target = document.querySelector(this.getAttribute('href')); - if (target) { - target.scrollIntoView({ - behavior: 'smooth', - block: 'start' - }); - } - }); - }); - - // Animate floating cards - const floatingCards = document.querySelectorAll('.floating-card'); - floatingCards.forEach((card, index) => { - card.style.animationDelay = `${-index * 2}s`; - }); - - // Progress bar animation - const progressFill = document.querySelector('.progress-fill'); - if (progressFill) { - setTimeout(() => { - progressFill.style.width = '75%'; - }, 1000); - } - - // Intersection Observer for animations - const observerOptions = { - threshold: 0.1, - rootMargin: '0px 0px -50px 0px' - }; - - const observer = new IntersectionObserver((entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - entry.target.style.opacity = '1'; - entry.target.style.transform = 'translateY(0)'; - } - }); - }, observerOptions); - - // Observe feature cards for animation - document.querySelectorAll('.feature-card, .language-card').forEach(card => { - card.style.opacity = '0'; - card.style.transform = 'translateY(20px)'; - card.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; - observer.observe(card); - }); - - // Add typing animation to hero text - const heroTitle = document.querySelector('.hero-text h1'); - if (heroTitle) { - const text = heroTitle.textContent; - heroTitle.textContent = ''; - let i = 0; - - function typeWriter() { - if (i < text.length) { - heroTitle.textContent += text.charAt(i); - i++; - setTimeout(typeWriter, 50); - } - } - - setTimeout(typeWriter, 500); - } - - // Add hover effects to stats - document.querySelectorAll('.stat').forEach(stat => { - stat.addEventListener('mouseenter', function() { - this.style.transform = 'scale(1.05)'; - }); - - stat.addEventListener('mouseleave', function() { - this.style.transform = 'scale(1)'; - }); - }); - - // Counter animation for stats - function animateCounter(element, target) { - let current = 0; - const increment = target / 100; - const timer = setInterval(() => { - current += increment; - if (current >= target) { - element.textContent = target; - clearInterval(timer); - } else { - element.textContent = Math.floor(current); - } - }, 20); - } - - // Animate counters when they come into view - const statObserver = new IntersectionObserver((entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - const statNumber = entry.target.querySelector('.stat-number'); - const targetText = statNumber.textContent; - const target = parseInt(targetText.replace(/\D/g, '')); - const suffix = targetText.replace(/\d/g, ''); - - animateCounter(statNumber, target); - statObserver.unobserve(entry.target); - } - }); - }); - - document.querySelectorAll('.stat').forEach(stat => { - statObserver.observe(stat); - }); - - // Background animation - function createFloatingElement() { - const element = document.createElement('div'); - element.className = 'floating-dot'; - element.style.cssText = ` - position: absolute; - width: 4px; - height: 4px; - background: var(--accent-primary); - border-radius: 50%; - opacity: 0.3; - pointer-events: none; - animation: float-up 4s linear infinite; - `; - - element.style.left = Math.random() * 100 + '%'; - element.style.animationDelay = Math.random() * 4 + 's'; - - document.querySelector('.hero').appendChild(element); - - setTimeout(() => { - element.remove(); - }, 4000); - } - - // Create floating elements periodically - setInterval(createFloatingElement, 800); - - // Add CSS for floating animation - const style = document.createElement('style'); - style.textContent = ` - @keyframes float-up { - 0% { - opacity: 0; - transform: translateY(100vh) scale(0); - } - 10% { - opacity: 0.3; - transform: translateY(90vh) scale(1); - } - 90% { - opacity: 0.3; - transform: translateY(10vh) scale(1); - } - 100% { - opacity: 0; - transform: translateY(0) scale(0); - } - } - `; - document.head.appendChild(style); -}); diff --git a/scripts/like-system.js b/scripts/like-system.js deleted file mode 100644 index 03e2ce4..0000000 --- a/scripts/like-system.js +++ /dev/null @@ -1,279 +0,0 @@ -/** - * Like System for OpenRockets Member Profiles - * Uses localStorage to track likes and simulates real-time data - */ - -class LikeSystem { - constructor() { - this.baseUrl = 'https://api.github.com/repos/openrockets'; // Fallback to GitHub API - this.init(); - } - - init() { - this.createLikeButtons(); - this.loadLikeCounts(); - } - - /** - * Create like buttons for all profile cards - */ - createLikeButtons() { - const profileCards = document.querySelectorAll('.profileCard-nav-right'); - - profileCards.forEach((navRight, index) => { - if (!navRight.querySelector('.like-button')) { - const memberName = this.getMemberName(); - const likeButton = this.createLikeButtonHTML(memberName); - navRight.innerHTML = likeButton; - } - }); - } - - /** - * Get current member name from page URL or title - */ - getMemberName() { - const url = window.location.pathname; - if (url.includes('neksha')) return 'neksha'; - if (url.includes('chethina')) return 'chethina'; - if (url.includes('vidul')) return 'vidul'; - if (url.includes('menul')) return 'menul'; - return 'unknown'; - } - - /** - * Create like button HTML - */ - createLikeButtonHTML(memberName) { - return ` - - `; - } - - /** - * Load and display like counts - */ - async loadLikeCounts() { - const likeButtons = document.querySelectorAll('.like-button'); - - likeButtons.forEach(async (button) => { - const memberName = button.dataset.member; - const likeCount = await this.getLikeCount(memberName); - const countSpan = button.querySelector('.like-count'); - - // Animate count loading - this.animateCountUp(countSpan, likeCount); - - // Add click event - button.addEventListener('click', () => this.handleLike(memberName, button)); - - // Check if already liked - if (this.hasUserLiked(memberName)) { - button.classList.add('liked'); - } - }); - } - - /** - * Get like count from various sources - */ - async getLikeCount(memberName) { - try { - // Try to get from GitHub stars first - const githubCount = await this.getGitHubStars(memberName); - if (githubCount > 0) { - const baseCount = githubCount; - const additionalLikes = this.getStoredLikes(memberName); - return baseCount + additionalLikes; - } - } catch (error) { - console.log('GitHub API unavailable, using fallback'); - } - - // Fallback: Use realistic random numbers + stored likes - const baseCount = this.getBaseCount(memberName); - const additionalLikes = this.getStoredLikes(memberName); - return baseCount + additionalLikes; - } - - /** - * Get GitHub stars for member's repos - */ - async getGitHubStars(memberName) { - try { - const usernames = { - 'neksha': 'nekshadesilva', - 'chethina': 'geek-cheth', - 'vidul': 'vidulhb', - 'menul': 'dms-menula' - }; - - const username = usernames[memberName]; - if (!username) return 0; - - const response = await fetch(`https://api.github.com/users/${username}/repos?per_page=5`); - if (!response.ok) throw new Error('GitHub API error'); - - const repos = await response.json(); - const totalStars = repos.reduce((sum, repo) => sum + repo.stargazers_count, 0); - - return Math.max(totalStars, 0); - } catch (error) { - return 0; - } - } - - /** - * Get base count for members (realistic starting numbers) - */ - getBaseCount(memberName) { - const baseCounts = { - 'neksha': 127, // Founder gets higher base - 'chethina': 89, - 'vidul': 94, - 'menul': 67 - }; - - return baseCounts[memberName] || Math.floor(Math.random() * 50) + 25; - } - - /** - * Get stored additional likes from localStorage - */ - getStoredLikes(memberName) { - const stored = localStorage.getItem(`likes_${memberName}`); - return stored ? parseInt(stored) : 0; - } - - /** - * Check if user has already liked this member - */ - hasUserLiked(memberName) { - return localStorage.getItem(`liked_${memberName}`) === 'true'; - } - - /** - * Handle like button click - */ - async handleLike(memberName, button) { - if (this.hasUserLiked(memberName)) { - // Already liked - show message or animation - this.showAlreadyLikedMessage(button); - return; - } - - // Add like - const currentAdditional = this.getStoredLikes(memberName); - localStorage.setItem(`likes_${memberName}`, (currentAdditional + 1).toString()); - localStorage.setItem(`liked_${memberName}`, 'true'); - - // Update UI - button.classList.add('liked'); - const countSpan = button.querySelector('.like-count'); - const newCount = parseInt(countSpan.textContent) + 1; - - // Animate the increment - this.animateLikeIncrement(countSpan, newCount); - this.addLikeAnimation(button); - } - - /** - * Show already liked message - */ - showAlreadyLikedMessage(button) { - const message = document.createElement('div'); - message.className = 'like-message'; - message.textContent = 'Already liked! ❤️'; - - button.appendChild(message); - - setTimeout(() => { - message.remove(); - }, 2000); - } - - /** - * Animate count up effect - */ - animateCountUp(element, targetCount) { - let currentCount = 0; - const increment = Math.ceil(targetCount / 20); - const timer = setInterval(() => { - currentCount += increment; - if (currentCount >= targetCount) { - currentCount = targetCount; - clearInterval(timer); - } - element.textContent = currentCount.toLocaleString(); - }, 50); - } - - /** - * Animate like increment - */ - animateLikeIncrement(element, newCount) { - element.style.transform = 'scale(1.2)'; - element.style.color = '#ff6b6b'; - - setTimeout(() => { - element.textContent = newCount.toLocaleString(); - element.style.transform = 'scale(1)'; - element.style.color = ''; - }, 200); - } - - /** - * Add like animation to button - */ - addLikeAnimation(button) { - const icon = button.querySelector('.like-icon'); - - // Heart animation - icon.style.transform = 'scale(1.3)'; - icon.style.color = '#ff6b6b'; - - // Floating hearts effect - this.createFloatingHearts(button); - - setTimeout(() => { - icon.style.transform = 'scale(1)'; - }, 300); - } - - /** - * Create floating hearts animation - */ - createFloatingHearts(button) { - for (let i = 0; i < 3; i++) { - const heart = document.createElement('div'); - heart.innerHTML = '❤️'; - heart.className = 'floating-heart'; - heart.style.cssText = ` - position: absolute; - pointer-events: none; - font-size: 12px; - animation: floatUp 1s ease-out forwards; - left: ${Math.random() * 20 - 10}px; - animation-delay: ${i * 0.1}s; - `; - - button.style.position = 'relative'; - button.appendChild(heart); - - setTimeout(() => heart.remove(), 1000); - } - } -} - -// Initialize like system when DOM is loaded -document.addEventListener('DOMContentLoaded', () => { - new LikeSystem(); -}); - -// Export for manual initialization if needed -window.LikeSystem = LikeSystem; diff --git a/scripts/main.js b/scripts/main.js deleted file mode 100644 index d2f79ec..0000000 --- a/scripts/main.js +++ /dev/null @@ -1,567 +0,0 @@ -// Main JavaScript file for OpenRockets Foundation website - -document.addEventListener('DOMContentLoaded', function() { - initializeNavigation(); - initializeSmoothScrolling(); - initializeScrollEffects(); - initializeFormHandling(); - initializeAnimations(); - initializeParallaxEffects(); -}); - -// Navigation functionality -function initializeNavigation() { - const navToggle = document.getElementById('navToggle'); - const navMenu = document.getElementById('navMenu'); - const navLinks = document.querySelectorAll('.nav-link'); - - if (navToggle && navMenu) { - // Toggle mobile menu - navToggle.addEventListener('click', function() { - navToggle.classList.toggle('active'); - navMenu.classList.toggle('active'); - - // Prevent body scroll when menu is open - if (navMenu.classList.contains('active')) { - document.body.style.overflow = 'hidden'; - } else { - document.body.style.overflow = ''; - } - }); - - // Close menu when clicking on nav links - navLinks.forEach(link => { - link.addEventListener('click', function() { - navToggle.classList.remove('active'); - navMenu.classList.remove('active'); - document.body.style.overflow = ''; - }); - }); - - // Close menu when clicking outside - document.addEventListener('click', function(e) { - if (!navToggle.contains(e.target) && !navMenu.contains(e.target)) { - navToggle.classList.remove('active'); - navMenu.classList.remove('active'); - document.body.style.overflow = ''; - } - }); - - // Close menu on escape key - document.addEventListener('keydown', function(e) { - if (e.key === 'Escape') { - navToggle.classList.remove('active'); - navMenu.classList.remove('active'); - document.body.style.overflow = ''; - } - }); - } -} - -// Smooth scrolling for anchor links -function initializeSmoothScrolling() { - const links = document.querySelectorAll('a[href^="#"]'); - - links.forEach(link => { - link.addEventListener('click', function(e) { - e.preventDefault(); - - const targetId = this.getAttribute('href').substring(1); - const targetElement = document.getElementById(targetId); - - if (targetElement) { - const navbar = document.querySelector('.navbar'); - const navbarHeight = navbar ? navbar.offsetHeight : 0; - const targetPosition = targetElement.offsetTop - navbarHeight; - - window.scrollTo({ - top: targetPosition, - behavior: 'smooth' - }); - } - }); - }); -} - -// Scroll effects for navbar and animations -function initializeScrollEffects() { - const navbar = document.querySelector('.navbar'); - let lastScrollTop = 0; - let isScrolling = false; - - function handleScroll() { - if (!isScrolling) { - window.requestAnimationFrame(() => { - const scrollTop = window.pageYOffset || document.documentElement.scrollTop; - - // Update navbar appearance based on scroll - if (navbar) { - if (scrollTop > 100) { - navbar.style.background = 'rgba(10, 10, 15, 0.95)'; - navbar.style.backdropFilter = 'blur(20px)'; - } else { - navbar.style.background = 'rgba(10, 10, 15, 0.9)'; - navbar.style.backdropFilter = 'blur(20px)'; - } - } - - // Update active nav link based on scroll position - updateActiveNavLink(); - - // Trigger scroll animations - triggerScrollAnimations(); - - lastScrollTop = scrollTop; - isScrolling = false; - }); - } - isScrolling = true; - } - - window.addEventListener('scroll', handleScroll, { passive: true }); -} - -// Update active navigation link based on scroll position -function updateActiveNavLink() { - const sections = document.querySelectorAll('section[id]'); - const navLinks = document.querySelectorAll('.nav-link'); - const navbar = document.querySelector('.navbar'); - const navbarHeight = navbar ? navbar.offsetHeight : 0; - - let currentSection = ''; - - sections.forEach(section => { - const sectionTop = section.offsetTop - navbarHeight - 100; - const sectionHeight = section.offsetHeight; - - if (window.pageYOffset >= sectionTop && window.pageYOffset < sectionTop + sectionHeight) { - currentSection = section.getAttribute('id'); - } - }); - - navLinks.forEach(link => { - link.classList.remove('active'); - if (link.getAttribute('href') === `#${currentSection}`) { - link.classList.add('active'); - } - }); -} - -// Trigger animations when elements come into view -function triggerScrollAnimations() { - const animatedElements = document.querySelectorAll('[data-animate]'); - - animatedElements.forEach(element => { - if (isElementInViewport(element) && !element.classList.contains('animated')) { - const animationType = element.getAttribute('data-animate'); - element.classList.add('animated', animationType); - } - }); -} - -// Check if element is in viewport -function isElementInViewport(element) { - const rect = element.getBoundingClientRect(); - return ( - rect.top >= 0 && - rect.left >= 0 && - rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && - rect.right <= (window.innerWidth || document.documentElement.clientWidth) - ); -} - -// Enhanced viewport check with threshold -function isElementVisible(element, threshold = 0.1) { - const rect = element.getBoundingClientRect(); - const viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight); - const viewWidth = Math.max(document.documentElement.clientWidth, window.innerWidth); - - const elementHeight = rect.bottom - rect.top; - const elementWidth = rect.right - rect.left; - - const visibleHeight = Math.min(rect.bottom, viewHeight) - Math.max(rect.top, 0); - const visibleWidth = Math.min(rect.right, viewWidth) - Math.max(rect.left, 0); - - const visibleArea = visibleHeight * visibleWidth; - const elementArea = elementHeight * elementWidth; - - return (visibleArea / elementArea) >= threshold; -} - -// Form handling -function initializeFormHandling() { - const contactForm = document.querySelector('.contact-form form'); - - if (contactForm) { - contactForm.addEventListener('submit', function(e) { - e.preventDefault(); - handleFormSubmission(this); - }); - - // Add real-time validation - const formInputs = contactForm.querySelectorAll('input, select, textarea'); - formInputs.forEach(input => { - input.addEventListener('blur', function() { - validateField(this); - }); - - input.addEventListener('input', function() { - clearFieldError(this); - }); - }); - } -} - -// Handle form submission -function handleFormSubmission(form) { - const formData = new FormData(form); - const submitButton = form.querySelector('button[type="submit"]'); - - // Validate form - if (!validateForm(form)) { - showNotification('Please fill in all required fields correctly.', 'error'); - return; - } - - // Show loading state - const originalButtonText = submitButton.textContent; - submitButton.textContent = 'Sending...'; - submitButton.disabled = true; - - // Simulate form submission (replace with actual API call) - setTimeout(() => { - // Reset form - form.reset(); - - // Reset button - submitButton.textContent = originalButtonText; - submitButton.disabled = false; - - // Show success message - showNotification('Thank you for your message! We\'ll get back to you soon.', 'success'); - }, 2000); -} - -// Form validation -function validateForm(form) { - const requiredFields = form.querySelectorAll('[required]'); - let isValid = true; - - requiredFields.forEach(field => { - if (!validateField(field)) { - isValid = false; - } - }); - - return isValid; -} - -// Validate individual field -function validateField(field) { - const value = field.value.trim(); - const fieldType = field.type; - let isValid = true; - let errorMessage = ''; - - // Check if required field is empty - if (field.hasAttribute('required') && !value) { - isValid = false; - errorMessage = 'This field is required.'; - } - - // Validate email - if (fieldType === 'email' && value) { - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - if (!emailRegex.test(value)) { - isValid = false; - errorMessage = 'Please enter a valid email address.'; - } - } - - // Show/hide error - if (!isValid) { - showFieldError(field, errorMessage); - } else { - clearFieldError(field); - } - - return isValid; -} - -// Show field error -function showFieldError(field, message) { - clearFieldError(field); - - field.style.borderColor = '#ff6b35'; - - const errorElement = document.createElement('div'); - errorElement.className = 'field-error'; - errorElement.textContent = message; - errorElement.style.cssText = ` - color: #ff6b35; - font-size: 0.875rem; - margin-top: 0.25rem; - `; - - field.parentNode.appendChild(errorElement); -} - -// Clear field error -function clearFieldError(field) { - field.style.borderColor = ''; - const errorElement = field.parentNode.querySelector('.field-error'); - if (errorElement) { - errorElement.remove(); - } -} - -// Show notification -function showNotification(message, type = 'info') { - const notification = document.createElement('div'); - notification.className = `notification notification-${type}`; - notification.textContent = message; - - const colors = { - success: '#00d4aa', - error: '#ff6b35', - info: '#0066cc' - }; - - notification.style.cssText = ` - position: fixed; - top: 100px; - right: 20px; - background: ${colors[type]}; - color: white; - padding: 1rem 1.5rem; - border-radius: 0.5rem; - box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); - z-index: 1080; - transform: translateX(100%); - transition: transform 0.3s ease; - max-width: 300px; - font-size: 0.875rem; - line-height: 1.4; - `; - - document.body.appendChild(notification); - - // Animate in - setTimeout(() => { - notification.style.transform = 'translateX(0)'; - }, 100); - - // Auto remove after 5 seconds - setTimeout(() => { - notification.style.transform = 'translateX(100%)'; - setTimeout(() => { - if (notification.parentNode) { - notification.parentNode.removeChild(notification); - } - }, 300); - }, 5000); -} - -// Initialize animations and interactive elements -function initializeAnimations() { - // Add stagger animation to cards - const cardGroups = [ - '.mission-grid .mission-card', - '.projects-grid .project-card', - '.about-stats .stat-item' - ]; - - cardGroups.forEach(selector => { - const cards = document.querySelectorAll(selector); - cards.forEach((card, index) => { - card.style.animationDelay = `${index * 0.1}s`; - }); - }); - - // Initialize intersection observer for animations - if ('IntersectionObserver' in window) { - const observerOptions = { - threshold: 0.1, - rootMargin: '0px 0px -50px 0px' - }; - - const observer = new IntersectionObserver((entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - entry.target.classList.add('animate-in'); - } - }); - }, observerOptions); - - // Observe elements for animation - const animatableElements = document.querySelectorAll(` - .mission-card, - .project-card, - .stat-item, - .community-card, - .section-header, - .about-text, - .rocket-diagram - `); - - animatableElements.forEach(el => { - observer.observe(el); - }); - } -} - -// Parallax effects for hero section -function initializeParallaxEffects() { - const floatingElements = document.querySelectorAll('.rocket-icon'); - const stars = document.querySelector('.stars'); - - if (floatingElements.length > 0 || stars) { - let ticking = false; - - function updateParallax() { - const scrolled = window.pageYOffset; - const rate = scrolled * -0.5; - - if (stars) { - stars.style.transform = `translateY(${rate}px)`; - } - - floatingElements.forEach((element, index) => { - const speed = 0.2 + (index * 0.1); - const yPos = -(scrolled * speed); - element.style.transform = `translateY(${yPos}px)`; - }); - - ticking = false; - } - - function requestTick() { - if (!ticking) { - requestAnimationFrame(updateParallax); - ticking = true; - } - } - - window.addEventListener('scroll', requestTick, { passive: true }); - } -} - -// Utility function to debounce events -function debounce(func, wait, immediate) { - let timeout; - return function executedFunction(...args) { - const later = () => { - timeout = null; - if (!immediate) func(...args); - }; - const callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func(...args); - }; -} - -// Handle window resize -window.addEventListener('resize', debounce(() => { - // Close mobile menu on resize to desktop - if (window.innerWidth >= 768) { - const navToggle = document.getElementById('navToggle'); - const navMenu = document.getElementById('navMenu'); - - if (navToggle && navMenu) { - navToggle.classList.remove('active'); - navMenu.classList.remove('active'); - document.body.style.overflow = ''; - } - } -}, 250)); - -// Add CSS animations via JavaScript -function addAnimationStyles() { - const style = document.createElement('style'); - style.textContent = ` - .animate-in { - animation: fadeInUp 0.6s ease forwards; - } - - @keyframes fadeInUp { - from { - opacity: 0; - transform: translateY(30px); - } - to { - opacity: 1; - transform: translateY(0); - } - } - - .mission-card, - .project-card, - .stat-item, - .community-card { - opacity: 0; - transform: translateY(30px); - transition: opacity 0.6s ease, transform 0.6s ease; - } - - .section-header, - .about-text, - .rocket-diagram { - opacity: 0; - transform: translateY(20px); - transition: opacity 0.8s ease, transform 0.8s ease; - } - `; - - document.head.appendChild(style); -} - -// Initialize animation styles -addAnimationStyles(); - -// Keyboard navigation improvements -document.addEventListener('keydown', function(e) { - // Improve focus visibility for keyboard navigation - if (e.key === 'Tab') { - document.body.classList.add('keyboard-navigation'); - } -}); - -document.addEventListener('mousedown', function() { - document.body.classList.remove('keyboard-navigation'); -}); - -// Performance optimization: Lazy load images if any are added later -function initializeLazyLoading() { - if ('IntersectionObserver' in window) { - const imageObserver = new IntersectionObserver((entries, observer) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - const img = entry.target; - img.src = img.dataset.src; - img.classList.remove('lazy'); - imageObserver.unobserve(img); - } - }); - }); - - const lazyImages = document.querySelectorAll('img[data-src]'); - lazyImages.forEach(img => imageObserver.observe(img)); - } -} - -// Initialize lazy loading -initializeLazyLoading(); - -// Add error handling for JavaScript errors -window.addEventListener('error', function(e) { - console.error('JavaScript error:', e.error); - // Could send error to analytics service here -}); - -// Service worker registration for PWA capabilities (if needed) -if ('serviceWorker' in navigator) { - window.addEventListener('load', () => { - // Service worker can be registered here if needed for PWA - }); -} diff --git a/scripts/navigation.js b/scripts/navigation.js deleted file mode 100644 index 90ecc82..0000000 --- a/scripts/navigation.js +++ /dev/null @@ -1,45 +0,0 @@ -// Mobile Navigation Toggle -document.addEventListener('DOMContentLoaded', function() { - const navToggle = document.querySelector('.nav-toggle'); - const navMenu = document.querySelector('.nav-menu'); - const body = document.body; - - if (navToggle && navMenu) { - navToggle.addEventListener('click', function() { - navMenu.classList.toggle('active'); - navToggle.classList.toggle('active'); - body.classList.toggle('nav-open'); - }); - - // Close nav when clicking on nav links - const navLinks = document.querySelectorAll('.nav-link'); - navLinks.forEach(link => { - link.addEventListener('click', function() { - navMenu.classList.remove('active'); - navToggle.classList.remove('active'); - body.classList.remove('nav-open'); - }); - }); - - // Close nav when clicking outside - document.addEventListener('click', function(e) { - if (!navToggle.contains(e.target) && !navMenu.contains(e.target)) { - navMenu.classList.remove('active'); - navToggle.classList.remove('active'); - body.classList.remove('nav-open'); - } - }); - } - - // Navbar scroll effect - const navbar = document.querySelector('.navbar'); - if (navbar) { - window.addEventListener('scroll', function() { - if (window.scrollY > 50) { - navbar.classList.add('scrolled'); - } else { - navbar.classList.remove('scrolled'); - } - }); - } -}); \ No newline at end of file diff --git a/scripts/opennetwork-banner.js b/scripts/opennetwork-banner.js deleted file mode 100644 index 240d1ea..0000000 --- a/scripts/opennetwork-banner.js +++ /dev/null @@ -1,427 +0,0 @@ -/** - * OpenNetwork Collective Banner - * Advanced JavaScript banner for website registration display - * Version: 1.0.0 - */ - -(function() { - 'use strict'; - - // Configuration - const CONFIG = { - flagLogoUrl: 'https://openrockets.com/assets/press/opennetwork.png', - affiliateUrl: 'https://opennetworked.org/en/privacy/affiliates', - statusApiUrl: 'https://status.opennetworked.org/api/status', // Placeholder API - animationDuration: 15000, // 15 seconds for marquee - checkInterval: 300000, // 5 minutes status check - position: 'bottom' // 'top' or 'bottom' - }; - - // Global state - let banner = null; - let statusCheck = null; - let isMarqueeActive = false; - - // Utility functions - const getCurrentDomain = () => { - return window.location.hostname.replace('www.', ''); - }; - - const createSVGIcon = (type, color = '#10B981') => { - const svgMap = { - 'operational': ` - - - - - - `, - 'warning': ` - - - - - `, - 'error': ` - - - - - ` - }; - return svgMap[type] || svgMap['operational']; - }; - - const checkSystemStatus = async () => { - try { - // Simulated status check - replace with actual API call - const response = await fetch(CONFIG.statusApiUrl).catch(() => ({ - ok: true, - json: () => Promise.resolve({ status: 'operational' }) - })); - - if (response.ok) { - const data = await response.json(); - return { - status: data.status || 'operational', - message: data.message || 'All Systems Operational' - }; - } - } catch (error) { - console.warn('OpenNetwork Banner: Status check failed', error); - } - - // Fallback to operational status - return { - status: 'operational', - message: 'All Systems Operational' - }; - }; - - const updateStatusIndicator = (status, message) => { - const statusElement = banner.querySelector('.on-status'); - if (!statusElement) return; - - const iconElement = statusElement.querySelector('.on-status-icon'); - const textElement = statusElement.querySelector('.on-status-text'); - - if (iconElement && textElement) { - iconElement.innerHTML = createSVGIcon(status); - textElement.textContent = message; - - // Update colors based on status - const colorMap = { - 'operational': '#10B981', - 'warning': '#F59E0B', - 'error': '#EF4444' - }; - - textElement.style.color = colorMap[status] || colorMap['operational']; - } - }; - - const checkTextOverflow = () => { - const textContainer = banner.querySelector('.on-text-content'); - const statusContainer = banner.querySelector('.on-status'); - - if (!textContainer || !statusContainer) return; - - const containerWidth = banner.offsetWidth - 60; // Account for padding - const textWidth = textContainer.scrollWidth; - const statusWidth = statusContainer.offsetWidth; - const totalWidth = textWidth + statusWidth + 20; // 20px gap - - if (totalWidth > containerWidth) { - startMarquee(); - } else { - stopMarquee(); - } - }; - - const startMarquee = () => { - if (isMarqueeActive) return; - - const textContent = banner.querySelector('.on-text-content'); - if (!textContent) return; - - isMarqueeActive = true; - textContent.style.animation = `marquee ${CONFIG.animationDuration}ms linear infinite`; - }; - - const stopMarquee = () => { - if (!isMarqueeActive) return; - - const textContent = banner.querySelector('.on-text-content'); - if (!textContent) return; - - isMarqueeActive = false; - textContent.style.animation = 'none'; - }; - - const createBannerHTML = (domain, status) => { - return ` -
-
- -
- ${domain} is a registered non-profit, currently active in operation under - OpenNetwork Collective -
-
-
-
${createSVGIcon(status.status)}
- ${status.message} -
-
- `; - }; - - const createBannerStyles = () => { - const style = document.createElement('style'); - style.id = 'opennetwork-banner-styles'; - style.textContent = ` - .opennetwork-banner { - position:relative; - ${CONFIG.position}: 0; - left: 0; - right: 0; - background: linear-gradient(135deg, #000000ff 0%, #000000ff 100%); - color: white; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; - font-size: 13px; - line-height: 1.4; - z-index: 999999; - box-shadow: ${CONFIG.position === 'bottom' ? '0 -2px 12px rgba(0,0,0,0.15)' : '0 2px 12px rgba(0,0,0,0.15)'}; - border-${CONFIG.position === 'bottom' ? 'top' : 'bottom'}: 1px solid rgba(255,255,255,0.1); - backdrop-filter: blur(10px); - -webkit-backdrop-filter: blur(10px); - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - padding: 8px 16px; - min-height: 40px; - display: flex; - align-items: center; - } - - .opennetwork-banner:hover { - background: linear-gradient(135deg, #000000ff 0%, #000000ff 100%); - transform: translateY(${CONFIG.position === 'bottom' ? '-1px' : '1px'}); - } - - .on-banner-content { - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; - max-width: 1200px; - margin: 0 auto; - overflow: hidden; - } - - .on-left-section { - display: flex; - align-items: center; - flex: 1; - overflow: hidden; - margin-right: 20px; - } - - .on-flag-logo { - width: 20px; - height: 20px; - margin-right: 12px; - border-radius: 3px; - box-shadow: 0 2px 4px rgba(0,0,0,0.2); - transition: transform 0.2s ease; - flex-shrink: 0; - } - - .on-flag-logo:hover { - transform: scale(1.1); - } - - .on-text-content { - white-space: nowrap; - overflow: hidden; - color: rgba(255,255,255,0.95); - font-weight: 400; - } - - .on-domain { - font-weight: 600; - color: #fbbf24; - text-shadow: 0 1px 2px rgba(0,0,0,0.2); - } - - .on-link { - color: #60a5fa; - text-decoration: none; - font-weight: 500; - transition: all 0.2s ease; - position: relative; - } - - .on-link:hover { - color: #93c5fd; - text-decoration: underline; - text-underline-offset: 2px; - } - - .on-status { - display: flex; - align-items: center; - background: rgba(255,255,255,0.1); - padding: 4px 8px; - border-radius: 20px; - font-size: 12px; - font-weight: 500; - backdrop-filter: blur(5px); - -webkit-backdrop-filter: blur(5px); - border: 1px solid rgba(255,255,255,0.15); - transition: all 0.2s ease; - flex-shrink: 0; - } - - .on-status:hover { - background: rgba(255,255,255,0.15); - transform: scale(1.02); - } - - .on-status-icon { - margin-right: 6px; - display: flex; - align-items: center; - animation: pulse 2s infinite; - } - - .on-status-text { - color: #10B981; - font-weight: 600; - text-shadow: 0 1px 2px rgba(0,0,0,0.2); - } - - @keyframes marquee { - 0% { transform: translateX(0); } - 100% { transform: translateX(-100%); } - } - - @keyframes pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.7; } - } - - @keyframes slideIn { - from { - opacity: 0; - transform: translateY(${CONFIG.position === 'bottom' ? '100%' : '-100%'}); - } - to { - opacity: 1; - transform: translateY(0); - } - } - - - /* Responsive design */ - @media (max-width: 768px) { - .opennetwork-banner { - font-size: 12px; - padding: 6px 12px; - min-height: 36px; - } - - .on-flag-logo { - width: 16px; - height: 16px; - margin-right: 8px; - } - - .on-status { - font-size: 11px; - padding: 3px 6px; - } - - .on-left-section { - margin-right: 12px; - } - } - - @media (max-width: 480px) { - .opennetwork-banner { - font-size: 11px; - padding: 5px 8px; - } - - .on-status-text { - display: none; - } - - .on-status { - width: 20px; - height: 20px; - justify-content: center; - padding: 0; - border-radius: 50%; - } - - .on-status-icon { - margin-right: 0; - } - } - `; - return style; - }; - - const initBanner = async () => { - // Prevent multiple initializations - if (banner) return; - - // Get initial status - const status = await checkSystemStatus(); - const domain = getCurrentDomain(); - - // Create banner element - banner = document.createElement('div'); - banner.className = 'opennetwork-banner on-animate-in'; - banner.innerHTML = createBannerHTML(domain, status); - - // Add styles - const styles = createBannerStyles(); - document.head.appendChild(styles); - - // Add banner to body - document.body.appendChild(banner); - - // Setup event listeners - window.addEventListener('resize', checkTextOverflow); - - // Check for text overflow after banner is rendered - setTimeout(() => { - checkTextOverflow(); - updateStatusIndicator(status.status, status.message); - }, 100); - - // Setup periodic status checks - statusCheck = setInterval(async () => { - const newStatus = await checkSystemStatus(); - updateStatusIndicator(newStatus.status, newStatus.message); - }, CONFIG.checkInterval); - - console.log('OpenNetwork Banner initialized for:', domain); - }; - - const destroyBanner = () => { - if (banner) { - banner.remove(); - banner = null; - } - - if (statusCheck) { - clearInterval(statusCheck); - statusCheck = null; - } - - const styles = document.getElementById('opennetwork-banner-styles'); - if (styles) { - styles.remove(); - } - - window.removeEventListener('resize', checkTextOverflow); - isMarqueeActive = false; - }; - - // Public API - window.OpenNetworkBanner = { - init: initBanner, - destroy: destroyBanner, - updateStatus: updateStatusIndicator, - config: CONFIG - }; - - // Auto-initialize when DOM is ready - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initBanner); - } else { - initBanner(); - } - -})(); diff --git a/scripts/opennetwork-banner.min.js b/scripts/opennetwork-banner.min.js deleted file mode 100644 index 3e98684..0000000 --- a/scripts/opennetwork-banner.min.js +++ /dev/null @@ -1,423 +0,0 @@ -/** - * OpenNetwork Collective Banner - * Advanced JavaScript banner for website registration display - * Version: 1.0.0 - */ - -(function() { - 'use strict'; - - // Configuration - const CONFIG = { - flagLogoUrl: 'https://openrockets.com/assets/press/opennetwork.png', - affiliateUrl: 'https://opennetworked.org/en/privacy/affiliates', - statusApiUrl: 'https://status.opennetworked.org/api/status', // Placeholder API - animationDuration: 15000, // 15 seconds for marquee - checkInterval: 300000, // 5 minutes status check - position: 'bottom' // 'top' or 'bottom' - }; - - // Global state - let banner = null; - let statusCheck = null; - let isMarqueeActive = false; - - // Utility functions - const getCurrentDomain = () => { - return window.location.hostname.replace('www.', ''); - }; - - const createSVGIcon = (type, color = '#10B981') => { - const svgMap = { - 'operational': ` - - - - - - `, - 'warning': ` - - - - - `, - 'error': ` - - - - - ` - }; - return svgMap[type] || svgMap['operational']; - }; - - const checkSystemStatus = async () => { - try { - // Simulated status check - replace with actual API call - const response = await fetch(CONFIG.statusApiUrl).catch(() => ({ - ok: true, - json: () => Promise.resolve({ status: 'operational' }) - })); - - if (response.ok) { - const data = await response.json(); - return { - status: data.status || 'operational', - message: data.message || 'All Systems Operational' - }; - } - } catch (error) { - console.warn('OpenNetwork Banner: Status check failed', error); - } - - // Fallback to operational status - return { - status: 'operational', - message: 'All Systems Operational' - }; - }; - - const updateStatusIndicator = (status, message) => { - const statusElement = banner.querySelector('.on-status'); - if (!statusElement) return; - - const iconElement = statusElement.querySelector('.on-status-icon'); - const textElement = statusElement.querySelector('.on-status-text'); - - if (iconElement && textElement) { - iconElement.innerHTML = createSVGIcon(status); - textElement.textContent = message; - - // Update colors based on status - const colorMap = { - 'operational': '#10B981', - 'warning': '#F59E0B', - 'error': '#EF4444' - }; - - textElement.style.color = colorMap[status] || colorMap['operational']; - } - }; - - const checkTextOverflow = () => { - const textContainer = banner.querySelector('.on-text-content'); - const statusContainer = banner.querySelector('.on-status'); - - if (!textContainer || !statusContainer) return; - - const containerWidth = banner.offsetWidth - 60; // Account for padding - const textWidth = textContainer.scrollWidth; - const statusWidth = statusContainer.offsetWidth; - const totalWidth = textWidth + statusWidth + 20; // 20px gap - - - }; - - const startMarquee = () => { - if (isMarqueeActive) return; - - const textContent = banner.querySelector('.on-text-content'); - if (!textContent) return; - - isMarqueeActive = true; - textContent.style.animation = `marquee ${CONFIG.animationDuration}ms linear infinite`; - }; - - const stopMarquee = () => { - if (!isMarqueeActive) return; - - const textContent = banner.querySelector('.on-text-content'); - if (!textContent) return; - - isMarqueeActive = false; - textContent.style.animation = 'none'; - }; - - const createBannerHTML = (domain, status) => { - return ` -
-
- -
- ${domain} is a registered non-profit, currently active in operation under - OpenNetwork Collective -
-
-
-
${createSVGIcon(status.status)}
- ${status.message} -
-
- `; - }; - - const createBannerStyles = () => { - const style = document.createElement('style'); - style.id = 'opennetwork-banner-styles'; - style.textContent = ` - .opennetwork-banner { - flex-wrap:wrap; - ${CONFIG.position}: 0; - left: 0; - right: 0; - background: linear-gradient(135deg, #000000ff 0%, #000000ff 100%); - color: white; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; - font-size: 13px; - line-height: 1.4; - z-index: 999999; - box-shadow: ${CONFIG.position === 'bottom' ? '0 -2px 12px rgba(0,0,0,0.15)' : '0 2px 12px rgba(0,0,0,0.15)'}; - border-${CONFIG.position === 'bottom' ? 'top' : 'bottom'}: 1px solid rgba(255,255,255,0.1); - backdrop-filter: blur(10px); - -webkit-backdrop-filter: blur(10px); - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - padding: 8px 16px; - min-height: 40px; - display: flex; - align-items: center; - } - - .opennetwork-banner:hover { - background: linear-gradient(135deg, #000000ff 0%, #000000ff 100%); - transform: translateY(${CONFIG.position === 'bottom' ? '-1px' : '1px'}); - } - - .on-banner-content { - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; - max-width: 1200px; - margin: 0 auto; - overflow: hidden; - } - - .on-left-section { - display: flex; - align-items: center; - flex: 1; - overflow: hidden; - margin-right: 20px; - } - - .on-flag-logo { - width: 20px; - height: 20px; - margin-right: 12px; - border-radius: 3px; - box-shadow: 0 2px 4px rgba(0,0,0,0.2); - transition: transform 0.2s ease; - flex-shrink: 0; - } - - .on-flag-logo:hover { - transform: scale(1.1); - } - - .on-text-content { - white-space: nowrap; - overflow: hidden; - color: rgba(255,255,255,0.95); - font-weight: 400; - } - - .on-domain { - font-weight: 600; - color: #fbbf24; - text-shadow: 0 1px 2px rgba(0,0,0,0.2); - } - - .on-link { - color: #60a5fa; - text-decoration: none; - font-weight: 500; - transition: all 0.2s ease; - position: relative; - } - - .on-link:hover { - color: #93c5fd; - text-decoration: underline; - text-underline-offset: 2px; - } - - .on-status { - display: flex; - align-items: center; - background: rgba(255,255,255,0.1); - padding: 4px 8px; - border-radius: 20px; - font-size: 12px; - font-weight: 500; - backdrop-filter: blur(5px); - -webkit-backdrop-filter: blur(5px); - border: 1px solid rgba(255,255,255,0.15); - transition: all 0.2s ease; - flex-shrink: 0; - } - - .on-status:hover { - background: rgba(255,255,255,0.15); - transform: scale(1.02); - } - - .on-status-icon { - margin-right: 6px; - display: flex; - align-items: center; - animation: pulse 2s infinite; - } - - .on-status-text { - color: #10B981; - font-weight: 600; - text-shadow: 0 1px 2px rgba(0,0,0,0.2); - } - - @keyframes marquee { - 0% { transform: translateX(0); } - 100% { transform: translateX(-100%); } - } - - @keyframes pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.7; } - } - - @keyframes slideIn { - from { - opacity: 0; - transform: translateY(${CONFIG.position === 'bottom' ? '100%' : '-100%'}); - } - to { - opacity: 1; - transform: translateY(0); - } - } - - - /* Responsive design */ - @media (max-width: 768px) { - .opennetwork-banner { - font-size: 12px; - padding: 6px 12px; - min-height: 36px; - } - - .on-flag-logo { - width: 16px; - height: 16px; - margin-right: 8px; - } - - .on-status { - font-size: 11px; - padding: 3px 6px; - } - - .on-left-section { - margin-right: 12px; - } - } - - @media (max-width: 480px) { - .opennetwork-banner { - font-size: 11px; - padding: 5px 8px; - } - - .on-status-text { - display: none; - } - - .on-status { - width: 20px; - height: 20px; - justify-content: center; - padding: 0; - border-radius: 50%; - } - - .on-status-icon { - margin-right: 0; - } - } - `; - return style; - }; - - const initBanner = async () => { - // Prevent multiple initializations - if (banner) return; - - // Get initial status - const status = await checkSystemStatus(); - const domain = getCurrentDomain(); - - // Create banner element - banner = document.createElement('div'); - banner.className = 'opennetwork-banner on-animate-in'; - banner.innerHTML = createBannerHTML(domain, status); - - // Add styles - const styles = createBannerStyles(); - document.head.appendChild(styles); - - // Add banner to body - document.body.appendChild(banner); - - // Setup event listeners - window.addEventListener('resize', checkTextOverflow); - - // Check for text overflow after banner is rendered - setTimeout(() => { - checkTextOverflow(); - updateStatusIndicator(status.status, status.message); - }, 100); - - // Setup periodic status checks - statusCheck = setInterval(async () => { - const newStatus = await checkSystemStatus(); - updateStatusIndicator(newStatus.status, newStatus.message); - }, CONFIG.checkInterval); - - console.log('OpenNetwork Banner initialized for:', domain); - }; - - const destroyBanner = () => { - if (banner) { - banner.remove(); - banner = null; - } - - if (statusCheck) { - clearInterval(statusCheck); - statusCheck = null; - } - - const styles = document.getElementById('opennetwork-banner-styles'); - if (styles) { - styles.remove(); - } - - window.removeEventListener('resize', checkTextOverflow); - isMarqueeActive = false; - }; - - // Public API - window.OpenNetworkBanner = { - init: initBanner, - destroy: destroyBanner, - updateStatus: updateStatusIndicator, - config: CONFIG - }; - - // Auto-initialize when DOM is ready - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initBanner); - } else { - initBanner(); - } - -})(); diff --git a/scripts/opennetwork-injector.js b/scripts/opennetwork-injector.js deleted file mode 100644 index e69de29..0000000 diff --git a/scripts/parrot-assistant.js b/scripts/parrot-assistant.js deleted file mode 100644 index f0d7a22..0000000 --- a/scripts/parrot-assistant.js +++ /dev/null @@ -1,208 +0,0 @@ -// Parrot Assistant - Interactive Screen Friend -class ParrotAssistant { - constructor() { - this.parrot = document.getElementById('parrot-assistant'); - this.bubble = document.getElementById('parrot-bubble'); - this.bubbleContent = document.getElementById('bubble-content'); - this.categoryBadge = document.getElementById('category-badge'); - this.parrotGif = this.parrot.querySelector('.parrot-gif'); - - this.currentMessageIndex = 0; - this.isVisible = false; - this.messageTimeout = null; - - // Message categories with content - this.messages = { - tips: [ - "Need free laptop stickers? Solve issues in our GitHub and we will contact you.", - "It's really lonely here to stay all the day..", - "Building an Innovation? Shall we give you 20 dollars FREE?", - "Not from India, Sri Lanka, Maldives, Malaysia? Let's start a new club in your country!", - "Hmm.." - ], - announcements: [ - "DAYDREAM SRI LANKA is happening 27th in Colombo!! Join it!", - "Weekly coding meetups every Saturday - virtual and in-person available!", - "Top 3 contributors get free domain names and full project ownership!", - "We've reached 2.9k+ GitHub stars! Thank you amazing community!" - ], - memes: [ - "Why do programmers prefer dark mode? Because light attracts bugs!", - "Me: I'll just fix this one small bug... 3 hours later... why is everything broken?", - "99 little bugs in the code, 99 little bugs... Take one down, patch it around... 127 little bugs in the code!", - "Git commit -m 'fixed everything' ...famous last words!", - "Coffee: Because debugging at 3 AM is a lifestyle choice, not a requirement!" - ], - ideas: [ - "Idea: Create a mobile app version of your favorite OpenRockets project!", - "Brainstorm: What if we added AR features to CityofGits? Build in 3D space!", - "Vision: AI-powered code review bot for our repositories!", - "Concept: Design system library for consistent OpenRockets branding!", - "Dream: Global coding bootcamp program powered by our community!" - ] - }; - - this.init(); - } - - init() { - // Add entrance animation - setTimeout(() => { - this.parrot.classList.add('animate-entrance'); - }, 1000); - - // Set up event listeners - this.setupEventListeners(); - - // Start showing messages - setTimeout(() => { - this.showRandomMessage(); - this.startMessageCycle(); - }, 3000); - } - - setupEventListeners() { - // Parrot click interaction - this.parrotGif.addEventListener('click', () => { - this.bounceParrot(); - this.showRandomMessage(); - }); - - // Scroll-triggered messages - let scrollTimeout; - window.addEventListener('scroll', () => { - clearTimeout(scrollTimeout); - scrollTimeout = setTimeout(() => { - if (Math.random() < 0.1) { // 10% chance on scroll - this.showRandomMessage(); - } - }, 500); - }); - - // Hover interactions on feature cards - document.querySelectorAll('.feature-card, .btn').forEach(element => { - element.addEventListener('mouseenter', () => { - if (Math.random() < 0.15) { // 15% chance on hover - this.showContextualMessage(element); - } - }); - }); - - // Hide bubble when clicking outside - document.addEventListener('click', (e) => { - if (!this.parrot.contains(e.target)) { - this.hideBubble(); - } - }); - } - - showRandomMessage() { - const categories = Object.keys(this.messages); - const randomCategory = categories[Math.floor(Math.random() * categories.length)]; - const messages = this.messages[randomCategory]; - const randomMessage = messages[Math.floor(Math.random() * messages.length)]; - - this.displayMessage(randomMessage, randomCategory); - } - - showContextualMessage(element) { - let message = ""; - let category = "tips"; - - // Context-aware messages based on element - if (element.classList.contains('btn-primary')) { - message = "🚀 Ready to start your coding journey? Click that button and let's build something amazing!"; - category = "announcements"; - } else if (element.classList.contains('feature-card')) { - const tips = [ - "This feature is perfect for beginners! Don't be afraid to dive in!", - "Hover over me more often for secret tips and tricks!", - "Each feature connects to real projects you can contribute to!" - ]; - message = tips[Math.floor(Math.random() * tips.length)]; - category = "tips"; - } - - if (message) { - this.displayMessage(message, category); - } - } - - displayMessage(message, category = 'tips') { - this.bubbleContent.textContent = message; - this.categoryBadge.textContent = category.toUpperCase(); - - // Update category badge color based on type - const categoryColors = { - tips: 'var(--gradient-electric)', - announcements: 'var(--gradient-sunset)', - memes: 'var(--gradient-cosmic)', - ideas: 'var(--gradient-neon)' - }; - - this.categoryBadge.style.background = categoryColors[category] || 'var(--gradient-electric)'; - - // Show bubble and badge - this.bubble.classList.add('show'); - this.categoryBadge.classList.add('show'); - this.isVisible = true; - - // Bounce parrot - this.bounceParrot(); - - // Auto-hide after 8 seconds - clearTimeout(this.messageTimeout); - this.messageTimeout = setTimeout(() => { - this.hideBubble(); - }, 8000); - } - - hideBubble() { - this.bubble.classList.remove('show'); - this.categoryBadge.classList.remove('show'); - this.isVisible = false; - } - - bounceParrot() { - this.parrotGif.classList.remove('bounce'); - setTimeout(() => { - this.parrotGif.classList.add('bounce'); - }, 10); - - setTimeout(() => { - this.parrotGif.classList.remove('bounce'); - }, 600); - } - - startMessageCycle() { - // Show random messages every 15-30 seconds - const showMessage = () => { - if (!this.isVisible) { - this.showRandomMessage(); - } - - // Random interval between 15-30 seconds - const nextInterval = 15000 + Math.random() * 15000; - setTimeout(showMessage, nextInterval); - }; - - setTimeout(showMessage, 20000); // First cycle after 20 seconds - } -} - -// Initialize when DOM is loaded -document.addEventListener('DOMContentLoaded', () => { - new ParrotAssistant(); -}); - -// Add some fun Easter eggs -document.addEventListener('keydown', (e) => { - // Konami code easter egg (up, up, down, down, left, right, left, right, b, a) - if (e.code === 'KeyP' && e.ctrlKey) { // Ctrl+P for parrot party - const parrot = document.getElementById('parrot-assistant'); - if (parrot) { - const assistant = new ParrotAssistant(); - assistant.displayMessage("🎉 *SQUAWK SQUAWK* You found the secret parrot party! 🦜🎊", "memes"); - } - } -}); diff --git a/server-new.js b/server-new.js deleted file mode 100644 index 8fd2f9d..0000000 --- a/server-new.js +++ /dev/null @@ -1,547 +0,0 @@ -const express = require('express'); -const cors = require('cors'); -const bcrypt = require('bcryptjs'); -const jwt = require('jsonwebtoken'); -const multer = require('multer'); -const { DataAPIClient } = require('@datastax/astra-db-ts'); -const path = require('path'); -const fs = require('fs'); -require('dotenv').config(); - -const app = express(); -const PORT = process.env.PORT || 3000; -const JWT_SECRET = process.env.JWT_SECRET || 'openrockets_secret_key_2024'; - -// Middleware -app.use(cors()); -app.use(express.json({ limit: '50mb' })); -app.use(express.urlencoded({ extended: true, limit: '50mb' })); -app.use(express.static('.')); - -// AstraDB Client -const client = new DataAPIClient(process.env.ASTRA_DB_APPLICATION_TOKEN); -const db = client.db(process.env.ASTRA_DB_API_ENDPOINT); - -// Collections -const users = db.collection('users'); -const posts = db.collection('posts'); -const comments = db.collection('comments'); -const events = db.collection('events'); -const messages = db.collection('messages'); -const notifications = db.collection('notifications'); - -// Helper function to convert image to DataURI -function imageToDataURI(imageBuffer, mimeType) { - return `data:${mimeType};base64,${imageBuffer.toString('base64')}`; -} - -// Multer setup for image uploads (in memory) -const upload = multer({ - storage: multer.memoryStorage(), - limits: { - fileSize: 5 * 1024 * 1024, // 5MB limit - }, - fileFilter: (req, file, cb) => { - if (file.mimetype.startsWith('image/')) { - cb(null, true); - } else { - cb(new Error('Only image files are allowed'), false); - } - } -}); - -// Authentication middleware -const authenticateToken = (req, res, next) => { - const authHeader = req.headers['authorization']; - const token = authHeader && authHeader.split(' ')[1]; - - if (!token) { - return res.status(401).json({ success: false, message: 'Access token required' }); - } - - jwt.verify(token, JWT_SECRET, (err, user) => { - if (err) { - return res.status(403).json({ success: false, message: 'Invalid token' }); - } - req.user = user; - next(); - }); -}; - -// Initialize collections if they don't exist -async function initializeCollections() { - try { - await users.insertOne({ _id: 'test', email: 'test@test.com' }); - await users.deleteOne({ _id: 'test' }); - console.log('Collections initialized successfully'); - } catch (error) { - console.log('Collections may already exist:', error.message); - } -} - -// Routes - -// User Registration -app.post('/api/register', async (req, res) => { - try { - const { email, password, fullName, studentId, role = 'student' } = req.body; - - // Check if user already exists - const existingUser = await users.findOne({ email }); - if (existingUser) { - return res.status(400).json({ success: false, message: 'User already exists' }); - } - - // Hash password - const hashedPassword = await bcrypt.hash(password, 12); - - // Create user - const newUser = { - email, - password: hashedPassword, - fullName, - studentId, - role, - profilePicture: '', - status: '', - verified: false, - joinedDate: new Date().toISOString(), - lastActive: new Date().toISOString(), - bio: '', - location: '', - website: '' - }; - - await users.insertOne(newUser); - - const token = jwt.sign( - { userId: newUser._id, email: newUser.email, role: newUser.role }, - JWT_SECRET, - { expiresIn: '24h' } - ); - - res.json({ - success: true, - token, - user: { - id: newUser._id, - email: newUser.email, - fullName: newUser.fullName, - role: newUser.role, - verified: newUser.verified - } - }); - } catch (error) { - console.error('Registration error:', error); - res.status(500).json({ success: false, message: 'Registration failed' }); - } -}); - -// User Login -app.post('/api/login', async (req, res) => { - try { - const { email, password } = req.body; - - const user = await users.findOne({ email }); - if (!user) { - return res.status(400).json({ success: false, message: 'Invalid credentials' }); - } - - const isValidPassword = await bcrypt.compare(password, user.password); - if (!isValidPassword) { - return res.status(400).json({ success: false, message: 'Invalid credentials' }); - } - - // Update last active - await users.updateOne( - { _id: user._id }, - { $set: { lastActive: new Date().toISOString() } } - ); - - const token = jwt.sign( - { userId: user._id, email: user.email, role: user.role }, - JWT_SECRET, - { expiresIn: '24h' } - ); - - res.json({ - success: true, - token, - user: { - id: user._id, - email: user.email, - fullName: user.fullName, - role: user.role, - verified: user.verified, - profilePicture: user.profilePicture, - status: user.status - } - }); - } catch (error) { - console.error('Login error:', error); - res.status(500).json({ success: false, message: 'Login failed' }); - } -}); - -// Get User Profile -app.get('/api/profile', authenticateToken, async (req, res) => { - try { - const user = await users.findOne({ _id: req.user.userId }); - if (!user) { - return res.status(404).json({ success: false, message: 'User not found' }); - } - - res.json({ - success: true, - user: { - id: user._id, - email: user.email, - fullName: user.fullName, - studentId: user.studentId, - role: user.role, - profilePicture: user.profilePicture, - status: user.status, - verified: user.verified, - bio: user.bio, - location: user.location, - website: user.website, - joinedDate: user.joinedDate - } - }); - } catch (error) { - console.error('Profile fetch error:', error); - res.status(500).json({ success: false, message: 'Failed to fetch profile' }); - } -}); - -// Update User Profile -app.put('/api/profile', authenticateToken, upload.single('profilePicture'), async (req, res) => { - try { - const { fullName, status, bio, location, website } = req.body; - const updateData = { fullName, status, bio, location, website }; - - if (req.file) { - updateData.profilePicture = imageToDataURI(req.file.buffer, req.file.mimetype); - } - - await users.updateOne( - { _id: req.user.userId }, - { $set: updateData } - ); - - res.json({ success: true, message: 'Profile updated successfully' }); - } catch (error) { - console.error('Profile update error:', error); - res.status(500).json({ success: false, message: 'Failed to update profile' }); - } -}); - -// Create Post -app.post('/api/posts', authenticateToken, upload.single('image'), async (req, res) => { - try { - const { content, tags } = req.body; - - const newPost = { - authorId: req.user.userId, - content, - tags: tags ? tags.split(',').map(tag => tag.trim()) : [], - likes: [], - comments: [], - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString() - }; - - if (req.file) { - newPost.image = imageToDataURI(req.file.buffer, req.file.mimetype); - } - - const result = await posts.insertOne(newPost); - - // Get the author details - const author = await users.findOne({ _id: req.user.userId }); - - res.json({ - success: true, - post: { - ...newPost, - _id: result.insertedId, - author: { - fullName: author.fullName, - profilePicture: author.profilePicture, - verified: author.verified - } - } - }); - } catch (error) { - console.error('Post creation error:', error); - res.status(500).json({ success: false, message: 'Failed to create post' }); - } -}); - -// Get Posts -app.get('/api/posts', async (req, res) => { - try { - const page = parseInt(req.query.page) || 1; - const limit = parseInt(req.query.limit) || 10; - const skip = (page - 1) * limit; - - const postList = await posts.find({}, { - sort: { createdAt: -1 }, - limit, - skip - }).toArray(); - - // Get author details for each post - for (let post of postList) { - const author = await users.findOne({ _id: post.authorId }); - post.author = { - fullName: author.fullName, - profilePicture: author.profilePicture, - verified: author.verified - }; - } - - res.json({ success: true, posts: postList }); - } catch (error) { - console.error('Posts fetch error:', error); - res.status(500).json({ success: false, message: 'Failed to fetch posts' }); - } -}); - -// Like/Unlike Post -app.post('/api/posts/:postId/like', authenticateToken, async (req, res) => { - try { - const { postId } = req.params; - const userId = req.user.userId; - - const post = await posts.findOne({ _id: postId }); - if (!post) { - return res.status(404).json({ success: false, message: 'Post not found' }); - } - - const liked = post.likes.includes(userId); - - if (liked) { - await posts.updateOne( - { _id: postId }, - { $pull: { likes: userId } } - ); - } else { - await posts.updateOne( - { _id: postId }, - { $addToSet: { likes: userId } } - ); - } - - res.json({ success: true, liked: !liked }); - } catch (error) { - console.error('Like toggle error:', error); - res.status(500).json({ success: false, message: 'Failed to toggle like' }); - } -}); - -// Add Comment -app.post('/api/posts/:postId/comments', authenticateToken, async (req, res) => { - try { - const { postId } = req.params; - const { content } = req.body; - - const newComment = { - _id: Date.now().toString(), - authorId: req.user.userId, - content, - createdAt: new Date().toISOString() - }; - - await posts.updateOne( - { _id: postId }, - { $push: { comments: newComment } } - ); - - // Get author details - const author = await users.findOne({ _id: req.user.userId }); - - res.json({ - success: true, - comment: { - ...newComment, - author: { - fullName: author.fullName, - profilePicture: author.profilePicture, - verified: author.verified - } - } - }); - } catch (error) { - console.error('Comment creation error:', error); - res.status(500).json({ success: false, message: 'Failed to add comment' }); - } -}); - -// Get Comments for a Post -app.get('/api/posts/:postId/comments', async (req, res) => { - try { - const { postId } = req.params; - - const post = await posts.findOne({ _id: postId }); - if (!post) { - return res.status(404).json({ success: false, message: 'Post not found' }); - } - - // Get author details for each comment - for (let comment of post.comments) { - const author = await users.findOne({ _id: comment.authorId }); - comment.author = { - fullName: author.fullName, - profilePicture: author.profilePicture, - verified: author.verified - }; - } - - res.json({ success: true, comments: post.comments }); - } catch (error) { - console.error('Comments fetch error:', error); - res.status(500).json({ success: false, message: 'Failed to fetch comments' }); - } -}); - -// Create Event -app.post('/api/events', authenticateToken, async (req, res) => { - try { - const { title, description, date, time, location, type } = req.body; - - const newEvent = { - title, - description, - date, - time, - location, - type, - organizerId: req.user.userId, - attendees: [], - createdAt: new Date().toISOString() - }; - - await events.insertOne(newEvent); - - res.json({ success: true, event: newEvent }); - } catch (error) { - console.error('Event creation error:', error); - res.status(500).json({ success: false, message: 'Failed to create event' }); - } -}); - -// Get Events -app.get('/api/events', async (req, res) => { - try { - const eventList = await events.find({}, { - sort: { date: 1, time: 1 } - }).toArray(); - - // Get organizer details for each event - for (let event of eventList) { - const organizer = await users.findOne({ _id: event.organizerId }); - event.organizer = { - fullName: organizer.fullName, - verified: organizer.verified - }; - } - - res.json({ success: true, events: eventList }); - } catch (error) { - console.error('Events fetch error:', error); - res.status(500).json({ success: false, message: 'Failed to fetch events' }); - } -}); - -// Join Event -app.post('/api/events/:eventId/join', authenticateToken, async (req, res) => { - try { - const { eventId } = req.params; - const userId = req.user.userId; - - await events.updateOne( - { _id: eventId }, - { $addToSet: { attendees: userId } } - ); - - res.json({ success: true, message: 'Successfully joined event' }); - } catch (error) { - console.error('Join event error:', error); - res.status(500).json({ success: false, message: 'Failed to join event' }); - } -}); - -// Search -app.get('/api/search', async (req, res) => { - try { - const { q, type = 'all' } = req.query; - const results = {}; - - if (type === 'all' || type === 'posts') { - results.posts = await posts.find({ - $or: [ - { content: { $regex: q, $options: 'i' } }, - { tags: { $regex: q, $options: 'i' } } - ] - }).limit(10).toArray(); - } - - if (type === 'all' || type === 'users') { - results.users = await users.find({ - $or: [ - { fullName: { $regex: q, $options: 'i' } }, - { email: { $regex: q, $options: 'i' } } - ] - }, { - projection: { password: 0 } - }).limit(10).toArray(); - } - - if (type === 'all' || type === 'events') { - results.events = await events.find({ - $or: [ - { title: { $regex: q, $options: 'i' } }, - { description: { $regex: q, $options: 'i' } } - ] - }).limit(10).toArray(); - } - - res.json({ success: true, results }); - } catch (error) { - console.error('Search error:', error); - res.status(500).json({ success: false, message: 'Search failed' }); - } -}); - -// Health check -app.get('/api/health', (req, res) => { - res.json({ status: 'OK', timestamp: new Date().toISOString() }); -}); - -// Serve static files -app.get('/', (req, res) => { - res.sendFile(path.join(__dirname, 'landing.html')); -}); - -// Error handling middleware -app.use((error, req, res, next) => { - console.error('Error:', error); - res.status(500).json({ success: false, message: 'Internal server error' }); -}); - -// Start server -async function startServer() { - try { - await initializeCollections(); - app.listen(PORT, () => { - console.log(`Server running on port ${PORT}`); - console.log(`Access the website at: http://localhost:${PORT}`); - }); - } catch (error) { - console.error('Failed to start server:', error); - process.exit(1); - } -} - -startServer(); diff --git a/server-simple.js b/server-simple.js deleted file mode 100644 index 33f5f29..0000000 --- a/server-simple.js +++ /dev/null @@ -1,416 +0,0 @@ -const express = require('express'); -const cors = require('cors'); -const bcrypt = require('bcryptjs'); -const jwt = require('jsonwebtoken'); -const multer = require('multer'); -const path = require('path'); -require('dotenv').config(); - -const app = express(); -const PORT = process.env.PORT || 3000; -const JWT_SECRET = process.env.JWT_SECRET || 'openrockets_secret_key_2024'; - -// Middleware -app.use(cors()); -app.use(express.json({ limit: '50mb' })); -app.use(express.urlencoded({ extended: true, limit: '50mb' })); -app.use(express.static('.')); - -// In-memory storage for demo (replace with AstraDB later) -let users = []; -let posts = []; -let events = []; -let comments = []; - -// Helper function to convert image to DataURI -function imageToDataURI(imageBuffer, mimeType) { - return `data:${mimeType};base64,${imageBuffer.toString('base64')}`; -} - -// Multer setup for image uploads (in memory) -const upload = multer({ - storage: multer.memoryStorage(), - limits: { - fileSize: 5 * 1024 * 1024, // 5MB limit - }, - fileFilter: (req, file, cb) => { - if (file.mimetype.startsWith('image/')) { - cb(null, true); - } else { - cb(new Error('Only image files are allowed'), false); - } - } -}); - -// Authentication middleware -const authenticateToken = (req, res, next) => { - const authHeader = req.headers['authorization']; - const token = authHeader && authHeader.split(' ')[1]; - - if (!token) { - return res.status(401).json({ success: false, message: 'Access token required' }); - } - - jwt.verify(token, JWT_SECRET, (err, user) => { - if (err) { - return res.status(403).json({ success: false, message: 'Invalid token' }); - } - req.user = user; - next(); - }); -}; - -// Routes - -// User Registration -app.post('/api/register', async (req, res) => { - try { - const { email, password, fullName, studentId, role = 'student' } = req.body; - - // Check if user already exists - const existingUser = users.find(u => u.email === email); - if (existingUser) { - return res.status(400).json({ success: false, message: 'User already exists' }); - } - - // Hash password - const hashedPassword = await bcrypt.hash(password, 12); - - // Create user - const newUser = { - id: Date.now().toString(), - email, - password: hashedPassword, - fullName, - studentId, - role, - profilePicture: '', - status: '', - verified: false, - joinedDate: new Date().toISOString(), - lastActive: new Date().toISOString(), - bio: '', - location: '', - website: '' - }; - - users.push(newUser); - - const token = jwt.sign( - { userId: newUser.id, email: newUser.email, role: newUser.role }, - JWT_SECRET, - { expiresIn: '24h' } - ); - - res.json({ - success: true, - token, - user: { - id: newUser.id, - email: newUser.email, - fullName: newUser.fullName, - role: newUser.role, - verified: newUser.verified - } - }); - } catch (error) { - console.error('Registration error:', error); - res.status(500).json({ success: false, message: 'Registration failed' }); - } -}); - -// User Login -app.post('/api/login', async (req, res) => { - try { - const { email, password } = req.body; - - const user = users.find(u => u.email === email); - if (!user) { - return res.status(400).json({ success: false, message: 'Invalid credentials' }); - } - - const isValidPassword = await bcrypt.compare(password, user.password); - if (!isValidPassword) { - return res.status(400).json({ success: false, message: 'Invalid credentials' }); - } - - // Update last active - user.lastActive = new Date().toISOString(); - - const token = jwt.sign( - { userId: user.id, email: user.email, role: user.role }, - JWT_SECRET, - { expiresIn: '24h' } - ); - - res.json({ - success: true, - token, - user: { - id: user.id, - email: user.email, - fullName: user.fullName, - role: user.role, - verified: user.verified, - profilePicture: user.profilePicture, - status: user.status - } - }); - } catch (error) { - console.error('Login error:', error); - res.status(500).json({ success: false, message: 'Login failed' }); - } -}); - -// Get User Profile -app.get('/api/profile', authenticateToken, (req, res) => { - try { - const user = users.find(u => u.id === req.user.userId); - if (!user) { - return res.status(404).json({ success: false, message: 'User not found' }); - } - - res.json({ - success: true, - user: { - id: user.id, - email: user.email, - fullName: user.fullName, - studentId: user.studentId, - role: user.role, - profilePicture: user.profilePicture, - status: user.status, - verified: user.verified, - bio: user.bio, - location: user.location, - website: user.website, - joinedDate: user.joinedDate - } - }); - } catch (error) { - console.error('Profile fetch error:', error); - res.status(500).json({ success: false, message: 'Failed to fetch profile' }); - } -}); - -// Update User Profile -app.put('/api/profile', authenticateToken, upload.single('profilePicture'), (req, res) => { - try { - const { fullName, status, bio, location, website } = req.body; - const user = users.find(u => u.id === req.user.userId); - - if (!user) { - return res.status(404).json({ success: false, message: 'User not found' }); - } - - user.fullName = fullName || user.fullName; - user.status = status || user.status; - user.bio = bio || user.bio; - user.location = location || user.location; - user.website = website || user.website; - - if (req.file) { - user.profilePicture = imageToDataURI(req.file.buffer, req.file.mimetype); - } - - res.json({ success: true, message: 'Profile updated successfully' }); - } catch (error) { - console.error('Profile update error:', error); - res.status(500).json({ success: false, message: 'Failed to update profile' }); - } -}); - -// Create Post -app.post('/api/posts', authenticateToken, upload.single('image'), (req, res) => { - try { - const { content, tags } = req.body; - - const newPost = { - id: Date.now().toString(), - authorId: req.user.userId, - content, - tags: tags ? tags.split(',').map(tag => tag.trim()) : [], - likes: [], - comments: [], - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString() - }; - - if (req.file) { - newPost.image = imageToDataURI(req.file.buffer, req.file.mimetype); - } - - posts.push(newPost); - - // Get the author details - const author = users.find(u => u.id === req.user.userId); - - res.json({ - success: true, - post: { - ...newPost, - author: { - fullName: author.fullName, - profilePicture: author.profilePicture, - verified: author.verified - } - } - }); - } catch (error) { - console.error('Post creation error:', error); - res.status(500).json({ success: false, message: 'Failed to create post' }); - } -}); - -// Get Posts -app.get('/api/posts', (req, res) => { - try { - const page = parseInt(req.query.page) || 1; - const limit = parseInt(req.query.limit) || 10; - const startIndex = (page - 1) * limit; - const endIndex = startIndex + limit; - - const sortedPosts = posts.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); - const postList = sortedPosts.slice(startIndex, endIndex); - - // Get author details for each post - postList.forEach(post => { - const author = users.find(u => u.id === post.authorId); - post.author = { - fullName: author ? author.fullName : 'Unknown User', - profilePicture: author ? author.profilePicture : '', - verified: author ? author.verified : false - }; - }); - - res.json({ success: true, posts: postList }); - } catch (error) { - console.error('Posts fetch error:', error); - res.status(500).json({ success: false, message: 'Failed to fetch posts' }); - } -}); - -// Like/Unlike Post -app.post('/api/posts/:postId/like', authenticateToken, (req, res) => { - try { - const { postId } = req.params; - const userId = req.user.userId; - - const post = posts.find(p => p.id === postId); - if (!post) { - return res.status(404).json({ success: false, message: 'Post not found' }); - } - - const likeIndex = post.likes.indexOf(userId); - const liked = likeIndex === -1; - - if (liked) { - post.likes.push(userId); - } else { - post.likes.splice(likeIndex, 1); - } - - res.json({ success: true, liked }); - } catch (error) { - console.error('Like toggle error:', error); - res.status(500).json({ success: false, message: 'Failed to toggle like' }); - } -}); - -// Create Event -app.post('/api/events', authenticateToken, (req, res) => { - try { - const { title, description, date, time, location, type } = req.body; - - const newEvent = { - id: Date.now().toString(), - title, - description, - date, - time, - location, - type, - organizerId: req.user.userId, - attendees: [], - createdAt: new Date().toISOString() - }; - - events.push(newEvent); - - res.json({ success: true, event: newEvent }); - } catch (error) { - console.error('Event creation error:', error); - res.status(500).json({ success: false, message: 'Failed to create event' }); - } -}); - -// Get Events -app.get('/api/events', (req, res) => { - try { - // Sort events by date and time - const sortedEvents = events.sort((a, b) => { - const dateA = new Date(a.date + ' ' + a.time); - const dateB = new Date(b.date + ' ' + b.time); - return dateA - dateB; - }); - - // Get organizer details for each event - sortedEvents.forEach(event => { - const organizer = users.find(u => u.id === event.organizerId); - event.organizer = { - fullName: organizer ? organizer.fullName : 'Unknown User', - verified: organizer ? organizer.verified : false - }; - }); - - res.json({ success: true, events: sortedEvents }); - } catch (error) { - console.error('Events fetch error:', error); - res.status(500).json({ success: false, message: 'Failed to fetch events' }); - } -}); - -// Join Event -app.post('/api/events/:eventId/join', authenticateToken, (req, res) => { - try { - const { eventId } = req.params; - const userId = req.user.userId; - - const event = events.find(e => e.id === eventId); - if (!event) { - return res.status(404).json({ success: false, message: 'Event not found' }); - } - - if (!event.attendees.includes(userId)) { - event.attendees.push(userId); - } - - res.json({ success: true, message: 'Successfully joined event' }); - } catch (error) { - console.error('Join event error:', error); - res.status(500).json({ success: false, message: 'Failed to join event' }); - } -}); - -// Health check -app.get('/api/health', (req, res) => { - res.json({ status: 'OK', timestamp: new Date().toISOString() }); -}); - -// Serve static files -app.get('/', (req, res) => { - res.sendFile(path.join(__dirname, 'landing.html')); -}); - -// Error handling middleware -app.use((error, req, res, next) => { - console.error('Error:', error); - res.status(500).json({ success: false, message: 'Internal server error' }); -}); - -// Start server -app.listen(PORT, () => { - console.log(`🚀 OpenRockets LMS Server running on port ${PORT}`); - console.log(`📚 Access the website at: http://localhost:${PORT}`); - console.log(`🔧 API endpoints available at: http://localhost:${PORT}/api/`); -}); diff --git a/server.js b/server.js deleted file mode 100644 index 8fd2f9d..0000000 --- a/server.js +++ /dev/null @@ -1,547 +0,0 @@ -const express = require('express'); -const cors = require('cors'); -const bcrypt = require('bcryptjs'); -const jwt = require('jsonwebtoken'); -const multer = require('multer'); -const { DataAPIClient } = require('@datastax/astra-db-ts'); -const path = require('path'); -const fs = require('fs'); -require('dotenv').config(); - -const app = express(); -const PORT = process.env.PORT || 3000; -const JWT_SECRET = process.env.JWT_SECRET || 'openrockets_secret_key_2024'; - -// Middleware -app.use(cors()); -app.use(express.json({ limit: '50mb' })); -app.use(express.urlencoded({ extended: true, limit: '50mb' })); -app.use(express.static('.')); - -// AstraDB Client -const client = new DataAPIClient(process.env.ASTRA_DB_APPLICATION_TOKEN); -const db = client.db(process.env.ASTRA_DB_API_ENDPOINT); - -// Collections -const users = db.collection('users'); -const posts = db.collection('posts'); -const comments = db.collection('comments'); -const events = db.collection('events'); -const messages = db.collection('messages'); -const notifications = db.collection('notifications'); - -// Helper function to convert image to DataURI -function imageToDataURI(imageBuffer, mimeType) { - return `data:${mimeType};base64,${imageBuffer.toString('base64')}`; -} - -// Multer setup for image uploads (in memory) -const upload = multer({ - storage: multer.memoryStorage(), - limits: { - fileSize: 5 * 1024 * 1024, // 5MB limit - }, - fileFilter: (req, file, cb) => { - if (file.mimetype.startsWith('image/')) { - cb(null, true); - } else { - cb(new Error('Only image files are allowed'), false); - } - } -}); - -// Authentication middleware -const authenticateToken = (req, res, next) => { - const authHeader = req.headers['authorization']; - const token = authHeader && authHeader.split(' ')[1]; - - if (!token) { - return res.status(401).json({ success: false, message: 'Access token required' }); - } - - jwt.verify(token, JWT_SECRET, (err, user) => { - if (err) { - return res.status(403).json({ success: false, message: 'Invalid token' }); - } - req.user = user; - next(); - }); -}; - -// Initialize collections if they don't exist -async function initializeCollections() { - try { - await users.insertOne({ _id: 'test', email: 'test@test.com' }); - await users.deleteOne({ _id: 'test' }); - console.log('Collections initialized successfully'); - } catch (error) { - console.log('Collections may already exist:', error.message); - } -} - -// Routes - -// User Registration -app.post('/api/register', async (req, res) => { - try { - const { email, password, fullName, studentId, role = 'student' } = req.body; - - // Check if user already exists - const existingUser = await users.findOne({ email }); - if (existingUser) { - return res.status(400).json({ success: false, message: 'User already exists' }); - } - - // Hash password - const hashedPassword = await bcrypt.hash(password, 12); - - // Create user - const newUser = { - email, - password: hashedPassword, - fullName, - studentId, - role, - profilePicture: '', - status: '', - verified: false, - joinedDate: new Date().toISOString(), - lastActive: new Date().toISOString(), - bio: '', - location: '', - website: '' - }; - - await users.insertOne(newUser); - - const token = jwt.sign( - { userId: newUser._id, email: newUser.email, role: newUser.role }, - JWT_SECRET, - { expiresIn: '24h' } - ); - - res.json({ - success: true, - token, - user: { - id: newUser._id, - email: newUser.email, - fullName: newUser.fullName, - role: newUser.role, - verified: newUser.verified - } - }); - } catch (error) { - console.error('Registration error:', error); - res.status(500).json({ success: false, message: 'Registration failed' }); - } -}); - -// User Login -app.post('/api/login', async (req, res) => { - try { - const { email, password } = req.body; - - const user = await users.findOne({ email }); - if (!user) { - return res.status(400).json({ success: false, message: 'Invalid credentials' }); - } - - const isValidPassword = await bcrypt.compare(password, user.password); - if (!isValidPassword) { - return res.status(400).json({ success: false, message: 'Invalid credentials' }); - } - - // Update last active - await users.updateOne( - { _id: user._id }, - { $set: { lastActive: new Date().toISOString() } } - ); - - const token = jwt.sign( - { userId: user._id, email: user.email, role: user.role }, - JWT_SECRET, - { expiresIn: '24h' } - ); - - res.json({ - success: true, - token, - user: { - id: user._id, - email: user.email, - fullName: user.fullName, - role: user.role, - verified: user.verified, - profilePicture: user.profilePicture, - status: user.status - } - }); - } catch (error) { - console.error('Login error:', error); - res.status(500).json({ success: false, message: 'Login failed' }); - } -}); - -// Get User Profile -app.get('/api/profile', authenticateToken, async (req, res) => { - try { - const user = await users.findOne({ _id: req.user.userId }); - if (!user) { - return res.status(404).json({ success: false, message: 'User not found' }); - } - - res.json({ - success: true, - user: { - id: user._id, - email: user.email, - fullName: user.fullName, - studentId: user.studentId, - role: user.role, - profilePicture: user.profilePicture, - status: user.status, - verified: user.verified, - bio: user.bio, - location: user.location, - website: user.website, - joinedDate: user.joinedDate - } - }); - } catch (error) { - console.error('Profile fetch error:', error); - res.status(500).json({ success: false, message: 'Failed to fetch profile' }); - } -}); - -// Update User Profile -app.put('/api/profile', authenticateToken, upload.single('profilePicture'), async (req, res) => { - try { - const { fullName, status, bio, location, website } = req.body; - const updateData = { fullName, status, bio, location, website }; - - if (req.file) { - updateData.profilePicture = imageToDataURI(req.file.buffer, req.file.mimetype); - } - - await users.updateOne( - { _id: req.user.userId }, - { $set: updateData } - ); - - res.json({ success: true, message: 'Profile updated successfully' }); - } catch (error) { - console.error('Profile update error:', error); - res.status(500).json({ success: false, message: 'Failed to update profile' }); - } -}); - -// Create Post -app.post('/api/posts', authenticateToken, upload.single('image'), async (req, res) => { - try { - const { content, tags } = req.body; - - const newPost = { - authorId: req.user.userId, - content, - tags: tags ? tags.split(',').map(tag => tag.trim()) : [], - likes: [], - comments: [], - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString() - }; - - if (req.file) { - newPost.image = imageToDataURI(req.file.buffer, req.file.mimetype); - } - - const result = await posts.insertOne(newPost); - - // Get the author details - const author = await users.findOne({ _id: req.user.userId }); - - res.json({ - success: true, - post: { - ...newPost, - _id: result.insertedId, - author: { - fullName: author.fullName, - profilePicture: author.profilePicture, - verified: author.verified - } - } - }); - } catch (error) { - console.error('Post creation error:', error); - res.status(500).json({ success: false, message: 'Failed to create post' }); - } -}); - -// Get Posts -app.get('/api/posts', async (req, res) => { - try { - const page = parseInt(req.query.page) || 1; - const limit = parseInt(req.query.limit) || 10; - const skip = (page - 1) * limit; - - const postList = await posts.find({}, { - sort: { createdAt: -1 }, - limit, - skip - }).toArray(); - - // Get author details for each post - for (let post of postList) { - const author = await users.findOne({ _id: post.authorId }); - post.author = { - fullName: author.fullName, - profilePicture: author.profilePicture, - verified: author.verified - }; - } - - res.json({ success: true, posts: postList }); - } catch (error) { - console.error('Posts fetch error:', error); - res.status(500).json({ success: false, message: 'Failed to fetch posts' }); - } -}); - -// Like/Unlike Post -app.post('/api/posts/:postId/like', authenticateToken, async (req, res) => { - try { - const { postId } = req.params; - const userId = req.user.userId; - - const post = await posts.findOne({ _id: postId }); - if (!post) { - return res.status(404).json({ success: false, message: 'Post not found' }); - } - - const liked = post.likes.includes(userId); - - if (liked) { - await posts.updateOne( - { _id: postId }, - { $pull: { likes: userId } } - ); - } else { - await posts.updateOne( - { _id: postId }, - { $addToSet: { likes: userId } } - ); - } - - res.json({ success: true, liked: !liked }); - } catch (error) { - console.error('Like toggle error:', error); - res.status(500).json({ success: false, message: 'Failed to toggle like' }); - } -}); - -// Add Comment -app.post('/api/posts/:postId/comments', authenticateToken, async (req, res) => { - try { - const { postId } = req.params; - const { content } = req.body; - - const newComment = { - _id: Date.now().toString(), - authorId: req.user.userId, - content, - createdAt: new Date().toISOString() - }; - - await posts.updateOne( - { _id: postId }, - { $push: { comments: newComment } } - ); - - // Get author details - const author = await users.findOne({ _id: req.user.userId }); - - res.json({ - success: true, - comment: { - ...newComment, - author: { - fullName: author.fullName, - profilePicture: author.profilePicture, - verified: author.verified - } - } - }); - } catch (error) { - console.error('Comment creation error:', error); - res.status(500).json({ success: false, message: 'Failed to add comment' }); - } -}); - -// Get Comments for a Post -app.get('/api/posts/:postId/comments', async (req, res) => { - try { - const { postId } = req.params; - - const post = await posts.findOne({ _id: postId }); - if (!post) { - return res.status(404).json({ success: false, message: 'Post not found' }); - } - - // Get author details for each comment - for (let comment of post.comments) { - const author = await users.findOne({ _id: comment.authorId }); - comment.author = { - fullName: author.fullName, - profilePicture: author.profilePicture, - verified: author.verified - }; - } - - res.json({ success: true, comments: post.comments }); - } catch (error) { - console.error('Comments fetch error:', error); - res.status(500).json({ success: false, message: 'Failed to fetch comments' }); - } -}); - -// Create Event -app.post('/api/events', authenticateToken, async (req, res) => { - try { - const { title, description, date, time, location, type } = req.body; - - const newEvent = { - title, - description, - date, - time, - location, - type, - organizerId: req.user.userId, - attendees: [], - createdAt: new Date().toISOString() - }; - - await events.insertOne(newEvent); - - res.json({ success: true, event: newEvent }); - } catch (error) { - console.error('Event creation error:', error); - res.status(500).json({ success: false, message: 'Failed to create event' }); - } -}); - -// Get Events -app.get('/api/events', async (req, res) => { - try { - const eventList = await events.find({}, { - sort: { date: 1, time: 1 } - }).toArray(); - - // Get organizer details for each event - for (let event of eventList) { - const organizer = await users.findOne({ _id: event.organizerId }); - event.organizer = { - fullName: organizer.fullName, - verified: organizer.verified - }; - } - - res.json({ success: true, events: eventList }); - } catch (error) { - console.error('Events fetch error:', error); - res.status(500).json({ success: false, message: 'Failed to fetch events' }); - } -}); - -// Join Event -app.post('/api/events/:eventId/join', authenticateToken, async (req, res) => { - try { - const { eventId } = req.params; - const userId = req.user.userId; - - await events.updateOne( - { _id: eventId }, - { $addToSet: { attendees: userId } } - ); - - res.json({ success: true, message: 'Successfully joined event' }); - } catch (error) { - console.error('Join event error:', error); - res.status(500).json({ success: false, message: 'Failed to join event' }); - } -}); - -// Search -app.get('/api/search', async (req, res) => { - try { - const { q, type = 'all' } = req.query; - const results = {}; - - if (type === 'all' || type === 'posts') { - results.posts = await posts.find({ - $or: [ - { content: { $regex: q, $options: 'i' } }, - { tags: { $regex: q, $options: 'i' } } - ] - }).limit(10).toArray(); - } - - if (type === 'all' || type === 'users') { - results.users = await users.find({ - $or: [ - { fullName: { $regex: q, $options: 'i' } }, - { email: { $regex: q, $options: 'i' } } - ] - }, { - projection: { password: 0 } - }).limit(10).toArray(); - } - - if (type === 'all' || type === 'events') { - results.events = await events.find({ - $or: [ - { title: { $regex: q, $options: 'i' } }, - { description: { $regex: q, $options: 'i' } } - ] - }).limit(10).toArray(); - } - - res.json({ success: true, results }); - } catch (error) { - console.error('Search error:', error); - res.status(500).json({ success: false, message: 'Search failed' }); - } -}); - -// Health check -app.get('/api/health', (req, res) => { - res.json({ status: 'OK', timestamp: new Date().toISOString() }); -}); - -// Serve static files -app.get('/', (req, res) => { - res.sendFile(path.join(__dirname, 'landing.html')); -}); - -// Error handling middleware -app.use((error, req, res, next) => { - console.error('Error:', error); - res.status(500).json({ success: false, message: 'Internal server error' }); -}); - -// Start server -async function startServer() { - try { - await initializeCollections(); - app.listen(PORT, () => { - console.log(`Server running on port ${PORT}`); - console.log(`Access the website at: http://localhost:${PORT}`); - }); - } catch (error) { - console.error('Failed to start server:', error); - process.exit(1); - } -} - -startServer(); diff --git a/simple-integration-test.html b/simple-integration-test.html deleted file mode 100644 index c320289..0000000 --- a/simple-integration-test.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - Simple Integration Test - OpenNetwork Banner - - - -
-

🚀 Simple Integration Test

-

This page demonstrates the most basic integration of the OpenNetwork Banner.

-

Just include the script and the banner automatically appears!

-

Domain detected: Loading...

-

The banner should appear at the bottom of this page with:

-
    -
  • Current domain name highlighted in yellow
  • -
  • OpenNetwork flag logo
  • -
  • Link to OpenNetwork Collective
  • -
  • Green status indicator showing "All Systems Operational"
  • -
-

Look at the bottom of the page!

-
- - - - - - - diff --git a/start-community.bat b/start-community.bat deleted file mode 100644 index e69de29..0000000 diff --git a/start-community.sh b/start-community.sh deleted file mode 100644 index e69de29..0000000 diff --git a/styles/brand-portfolio.css b/styles/brand-portfolio.css index 8fbe209..8c0015d 100644 --- a/styles/brand-portfolio.css +++ b/styles/brand-portfolio.css @@ -36,6 +36,26 @@ body { margin-bottom: 24px; } +/* Enlarged hero logo (replaces title) */ +.hero-logo.hero-logo-large { + width: 280px; + margin-bottom: 32px; +} + +/* Join Us underlined link */ +.join-link { + display: inline-block; + color: #1d1d1f; + text-decoration: underline; + font-size: 17px; + font-weight: 500; + transition: opacity 0.2s ease; +} + +.join-link:hover { + opacity: 0.7; +} + .hero-title { font-size: 48px; font-weight: 700; @@ -109,22 +129,22 @@ body { } .affiliate-logo { - width: 80px; - height: 80px; + width: 136px; + height: 136px; object-fit: contain; margin-bottom: 16px; border-radius: 16px; } .affiliate-logo-text { - width: 80px; - height: 80px; + width: 136px; + height: 136px; display: flex; align-items: center; justify-content: center; background-color: #1d1d1f; color: #ffffff; - font-size: 24px; + font-size: 40px; font-weight: 700; border-radius: 16px; margin: 0 auto 16px; @@ -162,8 +182,8 @@ body { } .partner-logo { - width: 60px; - height: 60px; + width: 102px; + height: 102px; object-fit: contain; margin-bottom: 12px; } @@ -205,12 +225,25 @@ body { .committee-member { padding: 8px 20px; - background-color: #ffffff; - border-radius: 20px; + background-color: transparent; font-size: 15px; color: #1d1d1f; } +/* PDF Container */ +.pdf-container { + width: 100%; + max-width: 800px; + margin: 0 auto; +} + +.pdf-iframe { + width: 100%; + height: 600px; + border: 1px solid #d2d2d7; + border-radius: 8px; +} + /* Sponsors Section */ .sponsors-section { text-align: center; diff --git a/styles/calendar.css b/styles/calendar.css deleted file mode 100644 index 4525d03..0000000 --- a/styles/calendar.css +++ /dev/null @@ -1,802 +0,0 @@ -/* ===== CALENDAR SPECIFIC STYLES ===== */ - -/* Calendar Container */ -.calendar-container { - padding: 24px; - height: calc(100vh - var(--topbar-height)); - display: flex; - flex-direction: column; -} - -/* Calendar Header */ -.calendar-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 24px; - padding: 0 4px; -} - -.calendar-navigation { - display: flex; - align-items: center; - gap: 20px; -} - -.nav-btn { - background: var(--secondary-bg); - border: 1px solid var(--border-color); - color: var(--text-secondary); - width: 36px; - height: 36px; - border-radius: 8px; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s ease; -} - -.nav-btn:hover { - background: var(--hover-bg); - color: var(--text-primary); - border-color: var(--accent-primary); -} - -.current-month-year h2 { - font-size: 1.8rem; - font-weight: 600; - color: var(--text-primary); - margin: 0; - min-width: 200px; - text-align: center; -} - -.calendar-controls { - display: flex; - align-items: center; - gap: 16px; -} - -.view-selector { - display: flex; - background: var(--secondary-bg); - border: 1px solid var(--border-color); - border-radius: 8px; - overflow: hidden; -} - -.view-btn { - background: none; - border: none; - color: var(--text-secondary); - padding: 8px 16px; - cursor: pointer; - font-weight: 500; - transition: all 0.2s ease; -} - -.view-btn:hover { - background: var(--hover-bg); - color: var(--text-primary); -} - -.view-btn.active { - background: var(--accent-primary); - color: white; -} - -.new-event-btn { - background: var(--gradient-primary); - color: white; - border: none; - padding: 10px 20px; - border-radius: 8px; - cursor: pointer; - font-weight: 600; - display: flex; - align-items: center; - gap: 8px; - transition: all 0.2s ease; -} - -.new-event-btn:hover { - transform: translateY(-1px); - box-shadow: var(--shadow-md); -} - -/* Calendar Layout */ -.calendar-layout { - display: grid; - grid-template-columns: 280px 1fr; - gap: 24px; - flex: 1; - overflow: hidden; -} - -/* Calendar Sidebar */ -.calendar-sidebar { - display: flex; - flex-direction: column; - gap: 20px; - height: 100%; - overflow-y: auto; -} - -/* Mini Calendar */ -.mini-calendar { - background: var(--card-bg); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 16px; -} - -.mini-calendar-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 16px; -} - -.mini-nav-btn { - background: none; - border: none; - color: var(--text-secondary); - cursor: pointer; - padding: 4px; - border-radius: 4px; - transition: all 0.2s ease; -} - -.mini-nav-btn:hover { - background: var(--hover-bg); - color: var(--text-primary); -} - -.mini-month { - font-weight: 600; - color: var(--text-primary); - font-size: 0.9rem; -} - -.mini-calendar-grid { - display: grid; - grid-template-columns: repeat(7, 1fr); - gap: 2px; -} - -.mini-day-header { - text-align: center; - font-size: 0.75rem; - font-weight: 600; - color: var(--text-secondary); - padding: 8px 4px; -} - -.mini-day { - text-align: center; - padding: 6px 4px; - font-size: 0.8rem; - color: var(--text-primary); - cursor: pointer; - border-radius: 4px; - transition: all 0.2s ease; -} - -.mini-day:hover { - background: var(--hover-bg); -} - -.mini-day.prev-month, -.mini-day.next-month { - color: var(--text-muted); -} - -.mini-day.today { - background: var(--accent-primary); - color: white; - font-weight: 600; -} - -/* Calendar Filters */ -.calendar-filters { - background: var(--card-bg); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 16px; -} - -.calendar-filters h3 { - font-size: 1rem; - font-weight: 600; - color: var(--text-primary); - margin-bottom: 16px; -} - -.filter-list { - display: flex; - flex-direction: column; - gap: 12px; -} - -.filter-item { - display: flex; - align-items: center; - gap: 10px; - cursor: pointer; - padding: 4px; - border-radius: 4px; - transition: background 0.2s ease; -} - -.filter-item:hover { - background: var(--hover-bg); -} - -.filter-item input[type="checkbox"] { - display: none; -} - -.checkbox-custom { - width: 16px; - height: 16px; - border-radius: 4px; - border: 2px solid var(--border-color); - position: relative; - transition: all 0.2s ease; -} - -.filter-item input[type="checkbox"]:checked + .checkbox-custom { - border-color: currentColor; -} - -.filter-item input[type="checkbox"]:checked + .checkbox-custom::after { - content: '\f00c'; - font-family: 'Font Awesome 6 Free'; - font-weight: 900; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-size: 10px; - color: currentColor; -} - -.checkbox-custom.javascript { color: #f7df1e; } -.checkbox-custom.python { color: #3776ab; } -.checkbox-custom.react { color: #61dafb; } -.checkbox-custom.assignments { color: #ff6b6b; } -.checkbox-custom.personal { color: #8b5cf6; } - -.filter-item span:last-child { - font-size: 0.9rem; - color: var(--text-primary); -} - -/* Upcoming Events */ -.upcoming-events { - background: var(--card-bg); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 16px; -} - -.upcoming-events h3 { - font-size: 1rem; - font-weight: 600; - color: var(--text-primary); - margin-bottom: 16px; -} - -.event-list { - display: flex; - flex-direction: column; - gap: 12px; -} - -.upcoming-event { - display: flex; - align-items: center; - gap: 12px; - padding: 8px; - border-radius: 6px; - transition: background 0.2s ease; -} - -.upcoming-event:hover { - background: var(--hover-bg); -} - -.upcoming-event .event-time { - font-size: 0.8rem; - font-weight: 600; - color: var(--accent-primary); - min-width: 60px; -} - -.upcoming-event .event-details { - flex: 1; -} - -.upcoming-event .event-title { - font-size: 0.85rem; - font-weight: 600; - color: var(--text-primary); - margin-bottom: 2px; -} - -.upcoming-event .event-instructor { - font-size: 0.75rem; - color: var(--text-secondary); -} - -.event-indicator { - width: 8px; - height: 8px; - border-radius: 50%; -} - -.event-indicator.javascript { background: #f7df1e; } -.event-indicator.python { background: #3776ab; } -.event-indicator.react { background: #61dafb; } - -/* Calendar Main View */ -.calendar-main { - background: var(--card-bg); - border: 1px solid var(--border-color); - border-radius: 12px; - overflow: hidden; - height: 100%; -} - -.calendar-view { - display: none; - height: 100%; - flex-direction: column; -} - -.calendar-view.active { - display: flex; -} - -/* Week View */ -.week-view { - height: 100%; -} - -.week-header { - display: grid; - grid-template-columns: 80px repeat(7, 1fr); - border-bottom: 1px solid var(--border-color); - background: var(--secondary-bg); -} - -.time-column-header { - padding: 16px 8px; - border-right: 1px solid var(--border-color); -} - -.day-header { - padding: 16px 8px; - text-align: center; - border-right: 1px solid var(--border-color); - transition: background 0.2s ease; -} - -.day-header:last-child { - border-right: none; -} - -.day-header:hover { - background: var(--hover-bg); -} - -.day-name { - font-size: 0.8rem; - font-weight: 600; - color: var(--text-secondary); - text-transform: uppercase; - margin-bottom: 4px; -} - -.day-number { - font-size: 1.2rem; - font-weight: 600; - color: var(--text-primary); - width: 32px; - height: 32px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - margin: 0 auto; -} - -.day-number.today { - background: var(--accent-primary); - color: white; -} - -.week-body { - display: grid; - grid-template-columns: 80px 1fr; - height: calc(100% - 65px); - overflow-y: auto; -} - -.time-column { - border-right: 1px solid var(--border-color); - background: var(--secondary-bg); -} - -.time-slot { - height: 60px; - padding: 8px; - border-bottom: 1px solid var(--border-color); - font-size: 0.8rem; - color: var(--text-secondary); - display: flex; - align-items: flex-start; - justify-content: center; -} - -.calendar-grid { - display: grid; - grid-template-columns: repeat(7, 1fr); - position: relative; -} - -.day-column { - border-right: 1px solid var(--border-color); - position: relative; -} - -.day-column:last-child { - border-right: none; -} - -.day-column.today-column { - background: rgba(98, 100, 167, 0.02); -} - -.hour-slot { - height: 60px; - border-bottom: 1px solid var(--border-color); - position: relative; - cursor: pointer; - transition: background 0.2s ease; -} - -.hour-slot:hover { - background: var(--hover-bg); -} - -/* Event Blocks */ -.event-block { - position: absolute; - left: 2px; - right: 2px; - top: 2px; - border-radius: 6px; - padding: 8px; - cursor: pointer; - transition: all 0.2s ease; - z-index: 1; - box-shadow: var(--shadow-sm); -} - -.event-block:hover { - transform: translateY(-1px); - box-shadow: var(--shadow-md); - z-index: 2; -} - -.event-block[data-duration="1"] { - height: calc(60px - 4px); -} - -.event-block[data-duration="1.5"] { - height: calc(90px - 4px); -} - -.event-block[data-duration="2"] { - height: calc(120px - 4px); -} - -.event-block.javascript { - background: linear-gradient(135deg, #f7df1e, #ffd700); - color: #1a1a1a; -} - -.event-block.python { - background: linear-gradient(135deg, #3776ab, #4b8bbe); - color: white; -} - -.event-block.react { - background: linear-gradient(135deg, #61dafb, #21d4fd); - color: #1a1a1a; -} - -.event-block.assignments { - background: linear-gradient(135deg, #ff6b6b, #ff5252); - color: white; -} - -.event-block.personal { - background: linear-gradient(135deg, #8b5cf6, #a855f7); - color: white; -} - -.event-content { - height: 100%; - display: flex; - flex-direction: column; - justify-content: space-between; -} - -.event-title { - font-size: 0.85rem; - font-weight: 600; - margin-bottom: 2px; - line-height: 1.2; -} - -.event-time { - font-size: 0.75rem; - opacity: 0.9; - margin-bottom: 2px; -} - -.event-instructor { - font-size: 0.7rem; - opacity: 0.8; - margin-bottom: 4px; -} - -.event-actions { - margin-top: auto; -} - -.join-btn { - background: rgba(255, 255, 255, 0.2); - border: 1px solid rgba(255, 255, 255, 0.3); - color: inherit; - padding: 4px 8px; - border-radius: 4px; - cursor: pointer; - font-size: 0.7rem; - font-weight: 500; - transition: all 0.2s ease; -} - -.join-btn:hover { - background: rgba(255, 255, 255, 0.3); - border-color: rgba(255, 255, 255, 0.5); -} - -.join-btn.disabled { - background: rgba(0, 0, 0, 0.2); - border-color: rgba(0, 0, 0, 0.3); - cursor: not-allowed; -} - -/* Event Details Modal */ -.event-details-content { - display: flex; - flex-direction: column; - gap: 16px; -} - -.event-detail-item { - display: flex; - gap: 12px; - align-items: flex-start; -} - -.event-detail-item i { - color: var(--accent-primary); - width: 20px; - margin-top: 2px; -} - -.event-detail-item div { - flex: 1; -} - -.event-detail-item strong { - color: var(--text-primary); - display: block; - margin-bottom: 4px; -} - -.event-detail-item span { - color: var(--text-secondary); - line-height: 1.5; -} - -/* Responsive Design */ -@media (max-width: 1200px) { - .calendar-layout { - grid-template-columns: 240px 1fr; - } - - .current-month-year h2 { - font-size: 1.5rem; - min-width: 180px; - } -} - -@media (max-width: 1024px) { - .calendar-layout { - grid-template-columns: 1fr; - } - - .calendar-sidebar { - display: none; - } - - .calendar-header { - flex-direction: column; - gap: 16px; - align-items: stretch; - } - - .calendar-navigation { - justify-content: center; - } - - .calendar-controls { - justify-content: center; - } -} - -@media (max-width: 768px) { - .calendar-container { - padding: 16px; - } - - .calendar-header { - margin-bottom: 16px; - } - - .calendar-navigation { - gap: 12px; - } - - .current-month-year h2 { - font-size: 1.3rem; - min-width: 150px; - } - - .calendar-controls { - gap: 12px; - flex-wrap: wrap; - } - - .new-event-btn { - padding: 8px 16px; - font-size: 0.9rem; - } - - .week-header { - grid-template-columns: 60px repeat(7, 1fr); - } - - .week-body { - grid-template-columns: 60px 1fr; - } - - .time-slot { - padding: 4px; - font-size: 0.7rem; - height: 50px; - } - - .hour-slot { - height: 50px; - } - - .day-header { - padding: 8px 4px; - } - - .day-name { - font-size: 0.7rem; - } - - .day-number { - font-size: 1rem; - width: 24px; - height: 24px; - } - - .event-block { - padding: 4px; - } - - .event-title { - font-size: 0.7rem; - } - - .event-time { - font-size: 0.65rem; - } - - .event-instructor { - font-size: 0.6rem; - } - - .join-btn { - font-size: 0.6rem; - padding: 2px 6px; - } -} - -@media (max-width: 480px) { - .view-selector { - width: 100%; - } - - .view-btn { - flex: 1; - text-align: center; - } - - .calendar-navigation { - gap: 8px; - } - - .current-month-year h2 { - font-size: 1.1rem; - min-width: 120px; - } - - .week-header { - grid-template-columns: 50px repeat(7, 1fr); - } - - .week-body { - grid-template-columns: 50px 1fr; - } - - .day-header { - padding: 4px 2px; - } - - .day-name { - font-size: 0.6rem; - margin-bottom: 2px; - } - - .day-number { - font-size: 0.9rem; - width: 20px; - height: 20px; - } - - .time-slot { - font-size: 0.6rem; - padding: 2px; - } - - .event-block { - padding: 2px; - left: 1px; - right: 1px; - } - - .event-title { - font-size: 0.6rem; - margin-bottom: 1px; - } - - .event-time, - .event-instructor { - display: none; - } - - .join-btn { - display: none; - } -} diff --git a/styles/community.css b/styles/community.css deleted file mode 100644 index bc30497..0000000 --- a/styles/community.css +++ /dev/null @@ -1,2233 +0,0 @@ -/* ===== COMMUNITY SPECIFIC STYLES ===== */ - -/* Import Hack Club gradient variables */ -:root { - --gradient-electric: linear-gradient(135deg, #7C3AED 0%, #06B6D4 100%); - --gradient-sunset: linear-gradient(135deg, #F59E0B 0%, #EF4444 100%); - --gradient-cosmic: linear-gradient(135deg, #8B5CF6 0%, #EC4899 100%); - --gradient-neon: linear-gradient(135deg, #10B981 0%, #06B6D4 100%); - --font-display: 'Kanit', sans-serif; - --font-mono: 'Space Mono', monospace; -} - -/* Community Layout */ -.community-container { - padding: 24px; - font-family: 'Space Grotesk', -apple-system, sans-serif; -} - -.community-layout { - display: grid; - grid-template-columns: 250px 1fr 320px; - gap: 24px; - max-width: 1400px; - margin: 0 auto; -} - -/* Community Sidebar */ -.community-sidebar { - background: var(--card-bg); - border: 1px solid var(--border-color); - border-radius: 16px; - padding: 24px; - height: fit-content; - position: sticky; - top: calc(var(--topbar-height) + 24px); - backdrop-filter: blur(20px); - box-shadow: 0 8px 32px rgba(124, 58, 237, 0.1); -} - -.community-nav .nav-section { - margin-bottom: 32px; -} - -.community-nav .nav-section:last-child { - margin-bottom: 0; -} - -.community-nav h3 { - font-family: var(--font-display); - font-size: 1rem; - font-weight: 600; - color: var(--text-primary); - margin-bottom: 16px; - text-transform: uppercase; - letter-spacing: 1px; - background: var(--gradient-electric); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -.nav-list { - list-style: none; -} - -.nav-list li { - margin-bottom: 4px; -} - -.nav-link { - display: flex; - align-items: center; - gap: 12px; - padding: 12px 16px; - color: var(--text-secondary); - text-decoration: none; - border-radius: 12px; - font-size: 0.95rem; - font-weight: 500; - transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); - border: 1px solid transparent; -} - -.nav-link:hover { - background: var(--gradient-electric); - color: white; - text-decoration: none; - transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(124, 58, 237, 0.3); -} - -.nav-link.active { - background: var(--gradient-electric); - color: white; - transform: translateY(-1px); - box-shadow: 0 6px 20px rgba(124, 58, 237, 0.25); -} - -.nav-link i { - width: 20px; - font-size: 1rem; -} - -/* Community Main Feed */ -.community-main { - min-height: 100vh; -} - -/* Create Post Card */ -.create-post-card { - background: var(--card-bg); - border: 2px solid transparent; - border-radius: 20px; - padding: 24px; - margin-bottom: 32px; - background-image: linear-gradient(var(--card-bg), var(--card-bg)), var(--gradient-electric); - background-origin: border-box; - background-clip: content-box, border-box; - backdrop-filter: blur(20px); - transition: all 0.3s ease; -} - -.create-post-card:hover { - transform: translateY(-4px); - box-shadow: 0 20px 40px rgba(124, 58, 237, 0.15); -} - -.create-post-header { - display: flex; - align-items: center; - gap: 16px; - margin-bottom: 20px; -} - -.create-post-btn { - flex: 1; - background: var(--secondary-bg); - border: 1px solid var(--border-color); - border-radius: 25px; - padding: 16px 20px; - color: var(--text-secondary); - text-align: left; - cursor: pointer; - font-size: 1rem; - font-family: 'Space Grotesk', sans-serif; - transition: all 0.3s ease; -} - -.create-post-btn:hover { - background: var(--gradient-electric); - color: white; - border-color: transparent; - transform: translateY(-1px); - box-shadow: 0 8px 25px rgba(124, 58, 237, 0.3); -} - -.create-post-actions { - display: flex; - gap: 12px; - justify-content: space-around; -} - -.action-btn { - display: flex; - align-items: center; - gap: 8px; - background: none; - border: 2px solid var(--border-color); - color: var(--text-secondary); - padding: 12px 16px; - border-radius: 12px; - cursor: pointer; - font-size: 0.9rem; - font-weight: 500; - transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); -} - -.action-btn:hover { - background: var(--gradient-electric); - color: white; - border-color: transparent; - transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(124, 58, 237, 0.3); -} - -.action-btn.liked { - background: var(--gradient-sunset); - color: white; - border-color: transparent; -} - -.action-btn i { - font-size: 1.1rem; -} - -/* Posts Feed */ -.posts-feed { - display: flex; - flex-direction: column; - gap: 32px; -} - -/* Post Styles */ -.post { - background: var(--card-bg); - border: 1px solid var(--border-color); - border-radius: 20px; - padding: 28px; - transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); - backdrop-filter: blur(20px); - position: relative; - overflow: hidden; -} - -.post::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 3px; - background: var(--gradient-electric); - opacity: 0; - transition: opacity 0.3s ease; -} - -.post:hover { - transform: translateY(-6px); - box-shadow: 0 20px 60px rgba(124, 58, 237, 0.15); -} - -.post:hover::before { - opacity: 1; -} - -.featured-post { - border: 2px solid transparent; - background-image: linear-gradient(var(--card-bg), var(--card-bg)), var(--gradient-electric); - background-origin: border-box; - background-clip: content-box, border-box; -} - -.featured-post::before { - opacity: 1; - height: 4px; - background: var(--gradient-cosmic); -} - -/* Right Sidebar Enhancements */ -.community-right-sidebar { - display: flex; - flex-direction: column; - gap: 24px; - height: fit-content; - position: sticky; - top: calc(var(--topbar-height) + 24px); -} - -.sidebar-card { - background: var(--card-bg); - border: 1px solid var(--border-color); - border-radius: 20px; - padding: 24px; - backdrop-filter: blur(20px); - transition: all 0.3s ease; -} - -.sidebar-card:hover { - transform: translateY(-2px); - box-shadow: 0 12px 32px rgba(124, 58, 237, 0.1); -} - -.card-title { - font-family: var(--font-display); - font-size: 1.2rem; - font-weight: 600; - color: var(--text-primary); - margin-bottom: 20px; - display: flex; - align-items: center; - gap: 10px; -} - -.gradient-icon { - background: var(--gradient-electric); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -/* Live Chat Styles */ -.live-chat-card { - border: 2px solid transparent; - background-image: linear-gradient(var(--card-bg), var(--card-bg)), var(--gradient-neon); - background-origin: border-box; - background-clip: content-box, border-box; -} - -.card-header { - display: flex; - justify-content: between; - align-items: center; - margin-bottom: 16px; -} - -.online-count { - background: var(--gradient-neon); - color: white; - padding: 4px 8px; - border-radius: 12px; - font-size: 0.75rem; - font-weight: 600; - margin-left: auto; -} - -.chat-container { - height: 300px; - display: flex; - flex-direction: column; -} - -.chat-messages { - flex: 1; - overflow-y: auto; - padding: 12px 0; - scroll-behavior: smooth; -} - -.chat-message { - display: flex; - gap: 8px; - margin-bottom: 12px; - opacity: 0; - animation: fadeInUp 0.3s ease forwards; -} - -.chat-message.system { - justify-content: center; - color: var(--text-secondary); - font-size: 0.85rem; - font-style: italic; -} - -.message-avatar { - width: 32px; - height: 32px; - border-radius: 50%; - background: var(--gradient-electric); - color: white; - display: flex; - align-items: center; - justify-content: center; - font-size: 0.8rem; - font-weight: 600; - flex-shrink: 0; -} - -.message-content { - flex: 1; -} - -.message-author { - font-weight: 600; - font-size: 0.85rem; - color: var(--text-primary); -} - -.message-text { - font-size: 0.9rem; - color: var(--text-secondary); - margin: 2px 0; -} - -.message-time { - font-size: 0.75rem; - color: var(--text-muted); -} - -.chat-input-area { - border-top: 1px solid var(--border-color); - padding-top: 12px; -} - -.chat-emoji-picker { - display: none; - gap: 4px; - margin-bottom: 8px; - padding: 8px; - background: var(--secondary-bg); - border-radius: 12px; -} - -.emoji-btn { - background: none; - border: none; - font-size: 1.2rem; - cursor: pointer; - padding: 4px; - border-radius: 6px; - transition: background 0.2s ease; -} - -.emoji-btn:hover { - background: var(--hover-bg); -} - -.chat-input-group { - display: flex; - gap: 8px; - align-items: center; -} - -.chat-input { - flex: 1; - background: var(--secondary-bg); - border: 1px solid var(--border-color); - border-radius: 20px; - padding: 8px 16px; - font-size: 0.9rem; - color: var(--text-primary); -} - -.chat-send-btn { - width: 36px; - height: 36px; - background: var(--gradient-electric); - border: none; - border-radius: 50%; - color: white; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.3s ease; -} - -.chat-send-btn:hover { - transform: scale(1.1); - box-shadow: 0 4px 12px rgba(124, 58, 237, 0.4); -} - -.chat-send-btn:disabled { - background: var(--border-color); - cursor: not-allowed; - transform: none; -} - -/* Trending Topics Enhanced */ -.trending-item { - display: flex; - align-items: center; - gap: 12px; - padding: 12px; - border-radius: 12px; - transition: all 0.3s ease; - cursor: pointer; - border: 1px solid transparent; -} - -.trending-item:hover { - background: var(--gradient-electric); - color: white; - transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(124, 58, 237, 0.3); -} - -.trending-rank { - font-weight: 700; - background: var(--gradient-electric); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - font-size: 1rem; - min-width: 24px; -} - -.trending-item:hover .trending-rank { - -webkit-text-fill-color: white; -} - -.trending-sparkline { - display: flex; - align-items: end; - gap: 2px; - height: 20px; -} - -.sparkline-bar { - width: 3px; - background: var(--gradient-electric); - border-radius: 2px; - transition: all 0.3s ease; -} - -.trending-item:hover .sparkline-bar { - background: white; -} - -.trend-up { - color: #10B981; -} - -.trend-down { - color: #EF4444; -} - -/* Daily Progress Tracker */ -.progress-card { - border: 2px solid transparent; - background-image: linear-gradient(var(--card-bg), var(--card-bg)), var(--gradient-cosmic); - background-origin: border-box; - background-clip: content-box, border-box; -} - -.daily-progress { - margin-bottom: 20px; -} - -.progress-item { - display: flex; - align-items: center; - gap: 12px; - padding: 12px; - margin-bottom: 8px; - border-radius: 12px; - transition: all 0.3s ease; -} - -.progress-item.completed { - background: rgba(16, 185, 129, 0.1); -} - -.progress-item.active { - background: var(--gradient-electric); - color: white; - box-shadow: 0 4px 12px rgba(124, 58, 237, 0.3); -} - -.progress-icon { - width: 36px; - height: 36px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-size: 0.9rem; -} - -.progress-item.completed .progress-icon { - background: #10B981; - color: white; -} - -.progress-item.active .progress-icon { - background: white; - color: var(--accent-primary); -} - -.progress-item:not(.completed):not(.active) .progress-icon { - background: var(--border-color); - color: var(--text-secondary); -} - -.daily-streak { - padding: 16px; - background: var(--secondary-bg); - border-radius: 12px; - text-align: center; -} - -.streak-number { - font-size: 2rem; - font-weight: 700; - background: var(--gradient-cosmic); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -.streak-calendar { - display: flex; - gap: 4px; - justify-content: center; - margin-top: 8px; -} - -.streak-day { - width: 12px; - height: 12px; - border-radius: 50%; - background: var(--border-color); -} - -.streak-day.completed { - background: var(--gradient-neon); -} - -.streak-day.today { - background: var(--gradient-cosmic); - box-shadow: 0 0 8px rgba(139, 92, 246, 0.5); -} - -/* Active Users Enhanced */ -.pulse-indicator { - width: 8px; - height: 8px; - background: #00ff88; - border-radius: 50%; - animation: pulse 2s infinite; -} - -.active-user { - display: flex; - align-items: center; - gap: 12px; - padding: 12px; - border-radius: 12px; - transition: all 0.3s ease; - cursor: pointer; -} - -.active-user:hover { - background: var(--gradient-electric); - color: white; - transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(124, 58, 237, 0.3); -} - -.user-avatar.online::after, -.user-avatar.available::after { - content: ''; - position: absolute; - bottom: 0; - right: 0; - width: 12px; - height: 12px; - border: 3px solid var(--card-bg); - border-radius: 50%; -} - -.user-avatar.online::after { - background: #00ff88; -} - -.user-avatar.available::after { - background: #F59E0B; -} - -.user-status i { - margin-right: 4px; -} - -.quick-message-btn { - background: var(--gradient-electric); - border: none; - color: white; - width: 32px; - height: 32px; - border-radius: 50%; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.3s ease; - opacity: 0; -} - -.active-user:hover .quick-message-btn { - opacity: 1; - transform: scale(1.1); -} - -/* Study Groups Enhanced */ -.study-group { - display: flex; - align-items: center; - gap: 12px; - padding: 12px; - border-radius: 12px; - transition: all 0.3s ease; - cursor: pointer; - border: 1px solid transparent; -} - -.study-group:hover { - background: var(--gradient-electric); - color: white; - transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(124, 58, 237, 0.3); -} - -.group-avatar { - width: 40px; - height: 40px; - border-radius: 12px; - background: var(--gradient-electric); - display: flex; - align-items: center; - justify-content: center; - color: white; - font-size: 1.2rem; -} - -.activity-indicator { - display: flex; - align-items: center; - gap: 4px; - font-size: 0.8rem; -} - -.activity-dot { - width: 8px; - height: 8px; - border-radius: 50%; - background: var(--gradient-neon); -} - -.activity-dot.new-messages { - animation: pulse 2s infinite; -} - -.gradient-btn { - background: var(--gradient-electric); - color: white; - border: none; - padding: 12px 16px; - border-radius: 12px; - cursor: pointer; - font-weight: 600; - width: 100%; - transition: all 0.3s ease; - display: flex; - align-items: center; - justify-content: center; - gap: 8px; -} - -.gradient-btn:hover { - transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(124, 58, 237, 0.4); -} - -/* Enhanced Modal Styles */ -.create-post-modal { - width: 95%; - max-width: 800px; - max-height: 90vh; -} - -.modal-title-section { - display: flex; - align-items: center; - gap: 12px; -} - -.modal-icon { - width: 40px; - height: 40px; - background: var(--gradient-electric); - border-radius: 12px; - display: flex; - align-items: center; - justify-content: center; - color: white; -} - -.form-row { - display: grid; - grid-template-columns: 2fr 1fr; - gap: 16px; -} - -.form-group label { - display: flex; - align-items: center; - gap: 8px; - font-weight: 600; - color: var(--text-primary); - font-size: 0.9rem; - margin-bottom: 8px; -} - -.optional { - font-weight: 400; - color: var(--text-secondary); - font-size: 0.8rem; -} - -.content-editor { - position: relative; -} - -.editor-toolbar { - display: flex; - gap: 4px; - padding: 8px; - background: var(--secondary-bg); - border: 1px solid var(--border-color); - border-radius: 8px 8px 0 0; - border-bottom: none; -} - -.toolbar-btn { - background: none; - border: none; - color: var(--text-secondary); - padding: 6px; - border-radius: 4px; - cursor: pointer; - transition: all 0.2s ease; -} - -.toolbar-btn:hover { - background: var(--gradient-electric); - color: white; -} - -.toolbar-divider { - width: 1px; - background: var(--border-color); - margin: 4px 4px; -} - -.content-textarea { - border-radius: 0 0 8px 8px; - border-top: none; - resize: vertical; -} - -.character-counter { - position: absolute; - bottom: 8px; - right: 12px; - font-size: 0.75rem; - color: var(--text-muted); -} - -.tags-input-container { - position: relative; -} - -.tags-display { - display: flex; - flex-wrap: wrap; - gap: 6px; - margin-bottom: 8px; - min-height: 20px; -} - -.tag-item { - background: var(--gradient-electric); - color: white; - padding: 4px 8px; - border-radius: 12px; - font-size: 0.8rem; - display: flex; - align-items: center; - gap: 4px; -} - -.tag-remove { - cursor: pointer; - opacity: 0.7; -} - -.tag-remove:hover { - opacity: 1; -} - -.suggested-tags { - display: flex; - flex-wrap: wrap; - gap: 6px; - margin-top: 8px; -} - -.tag-suggestion { - background: var(--secondary-bg); - color: var(--text-secondary); - padding: 4px 8px; - border-radius: 12px; - font-size: 0.8rem; - cursor: pointer; - transition: all 0.2s ease; -} - -.tag-suggestion:hover { - background: var(--gradient-electric); - color: white; -} - -.post-options-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 12px; - margin-top: 20px; -} - -.option-card { - display: flex; - align-items: center; - gap: 12px; - padding: 16px; - border: 2px solid var(--border-color); - border-radius: 12px; - cursor: pointer; - transition: all 0.3s ease; -} - -.option-card:hover { - border-color: var(--accent-primary); - background: var(--gradient-electric); - color: white; - transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(124, 58, 237, 0.3); -} - -.option-icon { - width: 40px; - height: 40px; - background: var(--secondary-bg); - border-radius: 10px; - display: flex; - align-items: center; - justify-content: center; - font-size: 1.2rem; - color: var(--accent-primary); - transition: all 0.3s ease; -} - -.option-card:hover .option-icon { - background: white; - color: var(--accent-primary); -} - -.option-info { - flex: 1; -} - -.option-title { - font-weight: 600; - margin-bottom: 2px; -} - -.option-subtitle { - font-size: 0.8rem; - opacity: 0.7; -} - -.option-btn { - width: 32px; - height: 32px; - background: var(--gradient-electric); - border: none; - border-radius: 50%; - color: white; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.3s ease; -} - -.option-btn:hover { - transform: scale(1.1); -} - -.modal-footer { - display: flex; - justify-content: space-between; - align-items: center; - padding: 20px 24px; - border-top: 1px solid var(--border-color); -} - -.footer-left, .footer-right { - display: flex; - gap: 12px; -} - -.publish-btn { - background: var(--gradient-electric); - color: white; - border: none; - padding: 12px 24px; - border-radius: 12px; - cursor: pointer; - font-weight: 600; - display: flex; - align-items: center; - gap: 8px; - transition: all 0.3s ease; -} - -.publish-btn:hover { - transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(124, 58, 237, 0.4); -} - -/* Animations */ -@keyframes fadeInUp { - from { - opacity: 0; - transform: translateY(10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -@keyframes pulse { - 0%, 100% { transform: scale(1); opacity: 1; } - 50% { transform: scale(1.1); opacity: 0.7; } -} - -/* Chat Modal Styles */ -.chat-modal .modal-content { - width: 95vw; - height: 90vh; - max-width: none; - max-height: none; - border-radius: 20px; -} - -.chat-modal-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 20px 24px; - border-bottom: 1px solid var(--border-color); - background: var(--gradient-electric); - color: white; - border-radius: 20px 20px 0 0; -} - -.chat-modal-body { - display: flex; - height: calc(100% - 80px); -} - -.chat-sidebar { - width: 250px; - background: var(--secondary-bg); - border-right: 1px solid var(--border-color); - padding: 20px; -} - -.chat-main-area { - flex: 1; - display: flex; - flex-direction: column; -} - -.chat-tabs { - display: flex; - flex-direction: column; - gap: 4px; - margin-bottom: 20px; -} - -.chat-tab { - background: none; - border: none; - text-align: left; - padding: 8px 12px; - border-radius: 8px; - cursor: pointer; - font-family: var(--font-mono); - color: var(--text-secondary); - transition: all 0.2s ease; -} - -.chat-tab:hover, .chat-tab.active { - background: var(--gradient-electric); - color: white; -} - -/* Responsive Design */ -@media (max-width: 1200px) { - .community-layout { - grid-template-columns: 220px 1fr 280px; - gap: 20px; - } -} - -@media (max-width: 1024px) { - .community-layout { - grid-template-columns: 1fr 280px; - } - - .community-sidebar { - display: none; - } -} - -@media (max-width: 768px) { - .community-container { - padding: 16px; - } - - .community-layout { - grid-template-columns: 1fr; - gap: 16px; - } - - .community-right-sidebar { - display: none; - } - - .create-post-actions { - flex-wrap: wrap; - } - - .post { - padding: 20px; - } - - .form-row { - grid-template-columns: 1fr; - } - - .post-options-grid { - grid-template-columns: 1fr; - } -} - -.post-header { - display: flex; - justify-content: space-between; - align-items: flex-start; - margin-bottom: 16px; -} - -.post-author { - display: flex; - align-items: center; - gap: 12px; -} - -.author-info { - display: flex; - flex-direction: column; -} - -.author-name { - font-size: 0.95rem; - font-weight: 600; - color: var(--text-primary); - display: flex; - align-items: center; - gap: 6px; -} - -.verified-badge { - color: var(--accent-primary); - font-size: 0.8rem; -} - -.post-meta { - display: flex; - gap: 12px; - font-size: 0.8rem; - color: var(--text-secondary); -} - -.post-category { - background: rgba(98, 100, 167, 0.2); - color: var(--accent-primary); - padding: 2px 8px; - border-radius: 10px; - font-weight: 500; -} - -.post-menu { - background: none; - border: none; - color: var(--text-secondary); - cursor: pointer; - padding: 4px 8px; - border-radius: 4px; - transition: all 0.2s ease; -} - -.post-menu:hover { - background: var(--hover-bg); - color: var(--text-primary); -} - -/* Post Content */ -.post-content h3, -.post-content h4 { - color: var(--text-primary); - margin-bottom: 12px; - font-weight: 600; -} - -.post-content h3 { - font-size: 1.3rem; -} - -.post-content h4 { - font-size: 1.1rem; -} - -.post-content p { - color: var(--text-primary); - line-height: 1.6; - margin-bottom: 16px; -} - -.post-content p:last-child { - margin-bottom: 0; -} - -/* Post Highlights */ -.post-highlights { - background: var(--secondary-bg); - padding: 16px; - border-radius: 8px; - margin: 16px 0; -} - -.highlight-item { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 8px; - color: var(--text-primary); - font-size: 0.9rem; -} - -.highlight-item:last-child { - margin-bottom: 0; -} - -.highlight-item i { - color: var(--accent-primary); - font-size: 0.8rem; -} - -/* Code Styles */ -.code-preview, -.code-block { - background: var(--primary-bg); - border: 1px solid var(--border-color); - border-radius: 8px; - margin: 16px 0; - overflow-x: auto; -} - -.code-title { - background: var(--secondary-bg); - padding: 8px 16px; - font-size: 0.85rem; - font-weight: 600; - color: var(--text-primary); - border-bottom: 1px solid var(--border-color); -} - -.code-preview pre, -.code-block pre { - margin: 0; - padding: 16px; - color: var(--text-primary); - font-family: var(--font-mono); - font-size: 0.85rem; - line-height: 1.5; -} - -.code-comparison { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 16px; - margin: 16px 0; -} - -/* Post Image */ -.post-image { - margin: 16px 0; - border-radius: 8px; - overflow: hidden; -} - -.post-image img { - width: 100%; - height: auto; - display: block; -} - -/* Post Tags */ -.post-tags { - display: flex; - flex-wrap: wrap; - gap: 8px; - margin: 16px 0; -} - -.tag { - background: rgba(98, 100, 167, 0.1); - color: var(--accent-primary); - padding: 4px 8px; - border-radius: 12px; - font-size: 0.75rem; - font-weight: 500; - text-decoration: none; - transition: all 0.2s ease; -} - -.tag:hover { - background: rgba(98, 100, 167, 0.2); - color: var(--accent-primary); - text-decoration: none; -} - -/* Content Lists */ -.content-list { - margin: 16px 0; - padding-left: 20px; - color: var(--text-primary); -} - -.content-list li { - margin-bottom: 8px; - line-height: 1.5; -} - -/* Event Details */ -.event-details { - background: var(--secondary-bg); - padding: 16px; - border-radius: 8px; - margin: 16px 0; -} - -.event-info { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 8px; - color: var(--text-primary); - font-size: 0.9rem; -} - -.event-info:last-child { - margin-bottom: 0; -} - -.event-info i { - color: var(--accent-primary); - width: 16px; -} - -.join-event-btn { - background: var(--gradient-primary); - color: white; - border: none; - padding: 12px 20px; - border-radius: 8px; - cursor: pointer; - font-weight: 600; - display: flex; - align-items: center; - gap: 8px; - margin-top: 16px; - transition: all 0.2s ease; -} - -.join-event-btn:hover { - transform: translateY(-1px); - box-shadow: var(--shadow-md); -} - -/* Post Footer */ -.post-footer { - margin-top: 20px; - padding-top: 16px; - border-top: 1px solid var(--border-color); -} - -.post-stats { - display: flex; - gap: 20px; - margin-bottom: 12px; - font-size: 0.85rem; - color: var(--text-secondary); -} - -.stat-item { - display: flex; - align-items: center; - gap: 4px; -} - -.post-actions { - display: flex; - gap: 8px; -} - -/* Comments Section */ -.comments-section { - margin-top: 16px; - padding-top: 16px; - border-top: 1px solid var(--border-color); -} - -.comment { - margin-bottom: 16px; - padding: 12px; - background: var(--secondary-bg); - border-radius: 8px; -} - -.comment-author { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 8px; -} - -.comment-info { - display: flex; - flex-direction: column; -} - -.comment-author-name { - font-size: 0.85rem; - font-weight: 600; - color: var(--text-primary); -} - -.comment-time { - font-size: 0.75rem; - color: var(--text-secondary); -} - -.comment-content p { - font-size: 0.9rem; - color: var(--text-primary); - margin-bottom: 8px; -} - -.comment-actions { - display: flex; - gap: 12px; - align-items: center; -} - -.comment-action { - background: none; - border: none; - color: var(--text-secondary); - cursor: pointer; - font-size: 0.8rem; - padding: 4px 8px; - border-radius: 4px; - display: flex; - align-items: center; - gap: 4px; - transition: all 0.2s ease; -} - -.comment-action:hover { - background: var(--hover-bg); - color: var(--text-primary); -} - -.add-comment { - display: flex; - align-items: center; - gap: 12px; - margin-top: 16px; -} - -.comment-input { - flex: 1; - background: var(--primary-bg); - border: 1px solid var(--border-color); - border-radius: 20px; - padding: 8px 16px; - color: var(--text-primary); - font-size: 0.9rem; -} - -.comment-input:focus { - outline: none; - border-color: var(--accent-primary); -} - -.send-comment { - background: var(--accent-primary); - border: none; - color: white; - width: 36px; - height: 36px; - border-radius: 50%; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s ease; -} - -.send-comment:hover { - background: var(--accent-secondary); -} - -.send-comment:disabled { - background: var(--hover-bg); - color: var(--text-secondary); - cursor: not-allowed; -} - -/* Load More */ -.load-more { - text-align: center; - margin-top: 32px; -} - -.load-more-btn { - background: var(--secondary-bg); - border: 1px solid var(--border-color); - color: var(--text-primary); - padding: 12px 24px; - border-radius: 8px; - cursor: pointer; - font-weight: 500; - display: inline-flex; - align-items: center; - gap: 8px; - transition: all 0.2s ease; -} - -.load-more-btn:hover { - background: var(--hover-bg); - border-color: var(--accent-primary); -} - -/* Right Sidebar */ -.community-right-sidebar { - display: flex; - flex-direction: column; - gap: 20px; - height: fit-content; - position: sticky; - top: calc(var(--topbar-height) + 24px); -} - -.sidebar-card { - background: var(--card-bg); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 20px; -} - -.card-title { - font-size: 1.1rem; - font-weight: 600; - color: var(--text-primary); - margin-bottom: 16px; -} - -/* Trending Topics */ -.trending-topics { - display: flex; - flex-direction: column; - gap: 12px; -} - -.trending-item { - display: flex; - align-items: center; - gap: 12px; - padding: 8px; - border-radius: 6px; - transition: background 0.2s ease; -} - -.trending-item:hover { - background: var(--hover-bg); -} - -.trending-rank { - font-weight: 700; - color: var(--accent-primary); - font-size: 0.9rem; - min-width: 20px; -} - -.trending-info { - flex: 1; -} - -.trending-topic { - font-weight: 600; - color: var(--text-primary); - font-size: 0.9rem; -} - -.trending-posts { - font-size: 0.8rem; - color: var(--text-secondary); -} - -/* Active Users */ -.active-users { - display: flex; - flex-direction: column; - gap: 12px; -} - -.active-user { - display: flex; - align-items: center; - gap: 10px; - padding: 8px; - border-radius: 6px; - transition: background 0.2s ease; -} - -.active-user:hover { - background: var(--hover-bg); -} - -.user-avatar.online { - position: relative; -} - -.user-avatar.online::after { - content: ''; - position: absolute; - bottom: 0; - right: 0; - width: 8px; - height: 8px; - background: #00ff88; - border: 2px solid var(--card-bg); - border-radius: 50%; -} - -.active-user .user-info { - flex: 1; -} - -.active-user .user-name { - font-size: 0.85rem; - font-weight: 600; - color: var(--text-primary); -} - -.user-status { - font-size: 0.75rem; - color: var(--text-secondary); -} - -/* Study Groups */ -.study-groups { - display: flex; - flex-direction: column; - gap: 12px; - margin-bottom: 16px; -} - -.study-group { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px; - border-radius: 6px; - transition: background 0.2s ease; -} - -.study-group:hover { - background: var(--hover-bg); -} - -.group-name { - font-weight: 600; - color: var(--text-primary); - font-size: 0.85rem; -} - -.group-members { - font-size: 0.75rem; - color: var(--text-secondary); -} - -.group-activity { - font-size: 0.75rem; - color: var(--accent-primary); - font-weight: 500; -} - -.see-all-btn { - background: none; - border: 1px solid var(--border-color); - color: var(--text-secondary); - padding: 8px 16px; - border-radius: 6px; - cursor: pointer; - font-size: 0.85rem; - width: 100%; - transition: all 0.2s ease; -} - -.see-all-btn:hover { - background: var(--hover-bg); - border-color: var(--accent-primary); - color: var(--text-primary); -} - -/* Modal Styles */ -.modal { - display: none; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.7); - z-index: 2000; - align-items: center; - justify-content: center; -} - -.modal.show { - display: flex; -} - -.modal-content { - background: var(--card-bg); - border-radius: 12px; - width: 90%; - max-width: 600px; - max-height: 90vh; - overflow-y: auto; - box-shadow: var(--shadow-lg); -} - -.modal-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 20px 24px; - border-bottom: 1px solid var(--border-color); -} - -.modal-header h3 { - color: var(--text-primary); - font-weight: 600; - margin: 0; -} - -.modal-close { - background: none; - border: none; - color: var(--text-secondary); - cursor: pointer; - padding: 4px; - border-radius: 4px; - transition: all 0.2s ease; -} - -.modal-close:hover { - background: var(--hover-bg); - color: var(--text-primary); -} - -.modal-body { - padding: 24px; -} - -.modal-footer { - display: flex; - justify-content: flex-end; - gap: 12px; - padding: 20px 24px; - border-top: 1px solid var(--border-color); -} - -/* Form Styles */ -.post-form { - display: flex; - flex-direction: column; - gap: 20px; -} - -.form-group { - display: flex; - flex-direction: column; - gap: 8px; -} - -.form-group label { - font-weight: 600; - color: var(--text-primary); - font-size: 0.9rem; -} - -.form-control { - background: var(--secondary-bg); - border: 1px solid var(--border-color); - border-radius: 6px; - padding: 10px 12px; - color: var(--text-primary); - font-size: 0.9rem; - transition: all 0.2s ease; -} - -.form-control:focus { - outline: none; - border-color: var(--accent-primary); - box-shadow: 0 0 0 3px rgba(98, 100, 167, 0.1); -} - -.form-control::placeholder { - color: var(--text-muted); -} - -textarea.form-control { - resize: vertical; - min-height: 120px; - font-family: var(--font-primary); - line-height: 1.5; -} - -.post-options { - display: flex; - gap: 12px; - flex-wrap: wrap; -} - -.option-btn { - display: flex; - align-items: center; - gap: 6px; - background: var(--secondary-bg); - border: 1px solid var(--border-color); - color: var(--text-secondary); - padding: 8px 12px; - border-radius: 6px; - cursor: pointer; - font-size: 0.85rem; - transition: all 0.2s ease; -} - -.option-btn:hover { - background: var(--hover-bg); - border-color: var(--accent-primary); - color: var(--text-primary); -} - -/* Responsive Design */ -@media (max-width: 1200px) { - .community-layout { - grid-template-columns: 200px 1fr 240px; - gap: 20px; - } -} - -@media (max-width: 1024px) { - .community-layout { - grid-template-columns: 1fr 260px; - } - - .community-sidebar { - display: none; - } -} - -@media (max-width: 768px) { - .community-container { - padding: 16px; - } - - .community-layout { - grid-template-columns: 1fr; - gap: 16px; - } - - .community-right-sidebar { - display: none; - } - - .create-post-actions { - flex-wrap: wrap; - } - - .post { - padding: 16px; - } - - .code-comparison { - grid-template-columns: 1fr; - } - - .post-actions { - flex-wrap: wrap; - } - - .modal-content { - width: 95%; - margin: 20px; - } - - .modal-body { - padding: 16px; - } - - .post-options { - justify-content: center; - } -} - -@media (max-width: 480px) { - .create-post-actions { - grid-template-columns: 1fr 1fr; - display: grid; - gap: 8px; - } - - .post-stats { - flex-wrap: wrap; - } - - .trending-item, - .active-user, - .study-group { - flex-direction: column; - align-items: flex-start; - gap: 4px; - } -} - -/* Message Form and Auth Prompt Styles */ -.message-form { - display: flex; - gap: 0.5rem; - padding: 1rem; - border-top: 1px solid var(--border-color); - background: rgba(17, 24, 39, 0.3); -} - -.message-form input { - flex: 1; - padding: 0.75rem; - border: 1px solid var(--border-color); - border-radius: 8px; - background: rgba(31, 41, 55, 0.5); - color: var(--text-color); - font-size: 0.9rem; -} - -.message-form input:disabled { - opacity: 0.6; - cursor: not-allowed; - background: rgba(31, 41, 55, 0.3); -} - -.message-form input:focus { - outline: none; - border-color: var(--primary-color); - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -.message-form .btn-primary { - padding: 0.75rem 1.5rem; - white-space: nowrap; -} - -.message-form .btn-primary:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -/* Auth Prompt Styles */ -.auth-prompt { - background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(147, 51, 234, 0.1) 100%); - border: 2px solid rgba(59, 130, 246, 0.2); - border-radius: 12px; - padding: 1.5rem; - margin-bottom: 1.5rem; - text-align: center; -} - -.auth-prompt-content { - display: flex; - flex-direction: column; - align-items: center; - gap: 1rem; -} - -.auth-prompt-icon { - width: 3rem; - height: 3rem; - background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-size: 1.5rem; - color: white; - margin-bottom: 0.5rem; -} - -.auth-prompt h3 { - margin: 0; - color: var(--text-color); - font-size: 1.25rem; - font-weight: 600; -} - -.auth-prompt p { - margin: 0; - color: var(--text-secondary); - font-size: 0.9rem; - line-height: 1.5; -} - -.auth-prompt-buttons { - display: flex; - gap: 0.75rem; - margin-top: 0.5rem; -} - -.auth-prompt-buttons .btn-primary, -.auth-prompt-buttons .btn-secondary { - padding: 0.6rem 1.2rem; - font-size: 0.85rem; - border-radius: 8px; -} - -.auth-prompt-features { - display: flex; - flex-wrap: wrap; - gap: 0.75rem; - justify-content: center; - margin-top: 1rem; - padding-top: 1rem; - border-top: 1px solid rgba(255, 255, 255, 0.1); -} - -.feature-item { - display: flex; - align-items: center; - gap: 0.5rem; - font-size: 0.8rem; - color: var(--text-secondary); -} - -.feature-item i { - color: var(--primary-color); - font-size: 0.9rem; -} - -/* Auth Prompt Modal */ -.auth-prompt-modal { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.7); - backdrop-filter: blur(4px); - display: flex; - align-items: center; - justify-content: center; - z-index: 10000; - padding: 1rem; - animation: fadeIn 0.3s ease; -} - -.auth-prompt-modal-content { - background: linear-gradient(135deg, rgba(17, 24, 39, 0.95) 0%, rgba(31, 41, 55, 0.95) 100%); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 16px; - padding: 0; - max-width: 400px; - width: 100%; - overflow: hidden; - animation: slideUp 0.3s ease; -} - -.auth-prompt-header { - background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); - padding: 1.5rem; - text-align: center; - position: relative; -} - -.auth-prompt-header .auth-prompt-icon { - width: 3rem; - height: 3rem; - background: rgba(255, 255, 255, 0.2); - border-radius: 50%; - display: inline-flex; - align-items: center; - justify-content: center; - font-size: 1.5rem; - color: white; - margin-bottom: 0.75rem; -} - -.auth-prompt-header h3 { - margin: 0; - color: white; - font-size: 1.25rem; - font-weight: 600; -} - -.close-auth-prompt { - position: absolute; - top: 1rem; - right: 1rem; - background: rgba(255, 255, 255, 0.1); - border: none; - color: white; - width: 2rem; - height: 2rem; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - font-size: 0.9rem; - transition: background 0.2s; -} - -.close-auth-prompt:hover { - background: rgba(255, 255, 255, 0.2); -} - -.auth-prompt-body { - padding: 1.5rem; - text-align: center; -} - -.auth-prompt-body p { - margin: 0 0 1rem 0; - color: var(--text-color); - line-height: 1.5; -} - -.auth-prompt-benefits { - display: flex; - flex-direction: column; - gap: 0.5rem; - margin: 1rem 0; -} - -.benefit-item { - display: flex; - align-items: center; - gap: 0.75rem; - font-size: 0.9rem; - color: var(--text-secondary); -} - -.benefit-item i { - color: #10b981; - font-size: 1rem; -} - -.auth-prompt-footer { - padding: 1.5rem; - background: rgba(17, 24, 39, 0.5); - border-top: 1px solid rgba(255, 255, 255, 0.1); - display: flex; - gap: 0.75rem; -} - -.auth-prompt-footer .btn-primary, -.auth-prompt-footer .btn-secondary { - flex: 1; - padding: 0.75rem; - font-size: 0.9rem; - text-align: center; - border-radius: 8px; -} - -/* Sample message styling */ -.sample-message { - opacity: 0.8; -} - -.sample-message::after { - content: ' (sample)'; - font-size: 0.7rem; - color: var(--text-muted); - margin-left: 0.5rem; -} - -/* Offline message */ -.offline-message { - text-align: center; - padding: 3rem 2rem; - background: rgba(17, 24, 39, 0.3); - border-radius: 12px; - border: 1px dashed var(--border-color); -} - -.offline-icon { - font-size: 3rem; - color: var(--text-muted); - margin-bottom: 1rem; -} - -.offline-message h3 { - color: var(--text-color); - margin-bottom: 0.5rem; -} - -.offline-message p { - color: var(--text-secondary); - line-height: 1.6; - margin-bottom: 1.5rem; -} - -@keyframes fadeIn { - from { opacity: 0; } - to { opacity: 1; } -} - -@keyframes slideUp { - from { - transform: translateY(2rem); - opacity: 0; - } - to { - transform: translateY(0); - opacity: 1; - } -} diff --git a/styles/components.css b/styles/components.css deleted file mode 100644 index a1c7cc9..0000000 --- a/styles/components.css +++ /dev/null @@ -1,636 +0,0 @@ -/* About Section */ -.about { - background: linear-gradient(135deg, var(--bg-secondary) 0%, var(--bg-tertiary) 100%); - position: relative; -} - -.about::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: url('data:image/svg+xml,') repeat; - background-size: 50px 50px; -} - -.about-content { - display: grid; - grid-template-columns: 1fr; - gap: var(--space-3xl); - align-items: center; - position: relative; - z-index: 1; -} - -.about-stats { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - gap: var(--space-xl); - margin-top: var(--space-2xl); -} - -.stat-item { - text-align: center; - padding: var(--space-lg); - background: rgba(255, 255, 255, 0.05); - border-radius: var(--radius-xl); - border: 1px solid rgba(255, 255, 255, 0.1); - backdrop-filter: blur(10px); - transition: all var(--transition-base); -} - -.stat-item:hover { - transform: translateY(-5px); - background: rgba(255, 255, 255, 0.08); - border-color: rgba(255, 255, 255, 0.2); -} - -.stat-number { - display: block; - font-size: var(--text-xl); - font-weight: var(--font-bold); - color: var(--primary-light); - margin-bottom: var(--space-sm); -} - -.stat-label { - font-size: var(--text-sm); - color: var(--gray-400); - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.about-visual { - display: flex; - justify-content: center; - align-items: center; -} - -.rocket-diagram { - position: relative; - width: 200px; - height: 300px; -} - -.rocket-parts { - position: relative; - width: 100%; - height: 100%; -} - -.nose-cone { - position: absolute; - top: 0; - left: 50%; - transform: translateX(-50%); - width: 60px; - height: 80px; - background: linear-gradient(135deg, var(--primary-light), var(--primary-color)); - clip-path: polygon(50% 0%, 0% 100%, 100% 100%); - animation: rocketPulse 3s ease-in-out infinite; -} - -.body-tube { - position: absolute; - top: 80px; - left: 50%; - transform: translateX(-50%); - width: 80px; - height: 140px; - background: linear-gradient(135deg, var(--gray-300), var(--gray-400)); - border-radius: var(--radius-md); - animation: rocketPulse 3s ease-in-out infinite 0.5s; -} - -.fins { - position: absolute; - bottom: 40px; - left: 50%; - transform: translateX(-50%); - width: 120px; - height: 60px; - background: linear-gradient(135deg, var(--accent-color), var(--accent-dark)); - clip-path: polygon(20% 0%, 80% 0%, 100% 100%, 0% 100%); - animation: rocketPulse 3s ease-in-out infinite 1s; -} - -.engine { - position: absolute; - bottom: 0; - left: 50%; - transform: translateX(-50%); - width: 60px; - height: 40px; - background: linear-gradient(135deg, var(--secondary-color), var(--secondary-dark)); - border-radius: 0 0 var(--radius-md) var(--radius-md); - animation: rocketPulse 3s ease-in-out infinite 1.5s; -} - -@keyframes rocketPulse { - 0%, 100% { opacity: 0.8; transform: translateX(-50%) scale(1); } - 50% { opacity: 1; transform: translateX(-50%) scale(1.05); } -} - -/* Mission Section */ -.mission { - background: var(--bg-primary); - position: relative; -} - -.mission::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: radial-gradient(circle at 30% 70%, rgba(0, 102, 204, 0.1) 0%, transparent 50%), - radial-gradient(circle at 70% 30%, rgba(0, 212, 170, 0.1) 0%, transparent 50%); -} - -.mission-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: var(--space-2xl); - position: relative; - z-index: 1; -} - -.mission-card { - background: rgba(255, 255, 255, 0.05); - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: var(--radius-2xl); - padding: var(--space-2xl); - text-align: center; - transition: all var(--transition-base); - position: relative; - overflow: hidden; -} - -.mission-card::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent); - transition: left var(--transition-slow); -} - -.mission-card:hover::before { - left: 100%; -} - -.mission-card:hover { - transform: translateY(-10px); - background: rgba(255, 255, 255, 0.08); - border-color: rgba(255, 255, 255, 0.2); - box-shadow: var(--shadow-xl); -} - -.card-icon { - width: 80px; - height: 80px; - margin: 0 auto var(--space-lg); - background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); - border-radius: var(--radius-full); - display: flex; - align-items: center; - justify-content: center; - color: var(--white); -} - -.mission-card h3 { - font-size: var(--text-2xl); - margin-bottom: var(--space-md); - color: var(--white); -} - -.mission-card p { - color: var(--gray-300); - line-height: 1.6; -} - -/* Projects Section */ -.projects { - background: linear-gradient(135deg, var(--bg-tertiary) 0%, var(--bg-secondary) 100%); - position: relative; -} - -.projects-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); - gap: var(--space-2xl); -} - -.project-card { - background: rgba(255, 255, 255, 0.05); - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: var(--radius-xl); - padding: var(--space-2xl); - transition: all var(--transition-base); - position: relative; - overflow: hidden; -} - -.project-card::after { - content: ''; - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 3px; - background: linear-gradient(90deg, var(--primary-color), var(--accent-color)); - transform: scaleX(0); - transform-origin: left; - transition: transform var(--transition-base); -} - -.project-card:hover::after { - transform: scaleX(1); -} - -.project-card:hover { - transform: translateY(-8px); - background: rgba(255, 255, 255, 0.08); - border-color: rgba(255, 255, 255, 0.2); - box-shadow: var(--shadow-xl); -} - -.project-header { - display: flex; - justify-content: space-between; - align-items: flex-start; - margin-bottom: var(--space-lg); -} - -.project-header h3 { - font-size: var(--text-xl); - margin-bottom: 0; - color: var(--white); -} - -.project-status { - padding: var(--space-xs) var(--space-md); - background: var(--accent-color); - color: var(--white); - border-radius: var(--radius-full); - font-size: var(--text-xs); - font-weight: var(--font-medium); - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.project-tech { - display: flex; - flex-wrap: wrap; - gap: var(--space-sm); - margin: var(--space-lg) 0; -} - -.tech-tag { - padding: var(--space-xs) var(--space-md); - background: rgba(255, 255, 255, 0.1); - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: var(--radius-full); - font-size: var(--text-xs); - color: var(--gray-300); - font-weight: var(--font-medium); -} - -.project-link { - color: var(--primary-light); - font-weight: var(--font-medium); - text-decoration: none; - transition: color var(--transition-base); -} - -.project-link:hover { - color: var(--accent-color); -} - -/* Community Section */ -.community { - background: var(--bg-primary); - position: relative; -} - -.community::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: - radial-gradient(circle at 20% 20%, rgba(0, 102, 204, 0.1) 0%, transparent 50%), - radial-gradient(circle at 80% 80%, rgba(255, 107, 53, 0.1) 0%, transparent 50%); -} - -.community-content { - display: grid; - grid-template-columns: 1fr; - gap: var(--space-3xl); - align-items: center; - position: relative; - z-index: 1; -} - -.community-text h3 { - font-size: var(--text-3xl); - margin-bottom: var(--space-lg); - color: var(--white); -} - -.community-benefits { - list-style: none; - padding: 0; -} - -.community-benefits li { - position: relative; - padding-left: var(--space-xl); - margin-bottom: var(--space-md); - color: var(--gray-300); -} - -.community-benefits li::before { - content: '✓'; - position: absolute; - left: 0; - top: 0; - color: var(--accent-color); - font-weight: var(--font-bold); - font-size: var(--text-lg); -} - -.community-links { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); - gap: var(--space-xl); -} - -.community-card { - display: flex; - align-items: center; - gap: var(--space-lg); - padding: var(--space-xl); - background: rgba(255, 255, 255, 0.05); - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: var(--radius-xl); - text-decoration: none; - transition: all var(--transition-base); - position: relative; - overflow: hidden; -} - -.community-card::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent); - transition: left var(--transition-slow); -} - -.community-card:hover::before { - left: 100%; -} - -.community-card:hover { - transform: translateY(-5px); - background: rgba(255, 255, 255, 0.08); - border-color: rgba(255, 255, 255, 0.2); - box-shadow: var(--shadow-lg); -} - -.community-icon { - width: 60px; - height: 60px; - border-radius: var(--radius-full); - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; -} - -.community-icon.discord { - background: linear-gradient(135deg, #5865f2, #4752c4); -} - -.community-icon.github { - background: linear-gradient(135deg, var(--gray-700), var(--gray-800)); -} - -.community-info h4 { - font-size: var(--text-lg); - margin-bottom: var(--space-xs); - color: var(--white); -} - -.community-info p { - font-size: var(--text-sm); - color: var(--gray-400); - margin-bottom: 0; -} - -/* Contact Section */ -.contact { - background: linear-gradient(135deg, var(--bg-secondary) 0%, var(--bg-tertiary) 100%); - position: relative; -} - -.contact::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: radial-gradient(circle at 50% 50%, rgba(0, 212, 170, 0.1) 0%, transparent 70%); -} - -.contact-content { - display: grid; - grid-template-columns: 1fr; - gap: var(--space-3xl); - position: relative; - z-index: 1; -} - -.contact-info h3 { - font-size: var(--text-3xl); - margin-bottom: var(--space-lg); - color: var(--white); -} - -.contact-details { - margin-top: var(--space-xl); -} - -.contact-item { - display: flex; - align-items: center; - gap: var(--space-md); - margin-bottom: var(--space-md); - padding: var(--space-md); - background: rgba(255, 255, 255, 0.05); - border-radius: var(--radius-md); - border-left: 3px solid var(--primary-color); -} - -.contact-item strong { - color: var(--white); - min-width: 80px; -} - -.contact-item a { - color: var(--primary-light); - text-decoration: none; - transition: color var(--transition-base); -} - -.contact-item a:hover { - color: var(--accent-color); -} - -.contact-form { - background: rgba(255, 255, 255, 0.05); - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: var(--radius-2xl); - padding: var(--space-2xl); -} - -.contact-form .btn { - width: 100%; - justify-content: center; -} - -/* Footer */ -.footer { - background: var(--bg-primary); - border-top: 1px solid rgba(255, 255, 255, 0.1); - position: relative; -} - -.footer::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 1px; - background: linear-gradient(90deg, transparent, var(--primary-color), transparent); -} - -.footer-content { - display: grid; - grid-template-columns: 1fr; - gap: var(--space-2xl); - padding: var(--space-3xl) 0 var(--space-xl); -} - -.footer-brand { - display: flex; - align-items: center; - gap: var(--space-lg); -} - -.footer-brand .logo { - color: var(--primary-light); -} - -.footer-brand h4 { - color: var(--white); - margin-bottom: var(--space-xs); -} - -.footer-brand p { - color: var(--gray-400); - margin-bottom: 0; - font-size: var(--text-sm); -} - -.footer-links { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - gap: var(--space-xl); -} - -.footer-section h5 { - color: var(--white); - font-size: var(--text-lg); - margin-bottom: var(--space-lg); -} - -.footer-section ul { - list-style: none; - padding: 0; - margin: 0; -} - -.footer-section li { - margin-bottom: var(--space-sm); -} - -.footer-section a { - color: var(--gray-400); - text-decoration: none; - font-size: var(--text-sm); - transition: color var(--transition-base); -} - -.footer-section a:hover { - color: var(--primary-light); -} - -.footer-section span { - color: var(--gray-500); - font-size: var(--text-sm); -} - -.footer-bottom { - border-top: 1px solid rgba(255, 255, 255, 0.1); - padding: var(--space-xl) 0; - text-align: center; -} - -.footer-bottom p { - color: var(--gray-500); - font-size: var(--text-sm); - margin-bottom: 0; -} - -/* Mobile Optimizations */ -@media (min-width: 768px) { - .about-content { - grid-template-columns: 1fr 1fr; - } - - .community-content { - grid-template-columns: 1fr 1fr; - } - - .contact-content { - grid-template-columns: 1fr 1fr; - } - - .footer-content { - grid-template-columns: 2fr 3fr; - } -} - -@media (min-width: 1024px) { - .footer-links { - grid-template-columns: repeat(3, 1fr); - } -} diff --git a/styles/dashboard.css b/styles/dashboard.css deleted file mode 100644 index 3089d63..0000000 --- a/styles/dashboard.css +++ /dev/null @@ -1,924 +0,0 @@ -/* ===== ROOT VARIABLES ===== */ -:root { - /* Colors - Microsoft Teams inspired */ - --primary-bg: #1f1f23; - --secondary-bg: #2a2a2e; - --sidebar-bg: #252529; - --card-bg: #292c33; - --hover-bg: #3a3d44; - --border-color: #3a3d44; - - /* Accent Colors */ - --accent-primary: #6264a7; - --accent-secondary: #464775; - --success-color: #92c5f7; - --warning-color: #ffaa44; - --danger-color: #f85149; - - /* Text Colors */ - --text-primary: #f6f8fa; - --text-secondary: #8c949d; - --text-muted: #656d76; - - /* Gradients */ - --gradient-primary: linear-gradient(135deg, var(--accent-primary), #7b68ee); - - /* Fonts */ - --font-primary: 'Inter', system-ui, sans-serif; - --font-mono: 'Space Mono', monospace; - - /* Spacing */ - --sidebar-width: 280px; - --topbar-height: 64px; - - /* Shadows */ - --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.2); - --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.3); - --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.4); -} - -/* ===== RESET & BASE ===== */ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: var(--font-primary); - background: var(--primary-bg); - color: var(--text-primary); - overflow-x: hidden; -} - -/* ===== LAYOUT ===== */ -.sidebar { - position: fixed; - left: 0; - top: 0; - width: var(--sidebar-width); - height: 100vh; - background: var(--sidebar-bg); - border-right: 1px solid var(--border-color); - display: flex; - flex-direction: column; - z-index: 1000; - transition: transform 0.3s ease; -} - -.main-content { - margin-left: var(--sidebar-width); - min-height: 100vh; - transition: margin-left 0.3s ease; -} - -/* ===== SIDEBAR ===== */ -.sidebar-header { - padding: 20px; - border-bottom: 1px solid var(--border-color); - display: flex; - align-items: center; - gap: 12px; -} - -.sidebar-logo { - width: 32px; - height: 32px; -} - -.sidebar-title { - font-size: 1.1rem; - font-weight: 600; - color: var(--text-primary); -} - -.sidebar-nav { - flex: 1; - padding: 20px 0; -} - -.nav-item { - display: flex; - align-items: center; - gap: 12px; - padding: 12px 20px; - color: var(--text-secondary); - text-decoration: none; - transition: all 0.2s ease; - position: relative; -} - -.nav-item:hover { - background: var(--hover-bg); - color: var(--text-primary); - text-decoration: none; -} - -.nav-item.active { - background: rgba(98, 100, 167, 0.1); - color: var(--accent-primary); - border-right: 3px solid var(--accent-primary); -} - -.nav-item i { - width: 20px; - font-size: 1.1rem; -} - -.badge { - background: var(--danger-color); - color: white; - font-size: 0.75rem; - padding: 2px 6px; - border-radius: 10px; - margin-left: auto; -} - -.sidebar-footer { - padding: 20px; - border-top: 1px solid var(--border-color); -} - -.user-profile { - display: flex; - align-items: center; - gap: 12px; - padding: 8px; - border-radius: 8px; - transition: background 0.2s ease; -} - -.user-profile:hover { - background: var(--hover-bg); -} - -.user-avatar { - width: 36px; - height: 36px; - background: var(--gradient-primary); - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-weight: 600; - color: white; - font-size: 0.9rem; -} - -.user-avatar.small { - width: 28px; - height: 28px; - font-size: 0.8rem; -} - -.user-avatar.mini { - width: 24px; - height: 24px; - font-size: 0.7rem; -} - -.user-info { - flex: 1; -} - -.user-name { - font-size: 0.9rem; - font-weight: 600; - color: var(--text-primary); -} - -.user-role { - font-size: 0.8rem; - color: var(--text-secondary); -} - -.user-menu-btn { - background: none; - border: none; - color: var(--text-secondary); - cursor: pointer; - padding: 4px; - border-radius: 4px; - transition: all 0.2s ease; -} - -.user-menu-btn:hover { - background: var(--hover-bg); - color: var(--text-primary); -} - -/* ===== TOP BAR ===== */ -.top-bar { - height: var(--topbar-height); - background: var(--secondary-bg); - border-bottom: 1px solid var(--border-color); - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 24px; - position: sticky; - top: 0; - z-index: 100; -} - -.top-bar-left { - display: flex; - align-items: center; - gap: 16px; -} - -.sidebar-toggle { - display: none; - background: none; - border: none; - color: var(--text-secondary); - font-size: 1.2rem; - cursor: pointer; - padding: 8px; - border-radius: 4px; - transition: all 0.2s ease; -} - -.sidebar-toggle:hover { - background: var(--hover-bg); - color: var(--text-primary); -} - -.page-title { - font-size: 1.5rem; - font-weight: 600; - color: var(--text-primary); -} - -.top-bar-right { - display: flex; - align-items: center; - gap: 16px; -} - -.search-box { - position: relative; - width: 300px; -} - -.search-box i { - position: absolute; - left: 12px; - top: 50%; - transform: translateY(-50%); - color: var(--text-secondary); -} - -.search-box input { - width: 100%; - padding: 8px 12px 8px 36px; - background: var(--primary-bg); - border: 1px solid var(--border-color); - border-radius: 6px; - color: var(--text-primary); - font-size: 0.9rem; - transition: all 0.2s ease; -} - -.search-box input:focus { - outline: none; - border-color: var(--accent-primary); - box-shadow: 0 0 0 3px rgba(98, 100, 167, 0.1); -} - -.search-box input::placeholder { - color: var(--text-muted); -} - -.notification-btn { - background: none; - border: none; - color: var(--text-secondary); - font-size: 1.2rem; - cursor: pointer; - padding: 8px; - border-radius: 4px; - position: relative; - transition: all 0.2s ease; -} - -.notification-btn:hover { - background: var(--hover-bg); - color: var(--text-primary); -} - -.notification-dot { - position: absolute; - top: 6px; - right: 6px; - width: 8px; - height: 8px; - background: var(--danger-color); - border-radius: 50%; -} - -.user-menu { - display: flex; - align-items: center; - gap: 8px; - cursor: pointer; - padding: 4px 8px; - border-radius: 6px; - transition: background 0.2s ease; -} - -.user-menu:hover { - background: var(--hover-bg); -} - -.user-menu i { - color: var(--text-secondary); - font-size: 0.8rem; -} - -/* ===== DASHBOARD CONTENT ===== */ -.dashboard-content { - padding: 24px; -} - -.welcome-section { - background: var(--card-bg); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 24px; - margin-bottom: 24px; - display: flex; - justify-content: space-between; - align-items: center; -} - -.welcome-content h2 { - font-size: 1.5rem; - font-weight: 600; - margin-bottom: 8px; - color: var(--text-primary); -} - -.welcome-content p { - color: var(--text-secondary); - font-size: 1rem; -} - -.quick-actions { - display: flex; - gap: 12px; -} - -.quick-action-btn { - display: flex; - align-items: center; - gap: 8px; - background: var(--accent-primary); - color: white; - border: none; - padding: 12px 20px; - border-radius: 8px; - cursor: pointer; - font-weight: 500; - transition: all 0.2s ease; -} - -.quick-action-btn:hover { - background: var(--accent-secondary); - transform: translateY(-1px); -} - -/* ===== DASHBOARD GRID ===== */ -.dashboard-grid { - display: grid; - grid-template-columns: 2fr 1fr; - gap: 24px; -} - -.dashboard-card { - background: var(--card-bg); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 24px; - transition: all 0.2s ease; -} - -.dashboard-card:hover { - box-shadow: var(--shadow-md); -} - -.card-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 20px; - padding-bottom: 16px; - border-bottom: 1px solid var(--border-color); -} - -.card-header h3 { - font-size: 1.25rem; - font-weight: 600; - color: var(--text-primary); -} - -.date { - color: var(--text-secondary); - font-size: 0.9rem; -} - -.progress-summary { - color: var(--success-color); - font-weight: 600; -} - -.assignment-count { - color: var(--warning-color); - font-weight: 600; -} - -/* ===== SCHEDULE CARD ===== */ -.schedule-card { - grid-column: 1 / -1; -} - -.schedule-list { - display: flex; - flex-direction: column; - gap: 16px; -} - -.schedule-item { - display: flex; - align-items: center; - gap: 20px; - padding: 16px; - background: var(--secondary-bg); - border-radius: 8px; - border-left: 4px solid transparent; - transition: all 0.2s ease; -} - -.schedule-item:hover { - background: var(--hover-bg); -} - -.schedule-item.current { - border-left-color: var(--accent-primary); - background: rgba(98, 100, 167, 0.05); -} - -.schedule-time { - display: flex; - flex-direction: column; - align-items: center; - min-width: 80px; -} - -.time { - font-weight: 600; - color: var(--text-primary); - font-size: 1rem; -} - -.duration { - font-size: 0.8rem; - color: var(--text-secondary); -} - -.schedule-details { - flex: 1; -} - -.schedule-details h4 { - font-size: 1.1rem; - font-weight: 600; - color: var(--text-primary); - margin-bottom: 4px; -} - -.schedule-details p { - color: var(--text-secondary); - margin-bottom: 8px; -} - -.schedule-tags { - display: flex; - gap: 8px; -} - -.tag { - background: rgba(98, 100, 167, 0.2); - color: var(--accent-primary); - padding: 2px 8px; - border-radius: 12px; - font-size: 0.75rem; - font-weight: 500; -} - -.btn-join { - background: var(--accent-primary); - color: white; - border: none; - padding: 8px 16px; - border-radius: 6px; - cursor: pointer; - font-weight: 500; - transition: all 0.2s ease; -} - -.btn-join:hover { - background: var(--accent-secondary); -} - -.btn-join.disabled { - background: var(--hover-bg); - color: var(--text-secondary); - cursor: not-allowed; -} - -.view-all-link { - display: inline-flex; - align-items: center; - color: var(--accent-primary); - text-decoration: none; - font-weight: 500; - margin-top: 16px; - transition: color 0.2s ease; -} - -.view-all-link:hover { - color: var(--text-primary); - text-decoration: none; -} - -/* ===== PROGRESS CARD ===== */ -.progress-list { - display: flex; - flex-direction: column; - gap: 20px; -} - -.progress-item { - display: flex; - align-items: center; - gap: 16px; -} - -.progress-info { - flex: 1; -} - -.progress-info h4 { - font-size: 1rem; - font-weight: 600; - color: var(--text-primary); - margin-bottom: 4px; -} - -.progress-info p { - font-size: 0.85rem; - color: var(--text-secondary); -} - -.progress-bar { - flex: 2; - height: 8px; - background: var(--hover-bg); - border-radius: 4px; - overflow: hidden; -} - -.progress-fill { - height: 100%; - background: var(--gradient-primary); - border-radius: 4px; - transition: width 0.3s ease; -} - -.progress-fill[data-progress="80"] { width: 80%; } -.progress-fill[data-progress="42"] { width: 42%; } -.progress-fill[data-progress="88"] { width: 88%; } - -.progress-percent { - font-size: 0.9rem; - font-weight: 600; - color: var(--text-primary); - min-width: 40px; - text-align: right; -} - -/* ===== ASSIGNMENTS CARD ===== */ -.assignments-list { - display: flex; - flex-direction: column; - gap: 16px; -} - -.assignment-item { - display: flex; - justify-content: space-between; - align-items: flex-start; - padding: 16px; - background: var(--secondary-bg); - border-radius: 8px; - border-left: 4px solid transparent; -} - -.assignment-item.urgent { - border-left-color: var(--danger-color); - background: rgba(248, 81, 73, 0.05); -} - -.assignment-info h4 { - font-size: 1rem; - font-weight: 600; - color: var(--text-primary); - margin-bottom: 4px; -} - -.assignment-info p { - font-size: 0.85rem; - color: var(--text-secondary); - margin-bottom: 8px; -} - -.assignment-meta { - display: flex; - gap: 12px; - font-size: 0.8rem; -} - -.due-date { - color: var(--warning-color); - font-weight: 500; -} - -.assignment-type { - color: var(--text-secondary); -} - -.submit-btn { - background: var(--success-color); - color: var(--primary-bg); - border: none; - padding: 8px 16px; - border-radius: 6px; - cursor: pointer; - font-weight: 500; - transition: all 0.2s ease; -} - -.submit-btn:hover { - background: #a8d3f7; -} - -/* ===== ACTIVITY CARD ===== */ -.activity-list { - display: flex; - flex-direction: column; - gap: 16px; -} - -.activity-item { - display: flex; - align-items: center; - gap: 12px; - padding: 12px; - background: var(--secondary-bg); - border-radius: 8px; -} - -.activity-icon { - width: 32px; - height: 32px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-size: 0.8rem; - color: white; -} - -.activity-icon.grade { background: #ffd700; } -.activity-icon.message { background: var(--accent-primary); } -.activity-icon.class { background: var(--success-color); } -.activity-icon.achievement { background: #ff6b35; } - -.activity-content { - flex: 1; -} - -.activity-content p { - font-size: 0.9rem; - color: var(--text-primary); - margin-bottom: 2px; -} - -.activity-time { - font-size: 0.8rem; - color: var(--text-secondary); -} - -.activity-grade { - font-weight: 700; - color: #ffd700; - font-size: 1.1rem; -} - -/* ===== STATS CARD ===== */ -.stats-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 16px; -} - -.stat-item { - display: flex; - align-items: center; - gap: 12px; - padding: 16px; - background: var(--secondary-bg); - border-radius: 8px; -} - -.stat-icon { - width: 40px; - height: 40px; - background: var(--gradient-primary); - border-radius: 8px; - display: flex; - align-items: center; - justify-content: center; - color: white; -} - -.stat-content { - display: flex; - flex-direction: column; -} - -.stat-number { - font-size: 1.5rem; - font-weight: 700; - color: var(--text-primary); -} - -.stat-label { - font-size: 0.8rem; - color: var(--text-secondary); -} - -/* ===== COMMUNITY CARD ===== */ -.community-posts { - display: flex; - flex-direction: column; - gap: 16px; -} - -.community-post { - padding: 16px; - background: var(--secondary-bg); - border-radius: 8px; -} - -.post-author { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 12px; -} - -.author-info { - display: flex; - flex-direction: column; -} - -.author-name { - font-size: 0.9rem; - font-weight: 600; - color: var(--text-primary); -} - -.post-time { - font-size: 0.75rem; - color: var(--text-secondary); -} - -.post-content { - font-size: 0.9rem; - color: var(--text-primary); - line-height: 1.5; - margin-bottom: 12px; -} - -.post-actions { - display: flex; - gap: 16px; -} - -.post-action { - display: flex; - align-items: center; - gap: 4px; - background: none; - border: none; - color: var(--text-secondary); - cursor: pointer; - font-size: 0.8rem; - transition: color 0.2s ease; -} - -.post-action:hover { - color: var(--accent-primary); -} - -/* ===== RESPONSIVE DESIGN ===== */ -@media (max-width: 1200px) { - .dashboard-grid { - grid-template-columns: 1fr; - } - - .stats-grid { - grid-template-columns: repeat(4, 1fr); - } -} - -@media (max-width: 768px) { - .sidebar { - transform: translateX(-100%); - } - - .sidebar.open { - transform: translateX(0); - } - - .main-content { - margin-left: 0; - } - - .sidebar-toggle { - display: block; - } - - .search-box { - width: 200px; - } - - .welcome-section { - flex-direction: column; - gap: 16px; - text-align: center; - } - - .quick-actions { - width: 100%; - justify-content: center; - } - - .dashboard-content { - padding: 16px; - } - - .stats-grid { - grid-template-columns: 1fr 1fr; - } - - .schedule-item { - flex-direction: column; - align-items: flex-start; - gap: 12px; - } - - .schedule-time { - flex-direction: row; - align-items: center; - gap: 8px; - min-width: auto; - } - - .assignment-item { - flex-direction: column; - align-items: flex-start; - gap: 12px; - } -} - -@media (max-width: 480px) { - .top-bar { - padding: 0 16px; - } - - .search-box { - width: 150px; - } - - .stats-grid { - grid-template-columns: 1fr; - } - - .quick-action-btn { - width: 100%; - justify-content: center; - } -} diff --git a/styles/hack-style.css b/styles/hack-style.css deleted file mode 100644 index e69de29..0000000 diff --git a/styles/hackclub-style.css b/styles/hackclub-style.css deleted file mode 100644 index 84a7d6e..0000000 --- a/styles/hackclub-style.css +++ /dev/null @@ -1,2843 +0,0 @@ -/* OpenRockets - Hack Club Inspired Design System */ -@import url('https://fonts.googleapis.com/css2?family=Phantom+Sans:wght@400;700;800;900&family=Space+Grotesk:wght@300;400;500;600;700;800;900&display=swap'); -@import url('https://fonts.googleapis.com/css2?family=Asimovian&family=Michroma&family=Zalando+Sans+Expanded:ital,wght@0,200..900;1,200..900&display=swap'); -:root { - /* Ada College Inspired Color Palette */ - --ada-yellow: #FFD23F; - --ada-green: #7CB342; - --ada-blue: #42A5F5; - --ada-purple: #BA68C8; - --ada-orange: #FF7043; - --ada-pink: #EC407A; - --ada-light-gray: #F8F9FA; - --ada-dark-gray: #263238; - - /* Modern Gradients - Ada Inspired */ - --gradient-primary: linear-gradient(135deg, var(--ada-yellow) 0%, var(--ada-orange) 100%); - --gradient-secondary: linear-gradient(135deg, var(--ada-green) 0%, var(--ada-blue) 100%); - --gradient-accent: linear-gradient(135deg, var(--ada-purple) 0%, var(--ada-pink) 100%); - --gradient-hero: linear-gradient(135deg, var(--ada-blue) 0%, var(--ada-purple) 50%, var(--ada-pink) 100%); - --gradient-geometric: linear-gradient(45deg, var(--ada-yellow) 25%, var(--ada-green) 25%, var(--ada-green) 50%, var(--ada-blue) 50%, var(--ada-blue) 75%, var(--ada-purple) 75%); - - /* Legacy Hack Club Colors (maintained for compatibility) */ - --hc-red: var(--ada-orange); - --hc-orange: var(--ada-yellow); - --hc-yellow: var(--ada-yellow); - --hc-green: var(--ada-green); - --hc-blue: var(--ada-blue); - --hc-purple: var(--ada-purple); - --hc-slate: #34495E; - --hc-muted: #7F8C8D; - --hc-smoke: #ECF0F1; - --hc-snow: #FFFFFF; - --hc-dark: #2C3E50; - --hc-black: #1A252F; - - /* Custom OpenRockets Extensions - Ada Style */ - --or-primary: var(--ada-blue); - --or-secondary: var(--ada-green); - --or-accent: var(--ada-yellow); - --or-dark: var(--ada-dark-gray); - --or-darker: #1A252F; - --or-light: var(--ada-light-gray); - - /* Typography */ - --font-primary: 'Space Grotesk', -apple-system, BlinkMacSystemFont, sans-serif; - --font-display: "Asimovian", sans-serif; - --font-modern: "Asimovian", sans-serif; - - /* Spacing */ - --space-1: 0.25rem; - --space-2: 0.5rem; - --space-3: 1rem; - --space-4: 1.5rem; - --space-5: 3rem; - --space-6: 4rem; - --space-7: 8rem; - --space-8: 16rem; - - /* Borders */ - --radius: 12px; - --radius-lg: 18px; - --radius-xl: 24px; - - /* Shadows */ - --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); - --shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1); - --shadow-xl: 0 25px 50px rgba(0, 0, 0, 0.15); - - /* Animations */ - --transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); - --transition-slow: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); -} - -/* Reset */ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -html { - scroll-behavior: smooth; -} - -body { - font-family: var(--font-primary); - background: linear-gradient(135deg, var(--ada-light-gray) 0%, #FFFFFF 25%, var(--ada-light-gray) 50%, #F0F3F7 75%, var(--ada-light-gray) 100%); - background-attachment: fixed; - color: var(--ada-dark-gray); - line-height: 1.6; - overflow-x: hidden; - font-weight: 400; - font-size: 18px; - position: relative; -} - -/* Ambient background effects - Ada Style Geometric Patterns */ -body::before { - content: ''; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: - radial-gradient(circle at 20% 80%, rgba(175, 122, 197, 0.08) 0%, transparent 50%), - radial-gradient(circle at 80% 20%, rgba(143, 209, 79, 0.08) 0%, transparent 50%), - radial-gradient(circle at 50% 50%, rgba(255, 210, 63, 0.05) 0%, transparent 50%); - pointer-events: none; - z-index: -1; -} - -/* Canvas Background */ -#canvas-bg { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: -1; - opacity: 0.1; -} - -/* Navigation - Ada Style */ -.navbar { - position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 1000; - background: rgba(0, 0, 0, 0.95); - -webkit-backdrop-filter: blur(20px); - backdrop-filter: blur(20px); - border-bottom: 3px solid var(--ada-yellow); - padding: var(--space-3) 0; - transition: var(--transition); - box-shadow: 0 2px 20px rgba(0, 0, 0, 0.08); -} - -.nav-container { - max-width: 1200px; - margin: 0 auto; - padding: 0 var(--space-4); - display: flex; - align-items: center; - justify-content: space-between; -} - -.nav-brand { - display: flex; - align-items: center; - gap: var(--space-3); - text-decoration: none; - transition: var(--transition); -} - -.nav-brand:hover { - transform: translateY(-2px); -} - -.nav-brand img { - width:4rem; - - object-fit: contain; - filter: drop-shadow(0 0 10px rgba(0, 0, 0, 0.3)); -} - -.brand-content { - display: flex; - flex-direction: column; - align-items: flex-start; -} - -.brand-text { - font-family: var(--font-display); - font-size: 1.5rem; - font-weight: 800; - - background: var(--ada-light-gray); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; - line-height: 1; -} - -.brand-platform { - font-size: 0.75rem; - color: var(--ada-blue); - margin-top: -2px; - opacity: 0.8; - letter-spacing: 2px; - text-transform: uppercase; -} - -.nav-menu { - display: flex; - gap: var(--space-4); - list-style: none; -} - -.nav-link { - color: var(--ada-dark-gray); - text-decoration: none; - font-weight: 600; - position: relative; - padding: var(--space-2) var(--space-3); - border-radius: var(--radius); - transition: var(--transition); - color: var(--ada-purple); - background: rgba(175, 122, 197, 0.1); -} - -.nav-link:hover { - color: var(--ada-purple); - background:transparent; - transform: translateY(-1px); -} - -.nav-link.active { - color: var(--ada-orange); - background: rgba(255, 140, 66, 0.15); - font-weight: 700; -} - -/* Mobile Navigation */ -.nav-toggle { - display: none; - background: none; - border: none; - color: var(--ada-dark-gray); - font-size: 1.5rem; - cursor: pointer; - padding: 0.5rem; - transition: all 0.3s ease; - z-index: 1001; -} - -.nav-toggle:hover { - color: var(--ada-purple); - transform: scale(1.1); -} - -.nav-toggle.active { - color: var(--ada-orange); -} - -/* Hero Section - Ada Style */ -.hero { - min-height: 100vh; - display: flex; - align-items: center; - justify-content: center; - text-align: center; - padding: var(--space-7) var(--space-4); - position: relative; - overflow: hidden; - background:url(../v/hero.png); - background-position: right; - background-repeat: no-repeat; - background-size: cover; -} - -.hero::before { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: - linear-gradient(45deg, var(--ada-yellow) 0%, transparent 20%), - linear-gradient(135deg, var(--ada-green) 0%, transparent 30%), - linear-gradient(225deg, var(--ada-purple) 0%, transparent 25%); - opacity: 0.1; - z-index: -1; -} - -@keyframes rotate { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } -} - -.hero-content { - position: relative; - z-index: 2; - max-width: 800px; -} - -.hero-badge { - display: inline-flex; - align-items: center; - gap: var(--space-2); - padding: var(--space-2) var(--space-4); - background: var(--gradient-primary); - color: var(--ada-dark-gray); - border-radius: 50px; - font-size: 0.875rem; - font-weight: 700; - margin-bottom: var(--space-4); - position: relative; - overflow: hidden; - border: 2px solid rgba(255, 255, 255, 0.3); - box-shadow: 0 4px 20px rgba(255, 210, 63, 0.3); -} - -.hero-badge.clickable { - cursor: pointer; - transition: all 0.3s ease; -} - -.hero-badge.clickable:hover { - transform: translateY(-2px) scale(1.05); - box-shadow: 0 8px 30px rgba(255, 210, 63, 0.5); -} - -@keyframes pulse { - 0%, 100% { transform: scale(1); } - 50% { transform: scale(1.05); } -} - -.hero-title { - font-family: var(--font-display); - font-size: clamp(3rem, 8vw, 6rem); - font-weight: 900; - line-height: 0.95; - margin-bottom: var(--space-4); - background: linear-gradient(45deg, white, #ffffff); - filter: drop-shadow(0 0 50px black); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; - animation: slideInUp 1s ease-out 0.2s both; -} - -.hero-subtitle { - font-size: clamp(1.2rem, 3vw, 1.8rem); - color: var(--ada-light-gray); - margin-bottom: var(--space-5); - animation: slideInUp 1s ease-out 0.4s both; - font-weight: 500; -} - -@keyframes slideInUp { - from { - opacity: 0; - transform: translateY(30px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.hero-cta { - display: flex; - gap: var(--space-3); - justify-content: center; - flex-wrap: wrap; - animation: slideInUp 1s ease-out 0.6s both; -} - -/* Buttons - Ada Style */ -.btn { - display: inline-flex; - align-items: center; - gap: var(--space-2); - padding: var(--space-3) var(--space-5); - border: none; - border-radius: var(--radius-lg); - font-family: var(--font-primary); - font-size: 1rem; - font-weight: 700; - text-decoration: none; - cursor: pointer; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - position: relative; - overflow: hidden; - text-transform: uppercase; - letter-spacing: 0.5px; - box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15); -} - -.btn::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); - transition: left 0.5s; -} - -.btn:hover::before { - left: 100%; -} - -.btn-primary { - background: var(--ada-dark-gray ); - color: var(--ada-light-gray); - border: 2px solid transparent; - background-clip: padding-box; -} - -.btn-primary:hover { - transform: translateY(-3px); - box-shadow: 0 8px 25px rgba(255, 210, 63, 0.4); - filter: brightness(1.1); -} - -.btn-secondary { - background: var(--ada-dark-gray ); - color: var(--ada-light-gray); - border: 2px solid transparent; - background-clip: padding-box; -} - -.btn-secondary:hover { - transform: translateY(-3px); - box-shadow: 0 8px 25px rgba(143, 209, 79, 0.4); - filter: brightness(1.1); -} - -.btn-large { - padding: var(--space-4) var(--space-5); - font-size: 1.125rem; - font-weight: 800; -} - -/* Hero Section Enhancements */ -.hero { - position: relative; - overflow: hidden; -} - -.hero-illustrations { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - pointer-events: none; - z-index: 1; -} - -.hero-img { - position: absolute; - - transition: all 0.6s ease; - filter: drop-shadow(0 0 20px rgba(124, 58, 237, 0.3)); -} - -.hero-img.girl-coding { - width: 200px; - top: 20%; - right: 10%; - animation: float 6s ease-in-out infinite; -} - -.hero-img.students-group { - width: 250px; - bottom: 15%; - left: 5%; - animation: float 8s ease-in-out infinite reverse; -} - -@keyframes float { - 0%, 100% { transform: translateY(0px); } - 50% { transform: translateY(-20px); } -} - @media (max-width: 1116px) { - .hero-illustrations { - display: none !important; - } - } - - -/* Stats Section - Ada Style */ -.stats { - padding: var(--space-6) var(--space-4); - background: var(--gradient-secondary); - margin: var(--space-6) 0; - border-top: 3px solid var(--ada-yellow); - border-bottom: 3px solid var(--ada-purple); -} - -.stats-grid { - max-width: 800px; - margin: 0 auto; - display: grid; - grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - gap: var(--space-5); - text-align: center; -} - -.stat-item { - animation: fadeInUp 1s ease-out both; - background: rgba(255, 255, 255, 0.2); - padding: var(--space-4); - border-radius: var(--radius-lg); - backdrop-filter: blur(10px); -} - -.stat-number { - font-family: var(--font-display); - font-size: 3rem; - font-weight: 900; - background: linear-gradient(45deg, var(--ada-dark-gray), #1A252F); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; - display: block; - line-height: 1; - margin-bottom: var(--space-2); -} - -.stat-label { - color: var(--ada-dark-gray); - font-size: 0.9rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -@keyframes fadeInUp { - from { - opacity: 0; - transform: translateY(20px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -/* Features Section - Enhanced Ada Style with Card Layout */ -.features { - padding: var(--space-7) var(--space-4); - background: #FFFFFF; -} - -.section-header { - text-align: left; - margin-bottom: var(--space-6); - max-width: 1200px; - margin-left: auto; - margin-right: auto; - display: flex; - justify-content: space-between; - align-items: center; -} - -.section-title { - font-family: var(--font-display); - font-size: clamp(2.5rem, 5vw, 3rem); - font-weight: 800; - margin-bottom: 0; - color: var(--ada-dark-gray); -} - -.section-subtitle { - font-size: 1.2rem; - color: var(--ada-dark-gray); - max-width: 600px; - margin: var(--space-2) 0 0 0; - font-weight: 400; - opacity: 0.8; -} - -.view-all-link { - color: var(--ada-blue); - text-decoration: none; - font-weight: 600; - font-size: 1rem; - transition: all 0.3s ease; -} - -.view-all-link:hover { - color: var(--ada-purple); - text-decoration: underline; -} - -.features-grid { - max-width: 1200px; - margin: 0 auto; - display: grid; - grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); - gap: var(--space-5); -} - -.feature-card { - background: #FFFFFF; - border: 1px solid #E5E7EB; - border-radius: 16px; - padding: .9rem; - transition: all 0.3s ease; - position: relative; - overflow: hidden; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); -} - -.feature-card::before { - display: none; -} - -.feature-card:hover { - transform: translateY(-4px); - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15); - border-color: var(--ada-blue); -} - -.feature-card-image { - width: 100%; - height: 200px; - background: var(--ada-light-gray); - border-radius: 16px 16px 0 0; - display: flex; - align-items: center; - justify-content: center; - background: linear-gradient(135deg, var(--ada-blue), var(--ada-purple)); -} - -.feature-card-image img { - width: 100%; - height: 100%; - object-fit: cover; -} - -.feature-card-content { - padding: var(--space-4); -} - -.feature-meta { - display: flex; - align-items: center; - gap: var(--space-2); - margin-bottom: var(--space-2); - font-size: 0.875rem; - color: var(--ada-dark-gray); - opacity: 0.7; -} - -.feature-category { - background: var(--ada-yellow); - color: var(--ada-dark); - padding: 0.25rem 0.75rem; - border-radius: 20px; - font-weight: 700; - text-transform: uppercase; - font-size: 0.75rem; - letter-spacing: 0.5px; -} - -.feature-date { - font-weight: 500; -} - -.feature-title { - font-size: 1.5rem; - font-weight: 700; - margin-bottom: var(--space-3); - color: var(--ada-dark-gray); - line-height: 1.3; -} - -.feature-description { - color: var(--ada-dark-gray); - line-height: 1.6; - opacity: 0.8; - margin-bottom: var(--space-4); -} - -.feature-link { - display: inline-flex; - align-items: center; - gap: 0.5rem; - color: var(--ada-dark-gray); - text-decoration: none; - font-weight: 600; - font-size: 0.9rem; - transition: all 0.3s ease; - background: var(--ada-light-gray); - padding: 0.5rem 1rem; - border-radius: 20px; -} - -.feature-link:hover { - background: var(--ada-dark-gray); - color: white; - transform: translateX(5px); -} - -.feature-link i { - font-size: 0.8rem; -} - -/* Remove old feature icon styling */ -.feature-icon { - display: none; -} - -/* CTA Section - Ada Style */ -.cta { - padding: var(--space-7) var(--space-4); - background: var(--gradient-primary); - text-align: center; - position: relative; - overflow: hidden; -} - -.cta::before { - content: ''; - position: absolute; - top: -2px; - left: 0; - right: 0; - height: 4px; - background: var(--gradient-secondary); -} - -.cta-content { - max-width: 600px; - margin: 0 auto; -} - -.cta-title { - font-family: var(--font-display); - font-size: clamp(2rem, 4vw, 3rem); - font-weight: 800; - margin-bottom: var(--space-3); - color: var(--ada-dark-gray); -} - -.cta-description { - font-size: 1.1rem; - color: var(--ada-dark-gray); - margin-bottom: var(--space-5); - line-height: 1.6; - opacity: 0.9; -} - -/* Footer - Ada Style */ -.footer { - background: var(--ada-dark-gray); - border-top: 4px solid var(--ada-blue); - padding: var(--space-6) var(--space-4) var(--space-4); - color: var(--ada-light-gray); -} - -.footer-content { - max-width: 1200px; - margin: 0 auto; - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: var(--space-5); - margin-bottom: var(--space-5); -} - -.footer-section h4 { - font-size: 1.1rem; - font-weight: 700; - margin-bottom: var(--space-3); - color: var(--hc-snow); -} - -.footer-section ul { - list-style: none; -} - -.footer-section a { - color: var(--hc-muted); - text-decoration: none; - transition: var(--transition); - display: block; - padding: var(--space-1) 0; -} - -.footer-section a:hover { - color: var(--hc-red); -} - -.footer-logo { - width: 3rem; - margin-bottom: 1rem; -} - -.footer-description { - color: var(--hc-muted); - margin-bottom: 1rem; -} - -.footer-social { - display: flex; - gap: 1rem; -} - -.footer-social a { - color: var(--hc-red); - font-size: 1.2rem; - padding: 0; -} - -.footer-social a:hover { - transform: translateY(-2px); -} - -.footer-bottom { - padding-top: var(--space-4); - border-top: 1px solid rgba(255, 255, 255, 0.08); - text-align: left; - width: 100%; - color: var(--hc-muted); - font-size: 0.9rem; -} - -/* Responsive Design - Ada Style */ -@media (max-width: 768px) { - .nav-menu { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100vh; - background: linear-gradient(135deg, var(--ada-light-gray) 0%, #FFFFFF 50%, var(--ada-light-gray) 100%); - flex-direction: column; - justify-content: center; - align-items: center; - gap: 2rem; - transform: translateX(-100%); - transition: transform 0.3s ease; - z-index: 1000; - padding: 0; - margin: 0; - list-style: none; - } - - .nav-menu.active { - transform: translateX(0); - } - - .nav-menu li { - margin: 0; - } - - .nav-menu .nav-link { - font-size: 1.5rem; - padding: 1rem 2rem; - border-radius: 12px; - transition: all 0.3s ease; - background: rgba(255, 255, 255, 0.8); - border: 2px solid var(--ada-yellow); - min-width: 200px; - text-align: center; - color: var(--ada-dark-gray); - font-weight: 700; - } - - .nav-menu .nav-link:hover, - .nav-menu .nav-link.active { - background: var(--gradient-primary); - transform: scale(1.05); - box-shadow: 0 8px 32px rgba(255, 210, 63, 0.3); - } - - .nav-brand { - z-index: 1001; - position: relative; - } - - .nav-toggle { - display: block; - } - - .hero-cta { - flex-direction: column; - align-items: center; - } - - .btn { - width: 100%; - max-width: 300px; - justify-content: center; - } -} - -/* Loading Animation */ -@keyframes shimmer { - 0% { background-position: -200px 0; } - 100% { background-position: calc(200px + 100%) 0; } -} - -.shimmer { - background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent); - background-size: 200px 100%; - animation: shimmer 2s infinite; -} - -/* Utility Classes - Ada Style */ -.text-gradient { - background: var(--gradient-primary); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; -} - -.glow { - box-shadow: 0 0 30px rgba(255, 210, 63, 0.4); -} - -.pulse { - animation: pulse 2s ease-in-out infinite; -} - -/* School Marquee Section - Ada Style */ -.school-marquee { - padding: var(--space-7) 0; - background: var(--ada-light-gray); - overflow: hidden; - border-top: 4px solid var(--ada-yellow); - border-bottom: 4px solid var(--ada-purple); -} - -.marquee-header { - text-align: center; - margin-bottom: var(--space-5); - max-width: 800px; - margin-left: auto; - margin-right: auto; - padding: 0 var(--space-3); -} - -.marquee-header h3 { - font-family: var(--font-display); - font-size: clamp(1.5rem, 4vw, 2.5rem); - font-weight: 800; - background: linear-gradient(135deg, var(--ada-dark-gray) 0%, #34495E 50%, var(--ada-dark-gray) 100%); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; - margin-bottom: var(--space-3); -} - -.marquee-header p { - color: var(--ada-dark-gray); - font-size: 1.125rem; - line-height: 1.6; - font-weight: 500; -} - -.marquee-container { - width: 100%; - overflow: hidden; - -webkit-mask: linear-gradient(90deg, transparent, white 20%, white 80%, transparent); - mask: linear-gradient(90deg, transparent, white 20%, white 80%, transparent); -} - -.marquee-content { - display: flex; - align-items: center; - animation: marquee 150s linear infinite; - width: fit-content; - gap: var(--space-6); -} - -.school-logo { - height: 60px; - width: auto; - object-fit: contain; - opacity: 0.8; - transition: all 0.3s ease; - filter: brightness(1.2) contrast(1.1); - flex-shrink: 0; -} - -.school-logo:hover { - opacity: 1; - transform: scale(1.1); - filter: brightness(1.4) contrast(1.2); -} - -@keyframes marquee { - 0% { - transform: translateX(0); - } - 100% { - transform: translateX(-50%); - } -} - -/* Pause animation on hover */ -.marquee-container:hover .marquee-content { - animation-play-state: paused; -} - -/* Footer Improvements */ -.footer-logo { - width: 3rem; - margin-bottom: 1rem; -} - -.footer-description { - color: var(--hc-muted); - margin-bottom: 1rem; - line-height: 1.6; -} - -.footer-social { - display: flex; - gap: 1rem; -} - -.footer-social a { - color: var(--hc-red); - font-size: 1.25rem; - transition: all 0.3s ease; -} - -.footer-social a:hover { - color: var(--hc-orange); - transform: translateY(-2px); -} - -/* Mobile adjustments for marquee */ -@media (max-width: 768px) { - .school-marquee { - padding: var(--space-5) 0; - } - - .school-logo { - height: 45px; - } - - .marquee-content { - gap: var(--space-4); - } - - .marquee-header h3 { - font-size: clamp(1.25rem, 5vw, 1.75rem); - } -} - -/* Parrot Assistant Styles */ -.parrot-assistant { - position: fixed; - bottom: 20px; - right: 20px; - z-index: 1000; - pointer-events: none; - transition: all 0.3s ease; -} - -.parrot-container { - position: relative; - display: flex; - align-items: flex-end; - gap: 15px; -} - -.parrot-gif { - width: 80px; - height: 80px; - object-fit: contain; - filter: drop-shadow(0 4px 15px rgba(0, 0, 0, 0.3)); - transition: all 0.3s ease; - pointer-events: auto; - cursor: pointer; -} - -.parrot-gif:hover { - transform: scale(1.1); - filter: drop-shadow(0 6px 20px rgba(0, 0, 0, 0.5)); -} - -.parrot-speech-bubble { - position: absolute; - bottom: 90px; - right: 0; - min-width: 200px; - max-width: 300px; - padding: 15px 20px; - background: linear-gradient(135deg,white, WHITE 100%); - border-radius: 20px; - border: 2px solid rgba(255, 255, 255, 0.2); - box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3); - transform: translateY(20px) scale(0.8); - opacity: 0; - transition: all 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55); - pointer-events: auto; -} - -.parrot-speech-bubble.show { - transform: translateY(0) scale(1); - opacity: 1; -} - -.bubble-content { - color: rgb(0, 0, 0); - font-size: 14px; - font-weight: 500; - line-height: 1.4; - text-align: left; -} - -.bubble-tail { - position: absolute; - bottom: -10px; - right: 30px; - width: 0; - height: 0; - border-left: 10px solid transparent; - border-right: 10px solid transparent; - border-top: 10px solid var(--primary-purple); -} - -.parrot-category-badge { - position: absolute; - top: -10px; - right: -10px; - background: var(--gradient-sunset); - color: rgb(0, 0, 0); - padding: 4px 8px; - border-radius: 12px; - font-size: 10px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.5px; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); - transform: scale(0); - transition: all 0.3s ease; -} - -.parrot-category-badge.show { - transform: scale(1); -} - -/* Animation for parrot entrance */ -@keyframes parrotEntrance { - 0% { - transform: translateY(100px) rotate(20deg); - opacity: 0; - } - 50% { - transform: translateY(-10px) rotate(-5deg); - opacity: 0.8; - } - 100% { - transform: translateY(0) rotate(0deg); - opacity: 1; - } -} - -.parrot-assistant.animate-entrance .parrot-gif { - animation: parrotEntrance 1.5s ease-out; -} - -/* Bounce animation for interactions */ -@keyframes parrotBounce { - 0%, 100% { transform: translateY(0); } - 25% { transform: translateY(-8px); } - 50% { transform: translateY(-4px); } - 75% { transform: translateY(-2px); } -} - -.parrot-gif.bounce { - animation: parrotBounce 0.6s ease; -} - -/* Mobile adjustments for parrot */ -@media (max-width: 768px) { - .parrot-assistant { - bottom: 15px; - right: 15px; - } - - .parrot-gif { - width: 60px; - height: 60px; - } - - .parrot-speech-bubble { - bottom: 70px; - min-width: 180px; - max-width: 250px; - padding: 12px 16px; - } - - .bubble-content { - font-size: 13px; - } -} - -/* Feature Card Enhancements */ -.feature-card { - position: relative; - overflow: hidden; -} - -.feature-image { - position: absolute; - top: 0; - right: 0; - z-index: 1; - pointer-events: none; -} - -.feature-image img { - width: auto; - height: auto; - opacity: 0.2; - transition: opacity 0.3s ease; -} - -.feature-card:hover .feature-image img { - opacity: 0.3; -} - -.feature-image.laptop { - top: 10px; - right: 15px; -} - -.feature-image.laptop img { - width: 80px; - opacity: 0.3; -} - -.feature-image.disk { - top: 20px; - right: 20px; -} - -.feature-image.disk img { - width: 60px; - opacity: 0.2; -} - -.feature-image.usb { - bottom: 15px; - right: 15px; -} - -.feature-image.usb img { - width: 70px; - opacity: 0.25; -} - -.feature-image.old-laptop { - top: 10px; - right: 10px; -} - -.feature-image.old-laptop img { - width: 90px; - opacity: 0.15; -} - -.feature-image.tinkercad { - bottom: 10px; - right: 10px; -} - -.feature-image.tinkercad img { - width: 100px; - opacity: 0.2; -} - -/* Project Links */ -.project-link { - display: inline-flex; - align-items: center; - gap: 0.5rem; - margin-top: 1rem; - color: var(--hc-red); - text-decoration: none; - font-weight: 600; - font-size: 0.9rem; - transition: all 0.3s ease; -} - -.project-link:hover { - color: var(--hc-orange); - transform: translateX(5px); -} - -.project-link i { - font-size: 0.8rem; -} - -/* HackClub DayDream Sri Lanka Event Styles */ -.hackclub-highlight { - background: linear-gradient(135deg, #ec3750, #ff8c37) !important; - background-clip: text !important; - -webkit-background-clip: text !important; - -webkit-text-fill-color: transparent !important; - font-weight: 700 !important; - position: relative; -} - -.hackclub-highlight::after { - content: ''; - position: absolute; - bottom: -2px; - left: 0; - right: 0; - height: 2px; - background: linear-gradient(135deg, #ec3750, #ff8c37); - border-radius: 1px; -} - -.hackclub-event-banner { - background: var(--ada-light-gray); - border: 2px solid var(--ada-orange); - position: relative; - overflow: hidden; - margin: 2rem 0; - border-radius: 24px; - max-width: 1200px; - margin: 2rem auto; - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08); -} - -.hackclub-event-banner::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: linear-gradient(135deg, var(--ada-orange), var(--ada-yellow), var(--ada-green), var(--ada-blue)); - z-index: -1; - margin: -2px; - border-radius: 24px; - opacity: 0.1; -} - -.event-container { - padding: 3rem 2rem; - position: relative; - z-index: 1; - max-width: 1200px; - margin: 0 auto; -} - -.event-header { - display: flex; - align-items: center; - gap: 1.5rem; - margin-bottom: 2rem; - flex-wrap: wrap; -} - -.hackclub-logo { - width: 60px; - height: 60px; - border-radius: 12px; - box-shadow: 0 8px 32px rgba(236, 55, 80, 0.3); -} - -.event-title-group { - flex: 1; - min-width: 250px; -} - -.event-title { - font-size: 2.5rem; - font-weight: 900; - background: linear-gradient(135deg, var(--ada-orange), var(--ada-purple)); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; - margin: 0; - line-height: 1.2; -} - -.event-date { - display: inline-block; - background: var(--gradient-primary); - color: var(--ada-dark-gray); - padding: 0.5rem 1rem; - border-radius: 20px; - font-weight: 700; - font-size: 0.9rem; - margin-top: 0.5rem; -} - -.event-badge { - background: var(--ada-orange); - color: white; - padding: 0.75rem 1.5rem; - border-radius: 25px; - font-weight: 700; - font-size: 0.9rem; - position: relative; - overflow: hidden; - animation: pulse-glow 2s infinite; -} - -.event-badge::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); - animation: shine 3s infinite; -} - -.event-subtitle { - font-size: 1.2rem; - color: var(--ada-dark-gray); - margin-bottom: 2rem; - line-height: 1.6; -} - -.event-description { - text-align: left; - max-width: 100%; -} - -.event-details { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 1rem; - margin: 2rem 0; -} - -.detail-item { - display: flex; - align-items: center; - gap: 0.75rem; - background: rgba(255, 255, 255, 0.8); - padding: 1rem; - border-radius: 12px; - border: 1px solid var(--ada-blue); - transition: all 0.3s ease; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); -} - -.detail-item:hover { - background: rgba(255, 255, 255, 1); - transform: translateY(-2px); - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); -} - -.detail-item i { - color: var(--ada-orange); - font-size: 1.2rem; - width: 20px; - text-align: center; -} - -.detail-item span { - color: var(--ada-dark-gray); - font-weight: 500; -} - -.event-cta { - display: flex; - gap: 1rem; - margin-top: 2rem; - flex-wrap: wrap; -} - -.btn-hackclub { - background: linear-gradient(135deg, #ec3750, #ff8c37) !important; - border: none !important; - position: relative; - overflow: hidden; -} - -.btn-hackclub::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); - animation: shine 3s infinite; -} - -/* Floating Button */ -.hackclub-floating-btn { - position: fixed; - bottom: 1rem; - left: 30px; - background: linear-gradient(135deg, #ec3750, #ff8c37); - border-radius: 60px; - padding: 1rem 1.5rem; - display: flex; - align-items: center; - gap: 1rem; - cursor: pointer; - box-shadow: 0 8px 32px rgba(236, 55, 80, 0.4); - transition: all 0.3s ease; - z-index: 1000; - max-width: 200px; - animation: float-bounce 3s infinite; -} - -.hackclub-floating-btn:hover { - transform: translateY(-5px); - box-shadow: 0 12px 48px rgba(236, 55, 80, 0.6); -} - -.float-logo { - width: 40px; - height: 40px; - border-radius: 8px; - box-shadow: 0 4px 16px rgba(0,0,0,0.3); -} - -.float-text { - display: flex; - flex-direction: column; - color: white; -} - -.float-title { - font-weight: 700; - font-size: 0.9rem; - line-height: 1; -} - -.float-date { - font-size: 0.75rem; - opacity: 0.9; - font-weight: 500; -} - -.pulse-ring { - position: absolute; - top: -10px; - right: -10px; - width: 20px; - height: 20px; - border: 3px solid #f1c40f; - border-radius: 50%; - animation: pulse-ring 2s infinite; -} - -/* Animations */ -@keyframes pulse-glow { - 0%, 100% { - box-shadow: 0 0 20px rgba(236, 55, 80, 0.5); - } - 50% { - box-shadow: 0 0 30px rgba(236, 55, 80, 0.8); - } -} - -@keyframes shine { - 0% { left: -100%; } - 100% { left: 100%; } -} - -@keyframes float-bounce { - 0%, 100% { transform: translateY(0); } - 50% { transform: translateY(-10px); } -} - -@keyframes pulse-ring { - 0% { - transform: scale(0.8); - opacity: 1; - } - 100% { - transform: scale(2); - opacity: 0; - } -} - -/* Responsive Design for HackClub Event */ -@media (max-width: 768px) { - .event-container { - padding: 2rem 1rem; - } - - .event-header { - flex-direction: column; - align-items: flex-start; - gap: 1rem; - } - - .event-title { - font-size: 2rem; - } - - .event-details { - grid-template-columns: 1fr; - } - - .event-cta { - flex-direction: column; - } - - .hackclub-floating-btn { - bottom: 80px; - left: 20px; - padding: 0.75rem 1rem; - max-width: 160px; - } - - .float-logo { - width: 32px; - height: 32px; - } - - .float-title { - font-size: 0.8rem; - } - - .float-date { - font-size: 0.7rem; - } -} - -@media (max-width: 480px) { - .hackclub-floating-btn { - bottom: 70px; - left: 15px; - padding: 0.5rem 0.75rem; - max-width: 140px; - } - - .hackclub-highlight { - font-size: 0.9rem; - } -} - -/* Events Section Styles */ -.hackclub-featured { - border: 2px solid transparent; - background: linear-gradient(135deg, #FFFFFF, #F8F9FA) padding-box, - linear-gradient(135deg, var(--ada-yellow), var(--ada-orange)) border-box; - position: relative; - overflow: hidden; -} - -.hackclub-featured::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: linear-gradient(135deg, rgba(236, 55, 80, 0.05), rgba(255, 140, 55, 0.05)); - pointer-events: none; -} - -.event-meta { - display: flex; - flex-direction: column; - gap: 0.5rem; - margin: 1rem 0; - font-size: 0.9rem; -} - -.event-meta span { - color: #8492a6; - font-weight: 500; -} - -.event-register-link { - background: linear-gradient(135deg, #ec3750, #ff8c37); - background-clip: text; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - font-weight: 700; -} - -.event-register-link:hover { - background: linear-gradient(135deg, #ff8c37, #f1c40f); - background-clip: text; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; -} - -/* Event Documentation Styles */ -.event-what-is { - background: rgba(255, 255, 255, 0.9); - border: 2px solid var(--ada-yellow); - border-radius: 16px; - padding: 2rem; - margin: 2rem 0; - box-shadow: 0 4px 20px rgba(255, 210, 63, 0.2); -} - -.event-what-is h3 { - color: var(--ada-purple); - font-size: 1.5rem; - font-weight: 700; - margin-bottom: 1rem; -} - -.event-what-is h4 { - color: var(--ada-blue); - font-size: 1.2rem; - font-weight: 600; - margin: 1.5rem 0 1rem 0; -} - -.event-what-is p { - color: #333; - line-height: 1.6; - margin-bottom: 1.5rem; -} - -.activity-list, .skill-levels { - list-style: none; - padding: 0; - margin: 1rem 0; -} - -.activity-list li, .skill-levels li { - background: rgba(255, 210, 63, 0.1); - border: 1px solid var(--ada-yellow); - border-radius: 8px; - padding: 0.75rem 1rem; - margin: 0.5rem 0; - color: #333; - transition: all 0.3s ease; -} - -.activity-list li:hover, .skill-levels li:hover { - background: var(--ada-yellow); - color: #333; - transform: translateX(5px); -} - -.skill-levels li strong { - color: #ff8c37; - font-weight: 700; -} - -/* Mobile Navigation Improvements */ -.nav-toggle { - display: none; - background: none; - border: none; - font-size: 1.5rem; - color: var(--hc-snow); - cursor: pointer; - padding: 0.5rem; - transition: all 0.3s ease; - z-index: 1001; -} - -.nav-toggle:hover { - color: var(--hc-red); - transform: scale(1.1); -} - -.nav-toggle.active { - color: var(--hc-red); -} - -/* Navbar scroll effect */ -.navbar.scrolled { - background: rgba(255, 255, 255, 0.98); - backdrop-filter: blur(20px); - box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1); - border-bottom: 3px solid var(--ada-blue); -} -.navbar.scrolled .brand-text{ - font-family: var(--font-display); - font-size: 1.5rem; - font-weight: 800; - - background: var(--ada-dark-gray); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; - line-height: 1; -} -.navbar.scrolled img{ - filter: invert(1); -} -/* Prevent body scroll when nav is open */ -body.nav-open { - overflow: hidden; -} - -/* Additional responsive improvements */ -@media (max-width: 480px) { - .hero-title { - font-size: clamp(2rem, 8vw, 3rem); - } - - .hero-subtitle { - font-size: 1rem; - } - - .hero-badge { - font-size: 0.75rem; - padding: var(--space-2) var(--space-3); - } - - .section-title { - font-size: clamp(1.8rem, 6vw, 2.5rem); - } - - .stats-grid { - grid-template-columns: repeat(2, 1fr); - gap: var(--space-3); - } - - .stat-number { - font-size: 2rem; - } - - .features-grid { - grid-template-columns: 1fr; - gap: var(--space-3); - } - - .feature-card { - padding: var(--space-4); - } -} - -@media (max-width: 768px) { - .nav-toggle { - display: block; - } - - .nav-menu { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100vh; - background: linear-gradient(135deg, #FFFFFF 0%, #F8F9FA 50%, #FFFFFF 100%); - flex-direction: column; - justify-content: center; - align-items: center; - gap: 2rem; - transform: translateX(-100%); - transition: transform 0.3s ease; - z-index: 1000; - padding: 0; - margin: 0; - list-style: none; - } - - .nav-menu.active { - transform: translateX(0); - } - - .nav-menu li { - margin: 0; - } - - .nav-menu .nav-link { - font-size: 1.5rem; - padding: 1rem 2rem; - border-radius: 12px; - transition: all 0.3s ease; - background: rgba(255, 255, 255, 0.05); - border: 1px solid rgba(255, 255, 255, 0.1); - min-width: 200px; - text-align: center; - } - - .nav-menu .nav-link:hover, - .nav-menu .nav-link.active { - background: linear-gradient(135deg, var(--hc-red), var(--hc-orange)); - transform: scale(1.05); - box-shadow: 0 8px 32px rgba(236, 55, 80, 0.3); - } - - .nav-menu .hackclub-highlight { - background: linear-gradient(135deg, #ec3750, #ff8c37) !important; - color: white !important; - -webkit-text-fill-color: white !important; - font-weight: 700; - animation: pulse-glow 2s infinite; - } - - .nav-brand { - z-index: 1001; - position: relative; - } -} - -/* Event Details Compact */ -.event-details-compact { - background: rgba(255, 210, 63, 0.1); - border-radius: 12px; - padding: 1.5rem; - margin: 1rem 0; - border: 1px solid var(--ada-yellow); -} - -.event-details-compact p { - margin: 0.75rem 0; - font-size: 0.9rem; - line-height: 1.5; - color: #333; -} - -.event-details-compact strong { - color: var(--ada-blue); - font-weight: 600; -} - -/* News & Updates Section - Ada Style Enhancement */ -.news-section { - background: linear-gradient(135deg, var(--ada-light-gray) 0%, #FFFFFF 50%, var(--ada-light-gray) 100%); - padding: 4rem 2rem; - margin: 2rem 0; - border-radius: 24px; - max-width: 1200px; - margin: 2rem auto; - border: 1px solid rgba(0, 0, 0, 0.05); - position: relative; - overflow: hidden; -} - -.news-section::before { - content: ''; - position: absolute; - top: 0; - right: 0; - width: 300px; - height: 300px; - background: linear-gradient(135deg, var(--ada-orange), var(--ada-pink)); - clip-path: polygon(100% 0, 100% 60%, 40% 0); - opacity: 0.1; -} - -.news-container { - max-width: 1000px; - margin: 0 auto; -} - -.news-content { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 3rem; - align-items: start; -} - -.twitter-timeline-container { - background: rgba(66, 165, 245, 0.1); - border-radius: 16px; - padding: 2rem; - border: 1px solid var(--ada-blue); - box-shadow: 0 4px 20px rgba(66, 165, 245, 0.2); -} - -.timeline-title { - color: #1DA1F2; - font-size: 1.5rem; - font-weight: 700; - margin-bottom: 1.5rem; - display: flex; - align-items: center; - gap: 0.75rem; -} - -.timeline-title i { - font-size: 1.3rem; -} - -.twitter-embed { - border-radius: 12px; - overflow: hidden; - background: #000; -} - -.quick-links { - background: rgba(186, 104, 200, 0.1); - border-radius: 16px; - padding: 2rem; - border: 1px solid var(--ada-purple); - box-shadow: 0 4px 20px rgba(186, 104, 200, 0.2); -} - -.quick-links-title { - color: var(--ada-purple); - font-size: 1.5rem; - font-weight: 700; - margin-bottom: 1.5rem; - display: flex; - align-items: center; - gap: 0.75rem; -} - -.quick-links-title i { - font-size: 1.3rem; -} - -.update-items { - display: flex; - flex-direction: column; - gap: 1.5rem; -} - -.update-item { - display: flex; - gap: 1rem; - background: rgba(124, 179, 66, 0.1); - border-radius: 12px; - padding: 1.5rem; - border: 1px solid var(--ada-green); - transition: all 0.3s ease; -} - -.update-item:hover { - background: rgba(124, 179, 66, 0.2); - transform: translateY(-2px); - box-shadow: 0 8px 32px rgba(124, 179, 66, 0.3); -} - -.update-icon { - background: linear-gradient(135deg, var(--hc-red), var(--hc-orange)); - width: 50px; - height: 50px; - border-radius: 12px; - display: flex; - align-items: center; - justify-content: center; - color: white; - font-size: 1.2rem; - flex-shrink: 0; -} - -.update-content h4 { - color: #333; - font-size: 1.1rem; - font-weight: 600; - margin-bottom: 0.5rem; -} - -.update-content p { - color: #8492a6; - font-size: 0.9rem; - line-height: 1.5; - margin-bottom: 1rem; -} - -.update-link { - display: inline-flex; - align-items: center; - gap: 0.5rem; - color: var(--hc-red); - text-decoration: none; - font-weight: 600; - font-size: 0.9rem; - transition: all 0.3s ease; -} - -.update-link:hover { - color: var(--hc-orange); - transform: translateX(5px); -} - -.update-link i { - font-size: 0.8rem; -} - -/* Responsive Design for News Section */ -@media (max-width: 768px) { - .news-section { - padding: 2rem 1rem; - margin: 1rem; - border-radius: 16px; - } - - .news-content { - grid-template-columns: 1fr; - gap: 2rem; - } - - .twitter-timeline-container, - .quick-links { - padding: 1.5rem; - } - - .timeline-title, - .quick-links-title { - font-size: 1.3rem; - } - - .update-item { - padding: 1rem; - } - - .update-icon { - width: 40px; - height: 40px; - font-size: 1rem; - } -} - -@media (max-width: 480px) { - .news-section { - padding: 1.5rem 0.75rem; - } - - .twitter-timeline-container, - .quick-links { - padding: 1rem; - } - - .update-item { - flex-direction: column; - text-align: center; - gap: 1rem; - } - - .update-icon { - align-self: center; - } -} - - /* About */ - .about-stats { - grid-template-columns: 1fr; - gap: var(--space-lg); - } - - .rocket-diagram { - width: 150px; - height: 225px; - margin: var(--space-xl) auto; - } - - /* Mission */ - .mission-grid { - grid-template-columns: 1fr; - gap: var(--space-xl); - } - - .mission-card { - padding: var(--space-xl); - } - - .card-icon { - width: 60px; - height: 60px; - } - - /* Projects */ - .projects-grid { - grid-template-columns: 1fr; - gap: var(--space-xl); - } - - .project-card { - padding: var(--space-xl); - } - - .project-header { - flex-direction: column; - align-items: flex-start; - gap: var(--space-sm); - } - - /* Community */ - .community-links { - grid-template-columns: 1fr; - gap: var(--space-lg); - } - - .community-card { - flex-direction: column; - text-align: center; - padding: var(--space-xl); - } - - /* Contact */ - .contact-form { - padding: var(--space-xl); - } - - /* Footer */ - .footer-content { - grid-template-columns: 1fr; - gap: var(--space-xl); - text-align: center; - } - - .footer-brand { - flex-direction: column; - gap: var(--space-md); - text-align: center; - } - - .footer-links { - grid-template-columns: 1fr; - gap: var(--space-lg); - } -} - -/* Small Devices (Landscape Phones) */ -@media (min-width: 576px) and (max-width: 767.98px) { - /* Hero */ - .hero-buttons { - flex-direction: row; - justify-content: center; - flex-wrap: wrap; - } - - .btn { - width: auto; - min-width: 160px; - } - - /* About */ - .about-stats { - grid-template-columns: repeat(2, 1fr); - } - - /* Mission */ - .mission-grid { - grid-template-columns: 1fr; - } - - /* Projects */ - .projects-grid { - grid-template-columns: 1fr; - } - - /* Community */ - .community-links { - grid-template-columns: 1fr; - } - - /* Footer */ - .footer-links { - grid-template-columns: repeat(2, 1fr); - } -} - -/* Medium Devices (Tablets) */ -@media (min-width: 768px) and (max-width: 991.98px) { - /* Navigation */ - .nav-container { - padding: 0 var(--space-lg); - } - - /* Hero */ - .hero-content { - max-width: 700px; - } - - /* About */ - .about-content { - grid-template-columns: 1fr 1fr; - gap: var(--space-2xl); - align-items: center; - } - - .about-stats { - grid-template-columns: repeat(3, 1fr); - } - - /* Mission */ - .mission-grid { - grid-template-columns: repeat(2, 1fr); - gap: var(--space-xl); - } - - /* Projects */ - .projects-grid { - grid-template-columns: repeat(2, 1fr); - } - - /* Community */ - .community-content { - grid-template-columns: 1fr 1fr; - gap: var(--space-2xl); - } - - .community-links { - grid-template-columns: 1fr; - } - - /* Contact */ - .contact-content { - grid-template-columns: 1fr 1fr; - gap: var(--space-2xl); - } - - /* Footer */ - .footer-content { - grid-template-columns: 1fr 2fr; - } - - .footer-links { - grid-template-columns: repeat(3, 1fr); - } -} - -/* Large Devices (Small Desktops) */ -@media (min-width: 992px) and (max-width: 1199.98px) { - /* Mission */ - .mission-grid { - grid-template-columns: repeat(3, 1fr); - } - - /* Projects */ - .projects-grid { - grid-template-columns: repeat(2, 1fr); - } - - /* Community */ - .community-links { - grid-template-columns: repeat(2, 1fr); - } -} - -/* Extra Large Devices (Large Desktops) */ -@media (min-width: 1200px) { - /* Container */ - .container { - max-width: 1200px; - padding: 0 var(--space-xl); - } - - /* Projects */ - .projects-grid { - grid-template-columns: repeat(3, 1fr); - } - - /* Community */ - .community-links { - grid-template-columns: repeat(2, 1fr); - } -} - -/* Height-based Media Queries for Short Screens */ -@media (max-height: 600px) and (orientation: landscape) { - .hero { - min-height: 100vh; - padding: var(--space-xl) 0; - } - - .hero-content { - padding: var(--space-lg); - } - - .hero-title { - font-size: var(--text-4xl); - margin-bottom: var(--space-md); - } - - .hero-subtitle { - font-size: var(--text-base); - margin-bottom: var(--space-lg); - } - - .hero-buttons { - gap: var(--space-md); - } - - section { - padding: var(--space-2xl) 0; - } -} - -/* Print Styles */ -@media print { - * { - background: transparent !important; - color: black !important; - box-shadow: none !important; - text-shadow: none !important; - } - - body { - font-size: 12pt; - line-height: 1.4; - } - - .navbar, - .nav-toggle, - .hero-background, - .floating-elements, - .contact-form, - .footer { - display: none !important; - } - - .hero { - min-height: auto; - padding: 2rem 0; - } - - .section-header { - margin-bottom: 1rem; - } - - section { - padding: 1rem 0; - page-break-inside: avoid; - } - - h1, h2, h3 { - page-break-after: avoid; - } - - .mission-grid, - .projects-grid { - grid-template-columns: 1fr; - gap: 1rem; - } - - .btn { - display: none; - } - - a[href^="http"]:after { - content: " (" attr(href) ")"; - font-size: 0.8em; - color: #666; - } -} - -/* Reduced Motion */ -@media (prefers-reduced-motion: reduce) { - *, - *::before, - *::after { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - scroll-behavior: auto !important; - } - - .stars { - animation: none; - } - - .rocket-icon { - animation: none; - } - - .rocket-parts > * { - animation: none; - } -} - -/* High Contrast Mode */ -@media (prefers-contrast: high) { - :root { - --primary-color: #f5f5f5; - --primary-light: #fafafa; - --accent-color: #00ff88; - --white: #ffffff; - --gray-300: #cccccc; - --gray-400: #999999; - --bg-primary: #000000; - --bg-secondary: #111111; - --bg-tertiary: #222222; - } - - .btn { - border: 2px solid currentColor; - } - - .mission-card, - .project-card, - .community-card, - .contact-form { - border: 2px solid rgba(255, 255, 255, 0.3); - } -} - -/* Dark Mode Override (if user prefers light) */ -@media (prefers-color-scheme: light) { - /* Keep dark theme as it's part of the design */ - /* This could be extended if a light mode is desired */ -} - -/* Touch Device Optimizations */ -@media (hover: none) and (pointer: coarse) { - /* Increase touch targets */ - .btn { - min-height: 44px; - padding: var(--space-md) var(--space-xl); - } - - .nav-link { - padding: var(--space-lg) var(--space-md); - } - - .community-card, - .project-card, - .mission-card { - padding: var(--space-xl); - }} - -/* Footer */ -.footer { - background: var(--bg-primary); - border-top: 1px solid rgba(255, 255, 255, 0.1); - position: relative; -} - -.footer::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 1px; - background: linear-gradient(90deg, transparent, var(--primary-color), transparent); -} - -.footer-content { - display: grid; - grid-template-columns: 1fr; - gap: var(--space-2xl); - padding: var(--space-3xl) 0 var(--space-xl); -} - -.footer-brand { - display: flex; - align-items: center; - gap: var(--space-lg); -} - -.footer-brand .logo { - color: var(--primary-light); -} - -.footer-brand h4 { - color: var(--white); - margin-bottom: var(--space-xs); -} - -.footer-brand p { - color: var(--gray-400); - margin-bottom: 0; - font-size: var(--text-sm); -} - -.footer-links { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - gap: var(--space-xl); -} - -.footer-section h5 { - color: var(--white); - font-size: var(--text-lg); - margin-bottom: var(--space-lg); -} - -.footer-section ul { - list-style: none; - padding: 0; - margin: 0; -} - -.footer-section li { - margin-bottom: var(--space-sm); -} - -.footer-section a { - color: var(--gray-400); - text-decoration: none; - font-size: var(--text-sm); - transition: color var(--transition-base); -} - -.footer-section a:hover { - color: var(--primary-light); -} - -.footer-section span { - color: var(--gray-500); - font-size: var(--text-sm); -} - -.footer-bottom { - border-top: 1px solid rgba(255, 255, 255, 0.1); - padding: var(--space-xl) 0; - text-align: left; -} - -.footer-bottom p { - color: var(--gray-500); - font-size: var(--text-sm); - margin-bottom: 0; -} - -/* Mobile Optimizations */ -@media (min-width: 768px) { - .about-content { - grid-template-columns: 1fr 1fr; - } - - .community-content { - grid-template-columns: 1fr 1fr; - } - - .contact-content { - grid-template-columns: 1fr 1fr; - } - - .footer-content { - grid-template-columns: 2fr 3fr; - } -} - -@media (min-width: 1024px) { - .footer-links { - grid-template-columns: repeat(3, 1fr); - } -} - -/* Geometric Section Styles - Ada Inspired Layout */ -.geometric-section { - position: relative; - overflow: hidden; -} - -/* Split Screen Sections */ -.split-section { - display: grid; - grid-template-columns: 1fr 1fr; - min-height: 70vh; - position: relative; - overflow: hidden; -} - -.split-left { - background: var(--ada-green); - padding: var(--space-7) var(--space-5); - display: flex; - flex-direction: column; - justify-content: center; - position: relative; - color: var(--ada-dark-gray); -} - -.split-right { - background: var(--ada-blue); - padding: var(--space-7) var(--space-5); - display: flex; - flex-direction: column; - justify-content: center; - position: relative; - color: var(--ada-dark-gray); -} - -/* Angular Geometric Dividers */ -.angular-divider { - position: relative; - height: 150px; - background: linear-gradient(135deg, var(--ada-purple) 0%, var(--ada-orange) 100%); - overflow: hidden; - z-index: 1; -} - -.angular-divider::before { - content: ''; - position: absolute; - top: -75px; - left: -10%; - width: 30%; - height: 300px; - background: var(--ada-blue); - transform: rotate(15deg); - opacity: 0.9; -} - -.angular-divider::after { - content: ''; - position: absolute; - bottom: -75px; - right: -10%; - width: 30%; - height: 300px; - background: var(--ada-yellow); - transform: rotate(-15deg); - opacity: 0.9; -} - -/* Testimonial/Partnership Section Styling */ -.partnership-section { - background: var(--ada-light-gray); - padding: var(--space-7) 0; - position: relative; -} - -.partnership-section::before { - content: ''; - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 4px; - background: linear-gradient(90deg, var(--ada-green), var(--ada-blue), var(--ada-yellow), var(--ada-purple)); -} - -.partnership-content { - background: white; - padding: var(--space-6); - border-radius: var(--radius-lg); - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08); - position: relative; - margin: 0 var(--space-4); - max-width: 1200px; - margin-left: auto; - margin-right: auto; -} - -.partnership-content::after { - content: ''; - position: absolute; - top: 0; - right: 0; - width: 200px; - height: 200px; - background: linear-gradient(135deg, var(--ada-yellow), var(--ada-orange)); - clip-path: polygon(100% 0, 100% 100%, 0 0); -} - -/* Split Section Content Styling */ -.split-content h2 { - font-size: clamp(2rem, 4vw, 3rem); - font-weight: 800; - margin-bottom: var(--space-4); - line-height: 1.2; -} - -.split-content p { - font-size: 1.2rem; - line-height: 1.6; - margin-bottom: var(--space-4); - opacity: 0.9; -} - -.split-content .btn { - background: rgba(255, 255, 255, 0.2); - color: var(--ada-dark-gray); - border: 2px solid rgba(255, 255, 255, 0.3); - backdrop-filter: blur(10px); -} - -.split-content .btn:hover { - background: rgba(255, 255, 255, 0.3); - transform: translateY(-2px); -} - -/* Mobile Responsive for Geometric Sections */ -@media (max-width: 768px) { - .split-section { - grid-template-columns: 1fr; - min-height: auto; - } - - .split-left, - .split-right { - padding: var(--space-5) var(--space-4); - min-height: 50vh; - } - - .angular-divider { - height: 100px; - } - - .angular-divider::before, - .angular-divider::after { - width: 60%; - height: 200px; - } - - .partnership-content { - margin: 0 var(--space-3); - padding: var(--space-4); - } - - .partnership-content::after { - width: 100px; - height: 100px; - } -} - -/* Final Visual Enhancements for Perfect Ada Style Match */ -.update-item { - background: rgba(255, 255, 255, 0.9) !important; - border: 1px solid rgba(0, 0, 0, 0.05) !important; - color: var(--ada-dark-gray) !important; -} - -.update-content h4 { - color: var(--ada-dark-gray) !important; -} - -.update-content p { - color: var(--ada-dark-gray) !important; - opacity: 0.8 !important; -} - -/* Ensure all text in news section is properly colored */ -.news-container .update-content h4, -.news-container .update-content p, -.news-container .timeline-title, -.news-container .quick-links-title { - color: var(--ada-dark-gray) !important; -} - -.twitter-embed { - background: white !important; - border-radius: 12px !important; - overflow: hidden !important; -} - -/* Fix Footer Visibility Issues */ -.footer { - background: var(--ada-dark-gray) !important; - color: var(--ada-light-gray) !important; -} - -.footer-section h4, -.footer-section h5 { - color: var(--ada-yellow) !important; -} - -.footer-section a, -.footer-section span { - color: var(--ada-light-gray) !important; - opacity: 0.9; -} - -.footer-section a:hover { - color: var(--ada-green) !important; - opacity: 1; -} - -.footer-bottom p { - color: var(--ada-light-gray) !important; - opacity: 0.7; -} - diff --git a/styles/index.css b/styles/index.css deleted file mode 100644 index e69de29..0000000 diff --git a/styles/inter.css b/styles/inter.css deleted file mode 100644 index d0bd7b7..0000000 --- a/styles/inter.css +++ /dev/null @@ -1,395 +0,0 @@ - @import url('https://fonts.googleapis.com/css2?family=Cal+Sans&display=swap'); - -:root { - --primary-bg: #10131a; - --secondary-bg: #181c23; - --card-bg: #181c23; - --accent: rgb(255, 0, 85); - --accent2: #1b1b1b; - --accent3: #161616; - --text-main: #e2e8f0; - --text-muted: #080808; - --border: #23272f; - --radius: 1.2rem; - --shadow: 0 4px 32px rgba(30, 144, 255, 0.10); -} - -html, body { - margin: 0; - padding: 0; - min-height: 100vh; - background: var(--primary-bg); - color: var(--text-main); -font-family: "Cal Sans", sans-serif; - font-size: 1rem; - scroll-behavior: smooth; -} - -/* Header */ -header { - background: var(--secondary-bg); - box-shadow: 0 2px 12px rgba(0,0,0,0.10); - border-radius: 0 0 var(--radius) var(--radius); - display: flex; - justify-content: space-between; - align-items: center; - padding: 1.2rem 1.5rem; - position: relative; - z-index: 10; -} -header img { - width: 5.5rem; - height: auto; - filter: drop-shadow(0 2px 8px #0008); -} -header nav { - display: flex; - gap: 2rem; -} -header nav a { - color: var(--text-muted); - text-decoration: none; - font-weight: 500; - font-size: 1.1rem; - letter-spacing: 0.01em; - transition: color 0.2s; - position: relative; -} -header nav a:hover, -header nav a.active, -header nav a[aria-current="page"] { - color: var(--accent); -} -header nav a.text-blue-400 { - color: var(--accent); - text-decoration: underline; -} -header .flex.items-center { - gap: 1rem; -} -header .text-2xl { - font-size: 1.5rem; -} - -/* Social icons */ -header .text-gray-400 { - color: #8b98b1; -} -header .text-gray-400:hover { - color: var(--accent); -} -a{ - color: white; -} -/* Mobile Nav */ -#menu-button { - display: none; - background: none; - border: none; - cursor: pointer; -} -#mobile-menu { - display: none; -} -#mobile-menu.show { - display: block; - animation: fadeIn 0.4s; -} -#mobile-menu nav { - display: flex; - flex-direction: column; - gap: 1rem; -} -#mobile-menu a { - color: var(--text-main); - font-size: 1.1rem; - padding: 0.5rem 0; - border-bottom: 1px solid var(--border); -} -#mobile-menu a:last-child { - border-bottom: none; -} - -/* Responsive Header */ -@media (max-width: 900px) { - header nav { - display: none; - } - #menu-button { - display: inline-block; - } -} - -/* Main Container */ -main { - max-width: 600px; - margin: 0 auto; - padding: 2.5rem 1rem 1rem 1rem; -} - -/* Hero Section */ -.hero-section { - background: linear-gradient(120deg, #1a2230 0%, #181c23 100%); - border-radius: var(--radius); - box-shadow: var(--shadow); - position: relative; - overflow: hidden; - padding: 3rem 1.5rem 2.5rem 1.5rem; - margin-bottom: 2.5rem; -} -.hero-section .fa-user-plus { - position: absolute; - top: 50%; - left: 50%; - font-size: 10rem; - color: var(--accent2); - opacity: 0.10; - transform: translate(-50%, -50%); - pointer-events: none; -} -.hero-section h1 { - position: relative; - z-index: 1; - font-size: 2.7rem; - font-weight: 800; - margin-bottom: 1.1rem; - letter-spacing: 0.01em; - color: #fff; - text-shadow: 0 2px 16px #0006; -} -.hero-section p { - position: relative; - z-index: 1; - color: var(--text-muted); - font-size: 1.25rem; - margin-bottom: 0; - max-width: 32rem; - margin-left: auto; - margin-right: auto; -} - -/* Form Section */ -section.bg-gray-800 { - background: var(--card-bg); - border-radius: var(--radius); - box-shadow: var(--shadow); - padding: 2rem 1.5rem; - margin-bottom: 2rem; -} -section.bg-gray-800 h2 { - font-size: 2rem; - font-weight: 700; - margin-bottom: 1.5rem; - letter-spacing: 0.01em; -} -form { - display: flex; - flex-direction: column; - gap: 1.2rem; -} -form label { - color: var(--text-muted); - font-size: 1rem; - margin-bottom: 0.3rem; -} -form input[type="text"], -form input[type="email"] { - background: #13161e; - color: var(--text-main); - border: 1.5px solid var(--border); - border-radius: 0.7rem; - padding: 0.8rem 1rem; - font-size: 1rem; - transition: border 0.2s, box-shadow 0.2s; - width: 100%; - box-sizing: border-box; -} -form input[type="text"]:focus, -form input[type="email"]:focus { - border-color: var(--accent); - outline: none; - box-shadow: 0 0 0 2px #38e8ff33; -} -.join-btn { - padding: 1rem 2.5rem; - font-size: 1.15rem; - border: none; - border-radius: 2rem; - background: linear-gradient(90deg, var(--accent2) 0%, var(--accent) 100%); - color: #fff; - font-weight: 700; - box-shadow: 0 4px 24px rgba(79, 140, 255, 0.15); - cursor: pointer; - transition: transform 0.15s, box-shadow 0.15s, background 0.2s; - letter-spacing: 0.03em; - margin-top: 0.5rem; - text-shadow: 0 2px 8px #0004; -} - -form + p { - color: var(--text-muted); - font-size: 0.97rem; - margin-top: 1rem; - text-align: center; -} - -/* Other Ways to Join */ -section.text-center h3 { - font-size: 1.3rem; - font-weight: 600; - margin-bottom: 1rem; - letter-spacing: 0.01em; -} -section.text-center .flex { - gap: 1rem; - flex-wrap: wrap; - justify-content: center; -} -section.text-center a { - display: inline-flex; - align-items: center; - gap: 0.7rem; - background: #23272f; - color: #fff; - font-weight: 600; - padding: 0.8rem 1.7rem; - border-radius: 0.8rem; - box-shadow: 0 2px 10px rgba(56, 232, 255, 0.07); - text-decoration: none; - font-size: 1.1rem; - transition: background 0.2s, transform 0.2s, color 0.2s; -} -section.text-center a.bg-blue-600 { - background: #23272f; - color: #fff; -} -section.text-center a:hover { - background: var(--accent3); - color: #fff; - transform: scale(1.04); -} - -/* Footer */ -footer { - background: var(--secondary-bg); - color: var(--text-muted); - border-radius: var(--radius) var(--radius) 0 0; - box-shadow: 0 -2px 12px rgba(0,0,0,0.10); - padding: 2rem 1rem 1rem 1rem; - margin-top: 2rem; - text-align: center; -} -footer .flex { - gap: 1.5rem; - justify-content: center; - margin-bottom: 1rem; -} -footer nav { - display: flex; - flex-wrap: wrap; - gap: 1.2rem; - justify-content: center; -} -footer nav a { - color: var(--text-muted); - font-size: 0.97rem; - text-decoration: none; - transition: color 0.2s; -} - - - -/* Animations */ -@keyframes fadeIn { - from { opacity: 0; transform: translateY(20px);} - to { opacity: 1; transform: translateY(0);} -} -.animated-fade-in { - animation: fadeIn 1s ease-out forwards; - opacity: 0; -} -@keyframes slideInLeft { - from { opacity: 0; transform: translateX(-20px);} - to { opacity: 1; transform: translateX(0);} -} -.animated-slide-in-left { - animation: slideInLeft 1s ease-out forwards; - opacity: 0; - animation-delay: 0.3s; -} -@keyframes slideInRight { - from { opacity: 0; transform: translateX(20px);} - to { opacity: 1; transform: translateX(0);} -} -.animated-slide-in-right { - animation: slideInRight 1s ease-out forwards; - opacity: 0; - animation-delay: 0.6s; -} - -/* Utility Classes */ -.text-center { text-align: center; } -.flex { display: flex; } -.items-center { align-items: center; } -.justify-center { justify-content: center; } -.space-x-4 > * + * { margin-left: 1rem; } -.space-x-6 > * + * { margin-left: 1.5rem; } -.space-y-6 > * + * { margin-top: 1.5rem; } -.rounded-xl { border-radius: var(--radius); } -.shadow-lg { box-shadow: 0 4px 24px rgba(56, 232, 255, 0.07);} -.shadow-2xl { box-shadow: 0 8px 40px rgba(56, 232, 255, 0.12);} -.shadow-inner { box-shadow: 0 -2px 12px rgba(0,0,0,0.08);} -.bg-gray-800 { background: var(--secondary-bg);} -.bg-gray-900 { background: var(--primary-bg);} -.text-gray-100 { color: var(--text-main);} -.text-gray-300 { color: var(--text-muted);} -.text-blue-400 { color: var(--accent);} -.font-semibold { font-weight: 600;} -.font-bold { font-weight: 700;} -.font-extrabold { font-weight: 800;} -.leading-tight { line-height: 1.1;} -.max-w-2xl { max-width: 42rem; } -.max-w-3xl { max-width: 56rem; } -.mx-auto { margin-left: auto; margin-right: auto; } -.px-6 { padding-left: 1.5rem; padding-right: 1.5rem; } -.py-8 { padding-top: 2rem; padding-bottom: 2rem; } -.py-12 { padding-top: 3rem; padding-bottom: 3rem; } -.py-16 { padding-top: 4rem; padding-bottom: 4rem; } -.py-24 { padding-top: 6rem; padding-bottom: 6rem; } -.mb-4 { margin-bottom: 1rem; } -.mb-6 { margin-bottom: 1.5rem; } -.mb-8 { margin-bottom: 2rem; } -.mb-12 { margin-bottom: 3rem; } -.mt-4 { margin-top: 1rem; } -.mt-8 { margin-top: 2rem; } -.w-full { width: 100%; } -.container { width: 100%; max-width: 1200px; margin: 0 auto; } - -@media (max-width: 700px) { - main, .max-w-2xl, .max-w-3xl { - max-width: 100vw; - padding: 0 0.5rem; - } - .hero-section h1 { - font-size: 2rem; - } - .hero-section .fa-user-plus { - font-size: 5rem; - } - section.bg-gray-800 { - padding: 1.2rem 0.5rem; - } - .join-btn { - font-size: 1rem; - padding: 0.8rem 1.2rem; - } -} - -/* Hide scrollbars on mobile nav overlay */ -#mobile-menu { - scrollbar-width: none; - -ms-overflow-style: none; -} -#mobile-menu::-webkit-scrollbar { - display: none; -} \ No newline at end of file diff --git a/styles/lms-main.css b/styles/lms-main.css deleted file mode 100644 index febf98c..0000000 --- a/styles/lms-main.css +++ /dev/null @@ -1,822 +0,0 @@ -/* ===== ROOT VARIABLES ===== */ -:root { - /* Primary Colors */ - --primary-bg: #0d1117; - --secondary-bg: #161b22; - --tertiary-bg: #21262d; - --card-bg: #1c2128; - --hover-bg: #262c36; - - /* Accent Colors */ - --accent-primary: #238636; - --accent-secondary: #1f6feb; - --accent-purple: #8b5cf6; - --accent-orange: #f59e0b; - - /* Text Colors */ - --text-primary: #f0f6fc; - --text-secondary: #8b949e; - --text-muted: #656d76; - - /* Border Colors */ - --border-primary: #30363d; - --border-secondary: #21262d; - - /* Gradients */ - --gradient-primary: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); - --gradient-purple: linear-gradient(135deg, var(--accent-purple), var(--accent-secondary)); - - /* Shadows */ - --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3); - --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4); - --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.5); - - /* Fonts */ - --font-primary: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; - --font-mono: 'Space Mono', 'Fira Code', monospace; -} - -/* ===== RESET & BASE STYLES ===== */ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -html { - scroll-behavior: smooth; -} - -body { - font-family: var(--font-primary); - background: var(--primary-bg); - color: var(--text-primary); - line-height: 1.6; - overflow-x: hidden; -} - -.container { - max-width: 1200px; - margin: 0 auto; - padding: 0 20px; -} - -/* ===== BUTTONS ===== */ -.btn-primary { - background: var(--gradient-primary); - color: white; - padding: 12px 24px; - border-radius: 8px; - text-decoration: none; - font-weight: 600; - transition: all 0.3s ease; - border: none; - cursor: pointer; - display: inline-flex; - align-items: center; - gap: 8px; -} - -.btn-primary:hover { - transform: translateY(-2px); - box-shadow: var(--shadow-md); - text-decoration: none; - color: white; -} - -.btn-primary.large { - padding: 16px 32px; - font-size: 1.1rem; -} - -.btn-secondary { - background: var(--secondary-bg); - color: var(--text-primary); - padding: 12px 24px; - border-radius: 8px; - text-decoration: none; - font-weight: 600; - transition: all 0.3s ease; - border: 1px solid var(--border-primary); -} - -.btn-secondary:hover { - background: var(--hover-bg); - text-decoration: none; - color: var(--text-primary); -} - -.btn-outline { - background: transparent; - color: var(--text-primary); - padding: 12px 24px; - border-radius: 8px; - text-decoration: none; - font-weight: 600; - transition: all 0.3s ease; - border: 2px solid var(--border-primary); -} - -.btn-outline:hover { - border-color: var(--accent-primary); - background: rgba(35, 134, 54, 0.1); - text-decoration: none; - color: var(--text-primary); -} - -.btn-outline.large { - padding: 16px 32px; - font-size: 1.1rem; -} - -/* ===== NAVIGATION ===== */ -.landing-nav { - position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 1000; - background: rgba(13, 17, 23, 0.95); - backdrop-filter: blur(20px); - border-bottom: 1px solid var(--border-primary); -} - -.nav-content { - max-width: 1200px; - margin: 0 auto; - padding: 0 20px; - display: flex; - align-items: center; - justify-content: space-between; - height: 70px; -} - -.nav-brand { - display: flex; - align-items: center; - gap: 12px; -} - -.logo { - height: 40px; - width: auto; -} - -.brand-text { - font-size: 1.25rem; - font-weight: 700; - color: var(--text-primary); -} - -.nav-links { - display: flex; - align-items: center; - gap: 20px; -} - -.nav-link { - color: var(--text-secondary); - text-decoration: none; - font-weight: 500; - transition: color 0.3s ease; -} - -.nav-link:hover { - color: var(--text-primary); - text-decoration: none; -} - -.mobile-menu-toggle { - display: none; - flex-direction: column; - cursor: pointer; - gap: 4px; -} - -.mobile-menu-toggle span { - width: 25px; - height: 3px; - background: var(--text-primary); - border-radius: 2px; - transition: all 0.3s ease; -} - -/* ===== HERO SECTION ===== */ -.hero { - min-height: 100vh; - display: flex; - align-items: center; - padding: 100px 0 50px; - position: relative; - overflow: hidden; -} - -.hero-content { - max-width: 1200px; - margin: 0 auto; - padding: 0 20px; - display: grid; - grid-template-columns: 1fr 1fr; - gap: 60px; - align-items: center; -} - -.hero-text h1 { - font-size: clamp(2.5rem, 5vw, 4rem); - font-weight: 700; - line-height: 1.2; - margin-bottom: 24px; - background: linear-gradient(135deg, var(--text-primary), var(--accent-primary)); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -.hero-text p { - font-size: 1.2rem; - color: var(--text-secondary); - margin-bottom: 32px; - line-height: 1.6; -} - -.hero-actions { - display: flex; - gap: 20px; - margin-bottom: 48px; - flex-wrap: wrap; -} - -.hero-stats { - display: flex; - gap: 40px; - flex-wrap: wrap; -} - -.stat { - display: flex; - flex-direction: column; - align-items: center; - text-align: center; -} - -.stat-number { - font-size: 2rem; - font-weight: 700; - color: var(--accent-primary); -} - -.stat-label { - font-size: 0.9rem; - color: var(--text-secondary); -} - -/* ===== HERO VISUAL ===== */ -.hero-visual { - position: relative; - height: 600px; -} - -.floating-elements { - position: relative; - width: 100%; - height: 100%; -} - -.floating-card { - position: absolute; - background: var(--card-bg); - border: 1px solid var(--border-primary); - border-radius: 12px; - padding: 20px; - box-shadow: var(--shadow-lg); - backdrop-filter: blur(20px); - animation: float 6s ease-in-out infinite; -} - -.floating-card.calendar-preview { - top: 50px; - left: 20px; - width: 280px; - animation-delay: -1s; -} - -.floating-card.community-preview { - top: 200px; - right: 40px; - width: 250px; - animation-delay: -3s; -} - -.floating-card.progress-preview { - bottom: 100px; - left: 60px; - width: 220px; - animation-delay: -5s; -} - -@keyframes float { - 0%, 100% { transform: translateY(0px) rotate(0deg); } - 50% { transform: translateY(-20px) rotate(1deg); } -} - -.card-header { - display: flex; - align-items: center; - gap: 10px; - margin-bottom: 16px; - color: var(--text-primary); - font-weight: 600; -} - -.card-header i { - color: var(--accent-primary); -} - -.class-item { - display: flex; - gap: 12px; - margin-bottom: 12px; - padding: 8px; - background: var(--secondary-bg); - border-radius: 8px; -} - -.class-time { - font-size: 0.85rem; - color: var(--accent-secondary); - font-weight: 600; - min-width: 60px; -} - -.class-title { - font-weight: 600; - color: var(--text-primary); - font-size: 0.9rem; -} - -.class-instructor { - font-size: 0.8rem; - color: var(--text-secondary); -} - -.community-post { - background: var(--secondary-bg); - padding: 12px; - border-radius: 8px; -} - -.post-author { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 8px; -} - -.avatar { - width: 24px; - height: 24px; - background: var(--accent-primary); - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-size: 0.7rem; - font-weight: 600; - color: white; -} - -.post-author span { - font-size: 0.85rem; - font-weight: 600; - color: var(--text-primary); -} - -.post-content { - font-size: 0.85rem; - color: var(--text-secondary); -} - -.progress-bar { - background: var(--secondary-bg); - height: 8px; - border-radius: 4px; - overflow: hidden; - margin-bottom: 8px; -} - -.progress-fill { - background: var(--gradient-primary); - height: 100%; - width: 75%; - border-radius: 4px; - transition: width 0.3s ease; -} - -.progress-text { - font-size: 0.85rem; - color: var(--text-secondary); -} - -/* ===== BACKGROUND ELEMENTS ===== */ -.bg-elements { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: -1; - opacity: 0.1; -} - -.bg-circle { - position: absolute; - border-radius: 50%; - background: var(--gradient-primary); - animation: pulse 4s ease-in-out infinite; -} - -.circle-1 { - width: 200px; - height: 200px; - top: 10%; - right: 10%; - animation-delay: -1s; -} - -.circle-2 { - width: 150px; - height: 150px; - bottom: 20%; - left: 5%; - animation-delay: -2s; -} - -.circle-3 { - width: 100px; - height: 100px; - top: 50%; - left: 50%; - animation-delay: -3s; -} - -@keyframes pulse { - 0%, 100% { opacity: 0.1; transform: scale(1); } - 50% { opacity: 0.3; transform: scale(1.1); } -} - -.code-lines { - position: absolute; - top: 30%; - right: 20%; - width: 200px; -} - -.code-line { - height: 4px; - background: var(--accent-secondary); - margin-bottom: 8px; - border-radius: 2px; - animation: typing 3s ease-in-out infinite; -} - -.code-line:nth-child(1) { width: 80%; animation-delay: -0.5s; } -.code-line:nth-child(2) { width: 60%; animation-delay: -1s; } -.code-line:nth-child(3) { width: 90%; animation-delay: -1.5s; } -.code-line:nth-child(4) { width: 70%; animation-delay: -2s; } - -@keyframes typing { - 0%, 100% { opacity: 0.3; } - 50% { opacity: 1; } -} - -/* ===== SECTIONS ===== */ -.features, .languages, .cta { - padding: 100px 0; -} - -.section-header { - text-align: center; - margin-bottom: 60px; -} - -.section-header h2 { - font-size: clamp(2rem, 4vw, 3rem); - font-weight: 700; - margin-bottom: 16px; - color: var(--text-primary); -} - -.section-header p { - font-size: 1.1rem; - color: var(--text-secondary); - max-width: 600px; - margin: 0 auto; -} - -/* ===== FEATURES GRID ===== */ -.features-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: 30px; -} - -.feature-card { - background: var(--card-bg); - border: 1px solid var(--border-primary); - border-radius: 16px; - padding: 32px; - text-align: center; - transition: all 0.3s ease; - position: relative; - overflow: hidden; -} - -.feature-card::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent); - transition: left 0.5s ease; -} - -.feature-card:hover { - transform: translateY(-8px); - box-shadow: var(--shadow-lg); - border-color: var(--accent-primary); -} - -.feature-card:hover::before { - left: 100%; -} - -.feature-icon { - width: 80px; - height: 80px; - background: var(--gradient-primary); - border-radius: 20px; - display: flex; - align-items: center; - justify-content: center; - margin: 0 auto 20px; - font-size: 2rem; - color: white; -} - -.feature-card h3 { - font-size: 1.5rem; - font-weight: 700; - margin-bottom: 16px; - color: var(--text-primary); -} - -.feature-card p { - color: var(--text-secondary); - line-height: 1.6; -} - -/* ===== LANGUAGES GRID ===== */ -.languages { - background: var(--secondary-bg); -} - -.languages-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 24px; -} - -.language-card { - background: var(--card-bg); - border: 1px solid var(--border-primary); - border-radius: 12px; - padding: 24px; - text-align: center; - transition: all 0.3s ease; -} - -.language-card:hover { - transform: translateY(-4px); - box-shadow: var(--shadow-md); -} - -.language-icon { - width: 60px; - height: 60px; - border-radius: 12px; - display: flex; - align-items: center; - justify-content: center; - margin: 0 auto 16px; - font-size: 1.5rem; - color: white; -} - -.language-icon.javascript { background: #f7df1e; color: #000; } -.language-icon.python { background: #3776ab; } -.language-icon.cpp { background: #00599c; } -.language-icon.react { background: #61dafb; color: #000; } -.language-icon.node { background: #339933; } -.language-icon.ai { background: var(--gradient-purple); } - -.language-card h3 { - font-size: 1.25rem; - font-weight: 600; - margin-bottom: 8px; - color: var(--text-primary); -} - -.language-card p { - font-size: 0.9rem; - color: var(--text-secondary); -} - -/* ===== CTA SECTION ===== */ -.cta { - background: var(--secondary-bg); - text-align: center; -} - -.cta-content h2 { - font-size: clamp(2rem, 4vw, 3rem); - font-weight: 700; - margin-bottom: 16px; - color: var(--text-primary); -} - -.cta-content p { - font-size: 1.1rem; - color: var(--text-secondary); - margin-bottom: 32px; -} - -.cta-actions { - display: flex; - gap: 20px; - justify-content: center; - flex-wrap: wrap; -} - -/* ===== FOOTER ===== */ -.footer { - background: var(--primary-bg); - border-top: 1px solid var(--border-primary); - padding: 60px 0 20px; -} - -.footer-content { - display: grid; - grid-template-columns: 1fr 2fr; - gap: 60px; - margin-bottom: 40px; -} - -.footer-brand { - max-width: 300px; -} - -.footer-logo { - height: 40px; - margin-bottom: 16px; -} - -.footer-brand p { - color: var(--text-secondary); - line-height: 1.6; -} - -.footer-links { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - gap: 40px; -} - -.footer-section h4 { - color: var(--text-primary); - font-weight: 600; - margin-bottom: 16px; -} - -.footer-section ul { - list-style: none; -} - -.footer-section ul li { - margin-bottom: 8px; -} - -.footer-section ul li a { - color: var(--text-secondary); - text-decoration: none; - transition: color 0.3s ease; -} - -.footer-section ul li a:hover { - color: var(--accent-primary); -} - -.footer-bottom { - text-align: center; - padding-top: 20px; - border-top: 1px solid var(--border-primary); - color: var(--text-muted); -} - -/* ===== RESPONSIVE DESIGN ===== */ -@media (max-width: 1024px) { - .hero-content { - grid-template-columns: 1fr; - text-align: center; - gap: 40px; - } - - .hero-visual { - height: 400px; - } -} - -@media (max-width: 768px) { - .nav-links { - display: none; - } - - .mobile-menu-toggle { - display: flex; - } - - .hero { - padding: 80px 0 30px; - } - - .hero-actions { - flex-direction: column; - align-items: center; - } - - .hero-stats { - justify-content: center; - } - - .floating-card { - position: relative; - margin-bottom: 20px; - top: auto !important; - left: auto !important; - right: auto !important; - bottom: auto !important; - } - - .floating-elements { - position: static; - height: auto; - display: flex; - flex-direction: column; - align-items: center; - } - - .features, .languages, .cta { - padding: 60px 0; - } - - .footer-content { - grid-template-columns: 1fr; - gap: 40px; - text-align: center; - } - - .footer-links { - grid-template-columns: repeat(2, 1fr); - } -} - -@media (max-width: 480px) { - .nav-content { - padding: 0 15px; - } - - .container { - padding: 0 15px; - } - - .hero-actions { - width: 100%; - } - - .btn-primary.large, - .btn-outline.large { - width: 100%; - justify-content: center; - } - - .cta-actions { - flex-direction: column; - align-items: center; - width: 100%; - } - - .footer-links { - grid-template-columns: 1fr; - } -} diff --git a/styles/main.css b/styles/main.css deleted file mode 100644 index b3842dd..0000000 --- a/styles/main.css +++ /dev/null @@ -1,573 +0,0 @@ -/* Reset and Base Styles */ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -:root { - /* Color Palette */ - --primary-color: #22ff00; - --primary-dark: #004499; - --primary-light: #000000; - --secondary-color: #ff6b35; - --secondary-dark: #e55a2b; - --accent-color: #00d4aa; - --accent-dark: #00b894; - - /* Neutral Colors */ - --white: #ffffff; - --gray-50: #f8fafc; - --gray-100: #f1f5f9; - --gray-200: #e2e8f0; - --gray-300: #cbd5e1; - --gray-400: #94a3b8; - --gray-500: #64748b; - --gray-600: #475569; - --gray-700: #334155; - --gray-800: #1e293b; - --gray-900: #0f172a; - --black: #000000; - - /* Background Colors */ - --bg-primary: #0a0a0f; - --bg-secondary: #1a1a2e; - --bg-tertiary: #16213e; - --bg-glass: rgba(255, 255, 255, 0.05); - --bg-glass-hover: rgba(255, 255, 255, 0.1); - - /* Typography */ - --font-primary: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; - --font-mono: 'Space Mono', 'Fira Code', 'Monaco', 'Consolas', monospace; - - /* Font Weights */ - --font-light: 300; - --font-regular: 400; - --font-medium: 500; - --font-semibold: 600; - --font-bold: 700; - - /* Font Sizes */ - --text-xs: 0.75rem; /* 12px */ - --text-sm: 0.875rem; /* 14px */ - --text-base: 1rem; /* 16px */ - --text-lg: 1.125rem; /* 18px */ - --text-xl: 1.25rem; /* 20px */ - --text-2xl: 1.5rem; /* 24px */ - --text-3xl: 1.875rem; /* 30px */ - --text-4xl: 2.25rem; /* 36px */ - --text-5xl: 3rem; /* 48px */ - --text-6xl: 3.75rem; /* 60px */ - - /* Spacing */ - --space-xs: 0.25rem; /* 4px */ - --space-sm: 0.5rem; /* 8px */ - --space-md: 1rem; /* 16px */ - --space-lg: 1.5rem; /* 24px */ - --space-xl: 2rem; /* 32px */ - --space-2xl: 3rem; /* 48px */ - --space-3xl: 4rem; /* 64px */ - --space-4xl: 6rem; /* 96px */ - - /* Border Radius */ - --radius-sm: 0.25rem; /* 4px */ - --radius-md: 0.5rem; /* 8px */ - --radius-lg: 0.75rem; /* 12px */ - --radius-xl: 1rem; /* 16px */ - --radius-2xl: 1.5rem; /* 24px */ - --radius-full: 50%; - - /* Shadows */ - --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); - --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); - --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); - --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); - --shadow-glow: 0 0 20px rgba(0, 102, 204, 0.3); - - /* Animations */ - --transition-fast: 0.15s ease; - --transition-base: 0.3s ease; - --transition-slow: 0.5s ease; - - /* Z-Index */ - --z-dropdown: 1000; - --z-sticky: 1020; - --z-fixed: 1030; - --z-modal-backdrop: 1040; - --z-modal: 1050; - --z-popover: 1060; - --z-tooltip: 1070; - --z-toast: 1080; -} - -/* Base Styles */ -html { - scroll-behavior: smooth; - font-size: 16px; -} - -body { - font-family: var(--font-primary); - font-weight: var(--font-regular); - line-height: 1.6; - color: var(--white); - background: var(--bg-primary); - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - overflow-x: hidden; -} - -/* Container */ -.container { - width: 100%; - max-width: 1200px; - margin: 0 auto; - padding: 0 var(--space-md); -} - -@media (min-width: 768px) { - .container { - padding: 0 var(--space-lg); - } -} - -/* Typography */ -h1, h2, h3, h4, h5, h6 { - font-weight: var(--font-semibold); - line-height: 1.2; - margin-bottom: var(--space-md); - color: var(--white); -} - -h1 { - font-size: var(--text-5xl); - font-weight: var(--font-bold); -} - -h2 { - font-size: var(--text-4xl); -} - -h3 { - font-size: var(--text-3xl); -} - -h4 { - font-size: var(--text-2xl); -} - -h5 { - font-size: var(--text-xl); -} - -h6 { - font-size: var(--text-lg); -} - -p { - margin-bottom: var(--space-md); - color: var(--gray-300); -} - -.lead { - font-size: var(--text-xl); - font-weight: var(--font-medium); - color: var(--gray-200); -} - -/* Links */ -a { - color: var(--primary-light); - text-decoration: none; - transition: color var(--transition-base); -} - -a:hover { - color: var(--primary-color); -} - -/* Lists */ -ul, ol { - margin-bottom: var(--space-md); - padding-left: var(--space-lg); -} - -li { - margin-bottom: var(--space-sm); - color: var(--gray-300); -} - -/* Buttons */ -.btn { - display: inline-flex; - align-items: center; - gap: var(--space-sm); - padding: var(--space-md) var(--space-xl); - font-size: var(--text-base); - font-weight: var(--font-medium); - text-decoration: none; - border: none; - border-radius: var(--radius-lg); - cursor: pointer; - transition: all var(--transition-base); - position: relative; - overflow: hidden; - background: transparent; - white-space: nowrap; -} - -.btn::before { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: linear-gradient(45deg, transparent 30%, rgba(255, 255, 255, 0.3) 50%, transparent 70%); - transform: translateX(-100%); - transition: transform var(--transition-slow); -} - -.btn:hover::before { - transform: translateX(100%); -} - -.btn-primary { - background: linear-gradient(135deg, var(--primary-color), var(--primary-dark)); - color: var(--white); - box-shadow: var(--shadow-lg); -} - -.btn-primary:hover { - background: linear-gradient(135deg, var(--primary-light), var(--primary-color)); - transform: translateY(-2px); - box-shadow: var(--shadow-xl), var(--shadow-glow); - color: var(--white); -} - -.btn-secondary { - background: rgba(255, 255, 255, 0.1); - color: var(--white); - border: 1px solid rgba(255, 255, 255, 0.2); - backdrop-filter: blur(10px); -} - -.btn-secondary:hover { - background: rgba(255, 255, 255, 0.2); - border-color: rgba(255, 255, 255, 0.3); - transform: translateY(-2px); - color: var(--white); -} - -/* Section Styling */ -section { - padding: var(--space-4xl) 0; - position: relative; -} - -.section-header { - text-align: center; - margin-bottom: var(--space-3xl); -} - -.section-title { - font-size: var(--text-4xl); - font-weight: var(--font-bold); - margin-bottom: var(--space-md); - background: linear-gradient(135deg, var(--white), var(--gray-300)); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -.section-subtitle { - font-size: var(--text-xl); - color: var(--gray-400); - max-width: 600px; - margin: 0 auto; -} - -/* Gradient Text */ -.gradient-text { - background: linear-gradient(135deg, var(--primary-light), var(--accent-color)); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -/* Navigation */ -.navbar { - position: fixed; - top: 0; - left: 0; - right: 0; - background: rgba(10, 10, 15, 0.9); - backdrop-filter: blur(20px); - border-bottom: 1px solid rgba(255, 255, 255, 0.1); - z-index: var(--z-fixed); - transition: all var(--transition-base); -} - -.nav-container { - max-width: 1200px; - margin: 0 auto; - padding: 0 var(--space-md); - display: flex; - align-items: center; - justify-content: space-between; - height: 70px; -} - -.nav-brand { - display: flex; - align-items: center; - gap: var(--space-md); - font-size: var(--text-xl); - font-weight: var(--font-bold); - color: var(--white); -} - -.logo { - color: var(--primary-light); -} - -.nav-menu { - display: flex; - align-items: center; - gap: var(--space-xl); -} - -.nav-link { - font-size: var(--text-base); - font-weight: var(--font-medium); - color: var(--gray-300); - transition: color var(--transition-base); - position: relative; -} - -.nav-link::after { - content: ''; - position: absolute; - bottom: -5px; - left: 0; - width: 0; - height: 2px; - background: linear-gradient(90deg, var(--primary-color), var(--accent-color)); - transition: width var(--transition-base); -} - -.nav-link:hover { - color: var(--white); -} - -.nav-link:hover::after { - width: 100%; -} - -.nav-toggle { - display: none; - flex-direction: column; - cursor: pointer; - gap: 4px; -} - -.nav-toggle span { - width: 25px; - height: 3px; - background: var(--white); - transition: all var(--transition-base); -} - -/* Hero Section */ -.hero { - min-height: 100vh; - display: flex; - align-items: center; - justify-content: center; - position: relative; - overflow: hidden; - background: radial-gradient(ellipse at center, var(--bg-secondary) 0%, var(--bg-primary) 70%); -} - -.hero-background { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 1; -} - -.stars { - position: absolute; - width: 100%; - height: 100%; - background-image: - radial-gradient(2px 2px at 20px 30px, #eee, transparent), - radial-gradient(2px 2px at 40px 70px, rgba(255,255,255,0.8), transparent), - radial-gradient(1px 1px at 90px 40px, #fff, transparent), - radial-gradient(1px 1px at 130px 80px, rgba(255,255,255,0.6), transparent), - radial-gradient(2px 2px at 160px 30px, #ddd, transparent); - background-repeat: repeat; - background-size: 200px 100px; - animation: sparkle 20s linear infinite; -} - -@keyframes sparkle { - from { transform: translateY(0px); } - to { transform: translateY(-100px); } -} - -.floating-elements { - position: absolute; - width: 100%; - height: 100%; -} - -.rocket-icon { - position: absolute; - opacity: 0.3; - animation: float 6s ease-in-out infinite; - color: var(--primary-light); -} - -.rocket-1 { - top: 20%; - left: 10%; - animation-delay: 0s; -} - -.rocket-2 { - top: 60%; - right: 15%; - animation-delay: 2s; -} - -.rocket-3 { - top: 80%; - left: 80%; - animation-delay: 4s; -} - -@keyframes float { - 0%, 100% { transform: translateY(0px) rotate(0deg); } - 50% { transform: translateY(-20px) rotate(5deg); } -} - -.hero-content { - text-align: center; - z-index: 2; - max-width: 800px; - padding: var(--space-xl); -} - -.hero-title { - font-size: clamp(var(--text-4xl), 8vw, var(--text-6xl)); - font-weight: var(--font-bold); - margin-bottom: var(--space-lg); - line-height: 1.1; -} - -.foundation-text { - display: block; - font-weight: var(--font-light); - color: var(--gray-300); - margin-top: var(--space-sm); -} - -.hero-subtitle { - font-size: var(--text-xl); - color: var(--gray-300); - margin-bottom: var(--space-2xl); - max-width: 600px; - margin-left: auto; - margin-right: auto; -} - -.hero-buttons { - display: flex; - gap: var(--space-lg); - justify-content: center; - flex-wrap: wrap; -} - -/* Form Styles */ -.form-group { - margin-bottom: var(--space-lg); -} - -input, select, textarea { - width: 100%; - padding: var(--space-md); - background: rgba(255, 255, 255, 0.05); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: var(--radius-md); - color: var(--white); - font-size: var(--text-base); - transition: all var(--transition-base); -} - -input:focus, select:focus, textarea:focus { - outline: none; - border-color: var(--primary-color); - box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1); - background: rgba(255, 255, 255, 0.08); -} - -input::placeholder, textarea::placeholder { - color: var(--gray-400); -} - -select option { - background: var(--bg-secondary); - color: var(--white); -} - -/* Utilities */ -.text-center { text-align: center; } -.text-left { text-align: left; } -.text-right { text-align: right; } - -.mb-0 { margin-bottom: 0; } -.mb-sm { margin-bottom: var(--space-sm); } -.mb-md { margin-bottom: var(--space-md); } -.mb-lg { margin-bottom: var(--space-lg); } -.mb-xl { margin-bottom: var(--space-xl); } - -.mt-0 { margin-top: 0; } -.mt-sm { margin-top: var(--space-sm); } -.mt-md { margin-top: var(--space-md); } -.mt-lg { margin-top: var(--space-lg); } -.mt-xl { margin-top: var(--space-xl); } - -/* Scrollbar Styles */ -::-webkit-scrollbar { - width: 8px; -} - -::-webkit-scrollbar-track { - background: var(--bg-primary); -} - -::-webkit-scrollbar-thumb { - background: var(--gray-600); - border-radius: var(--radius-full); -} - -::-webkit-scrollbar-thumb:hover { - background: var(--gray-500); -} - -/* Selection */ -::selection { - background: var(--primary-color); - color: var(--white); -} - -::-moz-selection { - background: var(--primary-color); - color: var(--white); -} -.downsvg-header-ltsc{ - width: 9em; -} \ No newline at end of file diff --git a/styles/meta-style.css b/styles/meta-style.css deleted file mode 100644 index e69de29..0000000 diff --git a/styles/neon-global.css b/styles/neon-global.css deleted file mode 100644 index e69de29..0000000 diff --git a/styles/responsive.css b/styles/responsive.css deleted file mode 100644 index 1bb3410..0000000 --- a/styles/responsive.css +++ /dev/null @@ -1,591 +0,0 @@ -/* Mobile First Approach - Base styles are mobile, then scale up */ - -/* Extra Small Devices (Portrait Phones) */ -@media (max-width: 575.98px) { - /* Typography adjustments */ - :root { - --text-5xl: 2.5rem; /* 40px */ - --text-4xl: 2rem; /* 32px */ - --text-3xl: 1.75rem; /* 28px */ - --text-2xl: 1.375rem; /* 22px */ - --text-xl: 1.125rem; /* 18px */ - } - - /* Container */ - .container { - padding: 0 var(--space-sm); - } - - /* Navigation */ - .nav-menu { - position: fixed; - top: 70px; - left: -100%; - width: 100%; - height: calc(100vh - 70px); - background: rgba(10, 10, 15, 0.95); - backdrop-filter: blur(20px); - flex-direction: column; - justify-content: flex-start; - align-items: center; - padding-top: var(--space-3xl); - transition: left var(--transition-base); - z-index: var(--z-modal); - } - - .nav-menu.active { - left: 0; - } - - .nav-link { - font-size: var(--text-lg); - padding: var(--space-lg) 0; - width: 100%; - text-align: center; - border-bottom: 1px solid rgba(255, 255, 255, 0.1); - } - - .nav-toggle { - display: flex; - } - - .nav-toggle.active span:nth-child(1) { - transform: rotate(45deg) translate(5px, 5px); - } - - .nav-toggle.active span:nth-child(2) { - opacity: 0; - } - - .nav-toggle.active span:nth-child(3) { - transform: rotate(-45deg) translate(7px, -6px); - } - - /* Hero */ - .hero-content { - padding: var(--space-lg); - } - - .hero-title { - font-size: clamp(var(--text-3xl), 12vw, var(--text-5xl)); - line-height: 1.1; - } - - .hero-subtitle { - font-size: var(--text-lg); - margin-bottom: var(--space-xl); - } - - .hero-buttons { - flex-direction: column; - gap: var(--space-md); - align-items: center; - } - - .btn { - width: 100%; - max-width: 280px; - justify-content: center; - } - - /* Sections */ - section { - padding: var(--space-3xl) 0; - } - - .section-header { - margin-bottom: var(--space-2xl); - } - - /* About */ - .about-stats { - grid-template-columns: 1fr; - gap: var(--space-lg); - } - - .rocket-diagram { - width: 150px; - height: 225px; - margin: var(--space-xl) auto; - } - - /* Mission */ - .mission-grid { - grid-template-columns: 1fr; - gap: var(--space-xl); - } - - .mission-card { - padding: var(--space-xl); - } - - .card-icon { - width: 60px; - height: 60px; - } - - /* Projects */ - .projects-grid { - grid-template-columns: 1fr; - gap: var(--space-xl); - } - - .project-card { - padding: var(--space-xl); - } - - .project-header { - flex-direction: column; - align-items: flex-start; - gap: var(--space-sm); - } - - /* Community */ - .community-links { - grid-template-columns: 1fr; - gap: var(--space-lg); - } - - .community-card { - flex-direction: column; - text-align: center; - padding: var(--space-xl); - } - - /* Contact */ - .contact-form { - padding: var(--space-xl); - } - - /* Footer */ - .footer-content { - grid-template-columns: 1fr; - gap: var(--space-xl); - text-align: center; - } - - .footer-brand { - flex-direction: column; - gap: var(--space-md); - text-align: center; - } - - .footer-links { - grid-template-columns: 1fr; - gap: var(--space-lg); - } -} - -/* Small Devices (Landscape Phones) */ -@media (min-width: 576px) and (max-width: 767.98px) { - /* Hero */ - .hero-buttons { - flex-direction: row; - justify-content: center; - flex-wrap: wrap; - } - - .btn { - width: auto; - min-width: 160px; - } - - /* About */ - .about-stats { - grid-template-columns: repeat(2, 1fr); - } - - /* Mission */ - .mission-grid { - grid-template-columns: 1fr; - } - - /* Projects */ - .projects-grid { - grid-template-columns: 1fr; - } - - /* Community */ - .community-links { - grid-template-columns: 1fr; - } - - /* Footer */ - .footer-links { - grid-template-columns: repeat(2, 1fr); - } -} - -/* Medium Devices (Tablets) */ -@media (min-width: 768px) and (max-width: 991.98px) { - /* Navigation */ - .nav-container { - padding: 0 var(--space-lg); - } - - /* Hero */ - .hero-content { - max-width: 700px; - } - - /* About */ - .about-content { - grid-template-columns: 1fr 1fr; - gap: var(--space-2xl); - align-items: center; - } - - .about-stats { - grid-template-columns: repeat(3, 1fr); - } - - /* Mission */ - .mission-grid { - grid-template-columns: repeat(2, 1fr); - gap: var(--space-xl); - } - - /* Projects */ - .projects-grid { - grid-template-columns: repeat(2, 1fr); - } - - /* Community */ - .community-content { - grid-template-columns: 1fr 1fr; - gap: var(--space-2xl); - } - - .community-links { - grid-template-columns: 1fr; - } - - /* Contact */ - .contact-content { - grid-template-columns: 1fr 1fr; - gap: var(--space-2xl); - } - - /* Footer */ - .footer-content { - grid-template-columns: 1fr 2fr; - } - - .footer-links { - grid-template-columns: repeat(3, 1fr); - } -} - -/* Large Devices (Small Desktops) */ -@media (min-width: 992px) and (max-width: 1199.98px) { - /* Mission */ - .mission-grid { - grid-template-columns: repeat(3, 1fr); - } - - /* Projects */ - .projects-grid { - grid-template-columns: repeat(2, 1fr); - } - - /* Community */ - .community-links { - grid-template-columns: repeat(2, 1fr); - } -} - -/* Extra Large Devices (Large Desktops) */ -@media (min-width: 1200px) { - /* Container */ - .container { - max-width: 1200px; - padding: 0 var(--space-xl); - } - - /* Projects */ - .projects-grid { - grid-template-columns: repeat(3, 1fr); - } - - /* Community */ - .community-links { - grid-template-columns: repeat(2, 1fr); - } -} - -/* Height-based Media Queries for Short Screens */ -@media (max-height: 600px) and (orientation: landscape) { - .hero { - min-height: 100vh; - padding: var(--space-xl) 0; - } - - .hero-content { - padding: var(--space-lg); - } - - .hero-title { - font-size: var(--text-4xl); - margin-bottom: var(--space-md); - } - - .hero-subtitle { - font-size: var(--text-base); - margin-bottom: var(--space-lg); - } - - .hero-buttons { - gap: var(--space-md); - } - - section { - padding: var(--space-2xl) 0; - } -} - -/* Print Styles */ -@media print { - * { - background: transparent !important; - color: black !important; - box-shadow: none !important; - text-shadow: none !important; - } - - body { - font-size: 12pt; - line-height: 1.4; - } - - .navbar, - .nav-toggle, - .hero-background, - .floating-elements, - .contact-form, - .footer { - display: none !important; - } - - .hero { - min-height: auto; - padding: 2rem 0; - } - - .section-header { - margin-bottom: 1rem; - } - - section { - padding: 1rem 0; - page-break-inside: avoid; - } - - h1, h2, h3 { - page-break-after: avoid; - } - - .mission-grid, - .projects-grid { - grid-template-columns: 1fr; - gap: 1rem; - } - - .btn { - display: none; - } - - a[href^="http"]:after { - content: " (" attr(href) ")"; - font-size: 0.8em; - color: #666; - } -} - -/* Reduced Motion */ -@media (prefers-reduced-motion: reduce) { - *, - *::before, - *::after { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - scroll-behavior: auto !important; - } - - .stars { - animation: none; - } - - .rocket-icon { - animation: none; - } - - .rocket-parts > * { - animation: none; - } -} - -/* High Contrast Mode */ -@media (prefers-contrast: high) { - :root { - --primary-color: #f5f5f5; - --primary-light: #fafafa; - --accent-color: #00ff88; - --white: #ffffff; - --gray-300: #cccccc; - --gray-400: #999999; - --bg-primary: #000000; - --bg-secondary: #111111; - --bg-tertiary: #222222; - } - - .btn { - border: 2px solid currentColor; - } - - .mission-card, - .project-card, - .community-card, - .contact-form { - border: 2px solid rgba(255, 255, 255, 0.3); - } -} - -/* Dark Mode Override (if user prefers light) */ -@media (prefers-color-scheme: light) { - /* Keep dark theme as it's part of the design */ - /* This could be extended if a light mode is desired */ -} - -/* Touch Device Optimizations */ -@media (hover: none) and (pointer: coarse) { - /* Increase touch targets */ - .btn { - min-height: 44px; - padding: var(--space-md) var(--space-xl); - } - - .nav-link { - padding: var(--space-lg) var(--space-md); - } - - .community-card, - .project-card, - .mission-card { - padding: var(--space-xl); - } - - /* Remove hover effects that don't work on touch */ - .btn:hover, - .mission-card:hover, - .project-card:hover, - .community-card:hover, - .stat-item:hover { - transform: none; - } - - /* Add active states for touch feedback */ - .btn:active { - transform: scale(0.98); - } - - .mission-card:active, - .project-card:active, - .community-card:active { - transform: scale(0.98); - background: rgba(255, 255, 255, 0.1); - } -} - -/* Landscape orientation adjustments for mobile */ -@media (orientation: landscape) and (max-height: 500px) { - .hero { - min-height: auto; - padding: var(--space-2xl) 0; - } - - .hero-title { - font-size: var(--text-3xl); - margin-bottom: var(--space-sm); - } - - .hero-subtitle { - font-size: var(--text-base); - margin-bottom: var(--space-lg); - } - - .section-header { - margin-bottom: var(--space-xl); - } - - section { - padding: var(--space-2xl) 0; - } -} - -/* Focus styles for accessibility */ -@media (prefers-reduced-motion: no-preference) { - .btn:focus, - .nav-link:focus, - input:focus, - select:focus, - textarea:focus { - outline: 2px solid var(--primary-color); - outline-offset: 2px; - } -} - -/* Utilities for responsive design */ -.hide-mobile { - display: none; -} - -@media (min-width: 768px) { - .hide-mobile { - display: block; - } - - .hide-desktop { - display: none; - } -} - -.show-mobile { - display: block; -} - -@media (min-width: 768px) { - .show-mobile { - display: none; - } -} - -/* Text alignment utilities for different screen sizes */ -@media (max-width: 767.98px) { - .text-center-mobile { - text-align: center !important; - } - - .text-left-mobile { - text-align: left !important; - } -} - -/* Spacing utilities for mobile */ -@media (max-width: 767.98px) { - .mb-mobile-lg { - margin-bottom: var(--space-lg) !important; - } - - .mt-mobile-lg { - margin-top: var(--space-lg) !important; - } - - .p-mobile-sm { - padding: var(--space-sm) !important; - } - - .p-mobile-md { - padding: var(--space-md) !important; - } -} diff --git a/teams/STYLE_GUIDE.md b/teams/STYLE_GUIDE.md deleted file mode 100644 index e69de29..0000000 diff --git a/teams/css/dark-theme.css b/teams/css/dark-theme.css deleted file mode 100644 index e69de29..0000000 diff --git a/teams/css/neon-theme.css b/teams/css/neon-theme.css deleted file mode 100644 index e69de29..0000000 diff --git a/teams/css/style-premium.css b/teams/css/style-premium.css deleted file mode 100644 index e69de29..0000000 diff --git a/teams/css/style.css b/teams/css/style.css deleted file mode 100644 index e69de29..0000000 diff --git a/teams/css/teams.css b/teams/css/teams.css deleted file mode 100644 index e69de29..0000000 diff --git a/teams/index-dark.html b/teams/index-dark.html deleted file mode 100644 index e69de29..0000000 diff --git a/teams/index-new.html b/teams/index-new.html deleted file mode 100644 index e69de29..0000000 diff --git a/teams/index.html b/teams/index.html deleted file mode 100644 index 983be65..0000000 --- a/teams/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - Document - - - - - - \ No newline at end of file diff --git a/teams/js/auth.js b/teams/js/auth.js deleted file mode 100644 index e69de29..0000000 diff --git a/teams/js/database-tests.js b/teams/js/database-tests.js deleted file mode 100644 index e69de29..0000000 diff --git a/teams/js/events.js b/teams/js/events.js deleted file mode 100644 index e69de29..0000000 diff --git a/teams/js/firebase-config.js b/teams/js/firebase-config.js deleted file mode 100644 index e69de29..0000000 diff --git a/teams/js/logs.js b/teams/js/logs.js deleted file mode 100644 index e69de29..0000000 diff --git a/teams/js/main.js b/teams/js/main.js deleted file mode 100644 index e69de29..0000000 diff --git a/teams/js/members.js b/teams/js/members.js deleted file mode 100644 index e69de29..0000000 diff --git a/teams/js/teams.js b/teams/js/teams.js deleted file mode 100644 index e69de29..0000000 diff --git a/teams/js/utils.js b/teams/js/utils.js deleted file mode 100644 index e69de29..0000000 diff --git a/teams/team.html b/teams/team.html deleted file mode 100644 index e69de29..0000000 diff --git a/test-community.html b/test-community.html deleted file mode 100644 index 5984478..0000000 --- a/test-community.html +++ /dev/null @@ -1,423 +0,0 @@ -del community.html - - - - - - Community Test - OpenRockets - - - -
-

🚀 OpenRockets Community System Test

-

This page tests all the community features to identify what's working and what needs to be fixed.

- - -
-

📡 Connection Status

-
Checking connection...
- - -
- - -
-

📝 Posts System

-
Testing posts...
- - - -
-
- - -
-

💬 Live Chat System

-
Chat disconnected
-
- - -
- - -
-

👤 User System

-
Checking user status...
- - -
-
- - -
-

📸 File Upload System

-
Upload system ready
- - -
-
- - - - - diff --git a/test-server.js b/test-server.js deleted file mode 100644 index 3e6117b..0000000 --- a/test-server.js +++ /dev/null @@ -1,20 +0,0 @@ -// Simple test to verify server setup -const express = require('express'); -const cors = require('cors'); - -const app = express(); -const PORT = 3000; - -app.use(cors()); -app.use(express.json()); - -app.get('/test', (req, res) => { - res.json({ message: 'Backend is working!', timestamp: new Date().toISOString() }); -}); - -app.listen(PORT, () => { - console.log(`Test server running on port ${PORT}`); -}); - -module.exports = app; -v \ No newline at end of file diff --git a/uploads/placeholder-jheuhyuer.png b/uploads/placeholder-jheuhyuer.png deleted file mode 100644 index 3fe2058..0000000 Binary files a/uploads/placeholder-jheuhyuer.png and /dev/null differ diff --git a/uploads/profile-images-reference.txt b/uploads/profile-images-reference.txt deleted file mode 100644 index ca76735..0000000 --- a/uploads/profile-images-reference.txt +++ /dev/null @@ -1,10 +0,0 @@ - - -Placeholder images for: -- mihitha.png -- thulana.png -- senul.png -- tharusha.png -- thisitha.png -- viruna.png -- navindu.png (use https://github.com/njnavindu.png) diff --git a/v/2025.html b/v/2025.html deleted file mode 100644 index 253f4df..0000000 --- a/v/2025.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - GITSTARS® By OpenRockets | 2025 Verified - - - Copyrights (c) 2025 OpenRockets Open-source Software Foundation. All Rights Reserved. -
- Content Will be available soon. - - \ No newline at end of file diff --git a/v/cityofgits-summercap-dark-lg-4000x1010.png b/v/cityofgits-summercap-dark-lg-4000x1010.png deleted file mode 100644 index 8521094..0000000 Binary files a/v/cityofgits-summercap-dark-lg-4000x1010.png and /dev/null differ diff --git a/v/gitstarts2025v.png b/v/gitstarts2025v.png deleted file mode 100644 index ea5acc9..0000000 Binary files a/v/gitstarts2025v.png and /dev/null differ diff --git a/v/gn-summer.png b/v/gn-summer.png deleted file mode 100644 index ad79cb6..0000000 Binary files a/v/gn-summer.png and /dev/null differ diff --git a/v/hero.png b/v/hero.png deleted file mode 100644 index 43874b7..0000000 Binary files a/v/hero.png and /dev/null differ diff --git a/v/openrockets-india.png b/v/openrockets-india.png deleted file mode 100644 index f4252bf..0000000 Binary files a/v/openrockets-india.png and /dev/null differ diff --git a/v/openrocketsradient.png b/v/openrocketsradient.png deleted file mode 100644 index 664f486..0000000 Binary files a/v/openrocketsradient.png and /dev/null differ