diff --git a/404.html b/404.html deleted file mode 100644 index abf9404f..00000000 --- a/404.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - Page Not Found :( - - - - -
44
-
Oops! Page not found.
-
We couldn't find the page you requested. It might be unavailable at the moment or have a different URL.
- Return to Home - - \ No newline at end of file diff --git a/Documentation/PROJECT_STRUCTURE.md b/Documentation/PROJECT_STRUCTURE.md index 901308b5..bda663ad 100644 --- a/Documentation/PROJECT_STRUCTURE.md +++ b/Documentation/PROJECT_STRUCTURE.md @@ -2,7 +2,13 @@ ``` -├── 404.html +├── LICENSE.md +├── README.md +├── SECURITY.md +├── appconfig +├── documentation.html +├── index.html +├── sitemap.xml ├── Documentation/ │ ├── PROJECT_STRUCTURE.md │ ├── contributing.md @@ -16,15 +22,8 @@ │ ├── repo_structure.txt │ └── styles/ │ └── sitemap.xsl -├── LICENSE.md -├── README.md -├── SECURITY.md -├── appconfig -├── documentation.html -├── index.html ├── installation/ │ └── requirements.txt -├── sitemap.xml ├── software/ │ ├── __pycache__/ │ │ ├── dataVisualization.cpython-311.pyc @@ -100,7 +99,14 @@ │ ├── report.py │ └── tempCodeRunnerFile.py └── website/ - ├── pages/ + ├── main.py + ├── routes/ + ├── models/ + ├── schema/ + ├── templates/ + | ├── index.html + | ├── documentation.html + | ├── 404.html │ ├── contributor.html │ ├── license.html │ ├── login.html diff --git a/README.md b/README.md index 311d3ab0..c3de7028 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Dataverse - + ###### Data Visualisation Software & Finance Tracker @@ -89,11 +89,11 @@ Software Home Page

Visualised Data
- +

Stored Data
- + @@ -154,20 +154,21 @@ V. Install MySQL if you don't have it already from [here](https://dev.mysql.com/ > [!IMPORTANT] > Change the values of `DB_HOST`, `DB_USER` and `DB_PASSWORD` in [software/db_config.py](software/db_config.py) file according to your MySQL account. -VI. Run the application: +VI (a). To run the application: > ``` -> python software/main.py +> python software\main.py > ``` -VII. To Run `index.html` (For Website Development) +VI (b). To run the website: -1. Install the **Go Live** extension in VS Code. -2. Open the `index.html` file in VS Code. -3. Click on the **Go Live** button in the bottom-right corner of VS Code. +> ``` +> cd website +> uvicorn main:app --reload +> ``` > The default URL will be: -> `http://localhost:5500/Dataverse/index.html` +> `http://127.0.0.1:8000` Now, the software should run smoothly with no errors, feel free to use the software and don't forget to give feedback on [Dataverse's website](https://multiverse-dataverse.netlify.app/)! diff --git a/index.html b/index.html deleted file mode 100644 index b756b856..00000000 --- a/index.html +++ /dev/null @@ -1,352 +0,0 @@ - - - - - - - Dataverse - - - - - - - - - - - - -
- -
-
-
-
- - -
- -
- - - - - -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Home
Documentation
Versions
Support
Contributors
Reviews
License
Stunning Visuals
Github Repository
-
-
SCROLL TO KNOW MORE -
-
-
-
-
-
-
-
-
-
-

Data

-

verse

-
-

An Open-Source Software (OSS) that allows organizing, analyzing and visualizing data, - providing flexible tools for financial tracking and customized chart creation. -

-

The adjoining map shows people who are using or contributing to Dataverse.

- - - - - -
-
-
-
-
-

So, What does this Software do?

-

Visualise Data!

-

Easily transform raw data into visually appealing charts such as bar graphs, pie charts, - and - line graphs. It also supports advanced data visualisation techniques like heatmaps, Radar charts, 3D - Surface Plots, etc.. -

- You can also download the generated plots or save the data for later use. -

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

Track Finances!

-

Manage your finances by providing data such as income, expense, investment, etc. and get - useful results such as visualization of data, extrapolated data, max expenditure, etc.. Also, the - software automatically stores data with a timestamp.

-
-
- -
-

Here are some examples of what you can do with Dataverse

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

Want to contribute to this Project?

-
-
-
-
-
-

Technologies Used

-
-
-

Python

-

Numpy

-

Pandas

-

Matplotlib

-

Tkinter

-

MySQL

-

HTML

-

CSS

-

JavaScript

-

MS Excel

-

Netlify

-

Postman

-
-
-

As this is an open-source project, contributions are always welcome. Whether you're - interested in adding new features, fixing bugs, or improving documentation, your contributions are - valuable. You can contribute to the project by visiting the link to the GitHub repository provided - below. Join me in making Dataverse even better for everyone!

- -
-
- - -
-
- - - - - - - - - - - - \ No newline at end of file diff --git a/installation/requirements.txt b/installation/requirements.txt index ee35841c..19410a88 100644 --- a/installation/requirements.txt +++ b/installation/requirements.txt @@ -1,16 +1,36 @@ -PIL -time +annotated-types +anyio +click +colorama +contourpy +customtkinter +cycler +darkdetect +dnspython +fastapi +fonttools +h11 +idna +Jinja2 +kiwisolver +MarkupSafe +matplotlib>=3.2,<3.11 # Adding version range for compatibility +motor +mysql-connector +mysql-connector-python numpy -ctypes -tkinter -datetime +packaging +pillow +pydantic +pydantic_core +pymongo +pyparsing +python-dateutil +python-dotenv +six +sniffio +starlette tabulate -functools -webbrowser -matplotlib -customtkinter -mysql.connector -matplotlib.pyplot -matplotlib.gridspec -matplotlib.widgets -mpl_toolkits.mplot3d \ No newline at end of file +typing_extensions +uvicorn +python-multipart \ No newline at end of file diff --git a/software/__pycache__/dataVisualization.cpython-311.pyc b/software/__pycache__/dataVisualization.cpython-311.pyc deleted file mode 100644 index 5fd2f71b..00000000 Binary files a/software/__pycache__/dataVisualization.cpython-311.pyc and /dev/null differ diff --git a/software/__pycache__/db_config.cpython-312.pyc b/software/__pycache__/db_config.cpython-312.pyc deleted file mode 100644 index b214bdce..00000000 Binary files a/software/__pycache__/db_config.cpython-312.pyc and /dev/null differ diff --git a/software/__pycache__/financeTracker.cpython-311.pyc b/software/__pycache__/financeTracker.cpython-311.pyc deleted file mode 100644 index 5eddb9ef..00000000 Binary files a/software/__pycache__/financeTracker.cpython-311.pyc and /dev/null differ diff --git a/software/__pycache__/functions.cpython-311.pyc b/software/__pycache__/functions.cpython-311.pyc deleted file mode 100644 index db5778c8..00000000 Binary files a/software/__pycache__/functions.cpython-311.pyc and /dev/null differ diff --git a/software/__pycache__/main.cpython-311.pyc b/software/__pycache__/main.cpython-311.pyc deleted file mode 100644 index a349598f..00000000 Binary files a/software/__pycache__/main.cpython-311.pyc and /dev/null differ diff --git a/software/__pycache__/mainGUI.cpython-311.pyc b/software/__pycache__/mainGUI.cpython-311.pyc deleted file mode 100644 index 4b011131..00000000 Binary files a/software/__pycache__/mainGUI.cpython-311.pyc and /dev/null differ diff --git a/software/__pycache__/manage_data.cpython-312.pyc b/software/__pycache__/manage_data.cpython-312.pyc deleted file mode 100644 index cec07004..00000000 Binary files a/software/__pycache__/manage_data.cpython-312.pyc and /dev/null differ diff --git a/software/__pycache__/plot.cpython-311.pyc b/software/__pycache__/plot.cpython-311.pyc deleted file mode 100644 index 7ca2d57a..00000000 Binary files a/software/__pycache__/plot.cpython-311.pyc and /dev/null differ diff --git a/software/__pycache__/report.cpython-311.pyc b/software/__pycache__/report.cpython-311.pyc deleted file mode 100644 index 91d4bd19..00000000 Binary files a/software/__pycache__/report.cpython-311.pyc and /dev/null differ diff --git a/website/main.py b/website/main.py new file mode 100644 index 00000000..ef2cb134 --- /dev/null +++ b/website/main.py @@ -0,0 +1,94 @@ +from fastapi import FastAPI, Request +from fastapi.responses import HTMLResponse +from pathlib import Path +from fastapi.staticfiles import StaticFiles +from motor.motor_asyncio import AsyncIOMotorClient +from fastapi.templating import Jinja2Templates +from contextlib import asynccontextmanager +from dotenv import load_dotenv +import os +from routes.feedback import router as feedback_router + +# Load .env file +load_dotenv() + +# Use lifespan to handle startup and shutdown events +@asynccontextmanager +async def lifespan(app: FastAPI): + # On startup + try: + # Try a simple query to check the connection + await db.command("ping") + print("MongoDB connected successfully!") + yield + except Exception as e: + print(f"Error connecting to MongoDB: {e}") + raise e + finally: + # On shutdown + client.close() # Clean up the MongoDB client + +# Create FastAPI app and pass lifespan +app = FastAPI(lifespan=lifespan) + +# Serve static files +app.mount("/static", StaticFiles(directory="../"), name="static") +app.mount("/web_images", StaticFiles(directory="web_images"), name="web_images") +app.mount("/styles", StaticFiles(directory="styles"), name="styles") +app.mount("/scripts", StaticFiles(directory="scripts"), name="scripts") +app.mount("/software", StaticFiles(directory="../software"), name="software") +app.mount("/Documentation", StaticFiles(directory="../Documentation"), name="Documentation") + +# Set up Jinja2 templates for dynamic HTML rendering +templates = Jinja2Templates(directory="templates/") + +# MongoDB setup +MONGODB_URL = os.getenv("MONGODB_URL") +client = AsyncIOMotorClient(MONGODB_URL) +db = client.Dataverse + +# Collections +feedback_collection = db["feedbacks"] + +# Include Routers +app.include_router(feedback_router) + +@app.get("/", response_class=HTMLResponse) +def read_root(request: Request): + return templates.TemplateResponse("index.html", {"request": request}) + +@app.get("/login", response_class=HTMLResponse) +def documentation(request: Request): + return templates.TemplateResponse("login.html", {"request": request}) + +@app.get("/signup", response_class=HTMLResponse) +def documentation(request: Request): + return templates.TemplateResponse("signup.html", {"request": request}) + +@app.get("/documentation", response_class=HTMLResponse) +def documentation(request: Request): + return templates.TemplateResponse("documentation.html", {"request": request}) + +@app.get("/versions", response_class=HTMLResponse) +def versions(request: Request): + return templates.TemplateResponse("versions.html", {"request": request}) + +@app.get("/support", response_class=HTMLResponse) +def support(request: Request): + return templates.TemplateResponse("support.html", {"request": request}) + +@app.get("/contributor", response_class=HTMLResponse) +def contributor(request: Request): + return templates.TemplateResponse("contributor.html", {"request": request}) + +@app.get("/reviews", response_class=HTMLResponse) +def reviews(request: Request): + return templates.TemplateResponse("reviews.html", {"request": request}) + +@app.get("/license", response_class=HTMLResponse) +def license(request: Request): + return templates.TemplateResponse("license.html", {"request": request}) + +@app.get("/404", response_class=HTMLResponse) +def not_found(request: Request): + return templates.TemplateResponse("404.html", {"request": request}) \ No newline at end of file diff --git a/website/models/feedback.py b/website/models/feedback.py new file mode 100644 index 00000000..8dec6bd5 --- /dev/null +++ b/website/models/feedback.py @@ -0,0 +1,6 @@ +from pydantic import BaseModel +class Feedback(BaseModel): + Name: str + Email: str + Message: str + Rating: int \ No newline at end of file diff --git a/website/pages/support.html b/website/pages/support.html deleted file mode 100644 index 4a01e67e..00000000 --- a/website/pages/support.html +++ /dev/null @@ -1,215 +0,0 @@ - - - - - - - Dataverse | Support - - - - - - - - - - - - - -
-
-
-
-
- - -
- -
- - - - - -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Home
Documentation
Versions
Support
Contributors
Reviews
License
Stunning Visuals
Github - Repository
-
-
- -
- - -
- -

Frequently Asked Questions

-
- What is Dataverse? -

Dataverse is an open-source software platform designed for tracking finances, visualizing data, - and integrating database management. It provides powerful tools for data visualization and - storage.

-
-
- What features does Dataverse offer? -

Dataverse offers features such as data visualization, financial tracking, chart generation, and - secure storage. It also allows users to download generated charts and manage data for future - use.

-
-
- Is Dataverse free to use? -

Yes, Dataverse is open-source and free to use. You can contribute to its development on GitHub. -

-
-
- How can I contribute to Dataverse? -

You can contribute by visiting the official Dataverse repository on GitHub and following the - contribution guidelines. We welcome developers, testers, and documentation writers.

-
-
- Where can I get support for using Dataverse? -

Support is available through our GitHub discussions page, community forums, and documentation. - Feel free to reach out for help!

-
-
-
-
-

Encountered an Issue?

- - -
-
-

Still have Doubts?

- - -
-
-
-

Support the Development of Dataverse

- As an open-source initiative, we depend on community support to sustain and enhance the project. Your - contributions will help us: -
    -
  • Develop New Features
  • -
  • Improve User Experience
  • -
  • Maintain the Platform
  • -
  • Foster Community Growth
  • -
  • Develop More Such Software
  • -
- -
-
-
-
- - - - - -
-
- - - - - - diff --git a/website/routes/feedback.py b/website/routes/feedback.py new file mode 100644 index 00000000..fec2a017 --- /dev/null +++ b/website/routes/feedback.py @@ -0,0 +1,17 @@ +from fastapi import APIRouter, Form +from schema.feedback import submit_feedback +from models.feedback import Feedback + +router = APIRouter() + +@router.post("/submit_feedback") +async def submit_feedback_route( + Name: str = Form(...), + Email: str = Form(...), + Message: str = Form(...), + Rating: int = Form(...), +): + # Convert the raw data into a Pydantic model + feedback = Feedback(Name=Name, Email=Email, Message=Message, Rating=Rating) + + return await submit_feedback(feedback) \ No newline at end of file diff --git a/website/schema/feedback.py b/website/schema/feedback.py new file mode 100644 index 00000000..33c15caf --- /dev/null +++ b/website/schema/feedback.py @@ -0,0 +1,21 @@ +from fastapi import HTTPException +from models.feedback import Feedback + +async def submit_feedback(feedback: Feedback): + try: + from main import feedback_collection + + feedback_data = feedback.dict() # Convert Pydantic model to dictionary + + result = await feedback_collection.insert_one(feedback_data) # Insert the feedback data into MongoDB + + # Check if insertion was successful and return a response + if result.inserted_id: + print(f"Inserted feedback with ID: {result.inserted_id}") + return {"success": True, "message": "Feedback submitted successfully."} + else: + raise HTTPException(status_code=400, detail="Failed to insert feedback.") + + except Exception as e: + print(f"Error: {e}") + raise HTTPException(status_code=500, detail="There was an error processing your feedback.") \ No newline at end of file diff --git a/website/scripts/beautifyReviews.js b/website/scripts/beautifyReviews.js index 28ae3f2b..9d6a080b 100644 --- a/website/scripts/beautifyReviews.js +++ b/website/scripts/beautifyReviews.js @@ -1,11 +1,11 @@ -fetch('../scripts/reviews.json') - .then(response => response.json()) - .then(data => { - const averageRating = calculateAverageRating(data); - displayAverageRating(averageRating); - displayReviews(data); - }) - .catch(error => console.error("Error loading reviews:", error)); +fetch("/scripts/reviews.json") + .then((response) => response.json()) + .then((data) => { + const averageRating = calculateAverageRating(data); + displayAverageRating(averageRating); + displayReviews(data); + }) + .catch((error) => console.error("Error loading reviews:", error)); // Function to calculate the average rating function calculateAverageRating(reviews) { diff --git a/website/scripts/script.js b/website/scripts/script.js index 13da2f33..b8f9b6c4 100644 --- a/website/scripts/script.js +++ b/website/scripts/script.js @@ -3,101 +3,130 @@ import { showModal, closeModal, attachToWindow } from "./sharedUtilities.js"; var windowFunctions = []; // Array of city names -var cities = ["Pune", "Moradabad", "Dehradun", "Rampur", "Delhi", "Coimbatore", "Riyadh", "Ahmedabad", "Kolkata", "Mumbai", "Jorhat", "Arrah", "Bhopal", "Bengalore", "Secunderabad", "Ludhiana", "Nagpur", "Lucknow", "Gorakhpur", "Bhilai", "Kanpur", "Panaji","Dhamtari","Vijaywada","Ujjain","Sydney"]; +var cities = [ + "Pune", + "Moradabad", + "Dehradun", + "Rampur", + "Delhi", + "Coimbatore", + "Riyadh", + "Ahmedabad", + "Kolkata", + "Mumbai", + "Jorhat", + "Arrah", + "Bhopal", + "Bengalore", + "Secunderabad", + "Ludhiana", + "Nagpur", + "Lucknow", + "Gorakhpur", + "Bhilai", + "Kanpur", + "Panaji", + "Dhamtari", + "Vijaywada", + "Ujjain", + "Sydney", +]; try { - var map = L.map('map', { + var map = L.map("map", { center: [22.7937, 77.9629], zoom: 4, - zoomControl: false - }); + zoomControl: false, + }); - // Add OpenStreetMap tile layer - L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { - attribution: '© OpenStreetMap contributors' + // Add OpenStreetMap tile layer + L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { + attribution: + '© OpenStreetMap contributors', }).addTo(map); var redIcon = L.icon({ - iconUrl: 'https://img1.picmix.com/output/stamp/normal/2/5/4/3/873452_376bb.png', + iconUrl: + "https://img1.picmix.com/output/stamp/normal/2/5/4/3/873452_376bb.png", iconSize: [20, 20], iconAnchor: [12, 12], popupAnchor: [1, -34], - shadowSize: [41, 41] + shadowSize: [41, 41], }); -} catch(e) { +} catch (e) { console.error(e); } // Your OpenCage API Key here -const OPENCAGE_API_KEY = '3f55c6e93c2c4b19ae45f1fd5db12cfc'; +const OPENCAGE_API_KEY = "3f55c6e93c2c4b19ae45f1fd5db12cfc"; // Function to get coordinates for a city and add a marker using OpenCage API async function addMarker(city) { - var url = `https://api.opencagedata.com/geocode/v1/json?q=${city}&key=${OPENCAGE_API_KEY}`; - - try { - const response = await fetch(url); - - if (!response.ok) throw new Error(`Network response was not ok: ${response.statusText}`); - - const data = await response.json(); - - if (data.results.length > 0) { - const lat = data.results[0].geometry.lat; - const lon = data.results[0].geometry.lng; - L.marker([lat, lon], { icon: redIcon }).addTo(map) - .bindPopup(city); - } else { - console.log("No results found for " + city); - } - } catch (error) { - console.error("Error fetching coordinates for " + city + ": " + error); + var url = `https://api.opencagedata.com/geocode/v1/json?q=${city}&key=${OPENCAGE_API_KEY}`; + + try { + const response = await fetch(url); + + if (!response.ok) + throw new Error(`Network response was not ok: ${response.statusText}`); + + const data = await response.json(); + + if (data.results.length > 0) { + const lat = data.results[0].geometry.lat; + const lon = data.results[0].geometry.lng; + L.marker([lat, lon], { icon: redIcon }).addTo(map).bindPopup(city); + } else { + console.log("No results found for " + city); } + } catch (error) { + console.error("Error fetching coordinates for " + city + ": " + error); + } } // Function to add markers with a delay to respect API limits async function addMarkersWithDelay(cities) { - for (let i = 0; i < cities.length; i++) { - await addMarker(cities[i]); - await new Promise(resolve => setTimeout(resolve, 1000)); // 1-second delay between requests - } + for (let i = 0; i < cities.length; i++) { + await addMarker(cities[i]); + await new Promise((resolve) => setTimeout(resolve, 1000)); // 1-second delay between requests + } } // Call the function to add markers addMarkersWithDelay(cities); - // /preloader js styling and other stuff needed for preload -window.addEventListener('DOMContentLoaded', () => { +window.addEventListener("DOMContentLoaded", () => { // Hide the loader after 3 seconds setTimeout(() => { - const loader = document.getElementById('video-loader'); - if(loader) { - loader.style.display = 'none'; - } + const loader = document.getElementById("video-loader"); + if (loader) { + loader.style.display = "none"; + } - displayCopyright(); + displayCopyright(); }, 3000); attachToWindow(windowFunctions); }); function topFunction() { - document.body.scrollTop = 0; - document.documentElement.scrollTop = 0; + document.body.scrollTop = 0; + document.documentElement.scrollTop = 0; } windowFunctions.push(topFunction); function changeCss() { - var top = document.getElementById("top"); - var scroll_icon = document.getElementById("scroll_icon"); - if(scroll_icon) { - (this.scrollY > 30) ? top.style.opacity = 1 : top.style.opacity = 0; - (this.scrollY > 0) ? scroll_icon.style.opacity = 0 : scroll_icon.style.opacity = 1; - } + var top = document.getElementById("top"); + var scroll_icon = document.getElementById("scroll_icon"); + if (scroll_icon) { + this.scrollY > 30 ? (top.style.opacity = 1) : (top.style.opacity = 0); + this.scrollY > 0 + ? (scroll_icon.style.opacity = 0) + : (scroll_icon.style.opacity = 1); + } } window.addEventListener("scroll", changeCss, false); - console.log("Tejas' Codes :)"); var l1 = document.getElementById("l1"); @@ -121,57 +150,43 @@ var download = document.getElementById("download_btn"); const lightButton = document.getElementById("lightButton"); const darkButton = document.getElementById("darkButton"); const technologies = document.getElementById("technologies"); -var footer=document.getElementById("footer"); -var links=document.getElementById("linksTejas"); -var endLogo=document.getElementById("end-logo"); - +var footer = document.getElementById("footer"); +var links = document.getElementById("linksTejas"); +var endLogo = document.getElementById("end-logo"); +var feedbackForm = document.getElementById("feedback-form"); let lastScrollTop = 0; const navbar = document.getElementById("navbar"); -window.addEventListener('scroll', () => { +window.addEventListener("scroll", () => { const scrollTop = window.pageYOffset || document.documentElement.scrollTop; if (scrollTop > lastScrollTop) { // Scroll down - navbar.style.top = '-60px'; - burger.style.top = '-60px'; - lines.style.top = '-60px'; - cross.style.top = '-60px'; + navbar.style.top = "-60px"; + burger.style.top = "-60px"; + lines.style.top = "-60px"; + cross.style.top = "-60px"; } else { // Scroll up - navbar.style.top = '0'; - burger.style.top = '10px'; - lines.style.top = '10px'; - cross.style.top = '10px'; + navbar.style.top = "0"; + burger.style.top = "10px"; + lines.style.top = "10px"; + cross.style.top = "10px"; } lastScrollTop = scrollTop; }); -window.onload = function() { - let feedbackField; - if(document.forms['feedback-form']) { - feedbackField = document.forms['feedback-form']['Message']; - - feedbackField.addEventListener('focus', () => { - checkFeedbackLength(feedbackField); - }) - - feedbackField.addEventListener('blur', () => { - document.getElementById('feedbackError').style.opacity = '0%'; - }) - } - -} - let lastScroll = 0; function progress() { var scroll = this.scrollY; - var scrollHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight; + var scrollHeight = + document.documentElement.scrollHeight - + document.documentElement.clientHeight; var percent = Math.round((scroll / scrollHeight) * 100); - document.getElementById("progress_bar").style.width = percent + 'vw'; -}; + document.getElementById("progress_bar").style.width = percent + "vw"; +} window.addEventListener("scroll", progress); @@ -199,94 +214,114 @@ function hide() { } windowFunctions.push(hide); - function updateIndicator(button) { const adjustment = (button.offsetHeight - indicator.offsetHeight) / 2; indicator.style.top = `${button.offsetTop + adjustment}px`; - const currentTheme = localStorage.getItem('theme'); - if (currentTheme === 'light') { - indicator.style.backgroundImage = "radial-gradient(circle, rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0))"; // Black glow for light mode - // indicator.style.boxShadow = "0 0 5px 3px rgba(0, 0, 0, 0.7)"; // Subtle black glow + const currentTheme = localStorage.getItem("theme"); + if (currentTheme === "light") { + indicator.style.backgroundImage = + "radial-gradient(circle, rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0))"; // Black glow for light mode + // indicator.style.boxShadow = "0 0 5px 3px rgba(0, 0, 0, 0.7)"; // Subtle black glow } else { - indicator.style.backgroundImage = "radial-gradient(circle, rgba(255, 255, 255, 0.7), rgba(0, 0, 0, 0))"; // White glow for dark mode - // indicator.style.boxShadow = "0 0 5px 3px rgba(255, 255, 255, 0.7)"; // Subtle white glow + indicator.style.backgroundImage = + "radial-gradient(circle, rgba(255, 255, 255, 0.7), rgba(0, 0, 0, 0))"; // White glow for dark mode + // indicator.style.boxShadow = "0 0 5px 3px rgba(255, 255, 255, 0.7)"; // Subtle white glow } } windowFunctions.push(updateIndicator); function light(flag) { - localStorage.setItem('theme', 'light'); - document.body.classList.remove('dark-mode'); - document.body.classList.add('light-mode'); - if(document.getElementById("map") && tags && contribute && shadow && technologies) { + localStorage.setItem("theme", "light"); + document.body.classList.remove("dark-mode"); + document.body.classList.add("light-mode"); + if ( + document.getElementById("map") && + tags && + contribute && + shadow && + technologies + ) { document.getElementById("map").style.filter = "none"; document.getElementById("map").style.zIndex = 0; tags.style.borderColor = "black"; tags.style.backgroundColor = "#00000000"; - contribute.style.filter="invert(1)"; - shadow.style.backgroundImage = "linear-gradient(115deg, #00000000,#f9f9f9,#00000000)"; - technologies.style.border="1px solid #000000"; + contribute.style.filter = "invert(1)"; + shadow.style.backgroundImage = + "linear-gradient(115deg, #00000000,#f9f9f9,#00000000)"; + technologies.style.border = "1px solid #000000"; /*footer.style.backgroundColor="#ffffff"; footer.style.color="#000000"; links.style.backgroundColor="#ffffff"; links.style.color="#000000";*/ - footer.style.filter="invert(1)"; - links.style.filter="invert(1)"; - endLogo.style.filter="invert(1)"; + footer.style.filter = "invert(1)"; + links.style.filter = "invert(1)"; + endLogo.style.filter = "invert(1)"; } - indicator.style.backgroundImage = "radial-gradient(rgba(0,0,0, 0.608),#00000000,#00000000)"; + indicator.style.backgroundImage = + "radial-gradient(rgba(0,0,0, 0.608),#00000000,#00000000)"; const lightButton = document.getElementById("lightButton"); updateIndicator(lightButton); // Update the indicator position and style for the light button } windowFunctions.push(light); function dark(flag) { - if(document.getElementById("map") && tags && contribute && shadow && technologies) { - document.getElementById("map").style.filter = "invert(1) hue-rotate(180deg) brightness(1.7)"; + if ( + document.getElementById("map") && + tags && + contribute && + shadow && + technologies + ) { + document.getElementById("map").style.filter = + "invert(1) hue-rotate(180deg) brightness(1.7)"; tags.style.borderColor = "rgba(255, 255, 255, 0.323)"; tags.style.backgroundColor = "#00000000"; - shadow.style.backgroundImage = "linear-gradient(115deg, #00000000,#000000d4,#00000000)"; - contribute.style.filter="invert(0)"; - technologies.style.border="1px solid #ffffff44"; + shadow.style.backgroundImage = + "linear-gradient(115deg, #00000000,#000000d4,#00000000)"; + contribute.style.filter = "invert(0)"; + technologies.style.border = "1px solid #ffffff44"; /*footer.style.backgroundColor="#000000"; footer.style.color="#ffffff"; links.style.backgroundColor="#000000"; links.style.color="#ffffff";*/ - footer.style.filter="invert(0)"; - links.style.filter="invert(0)"; - endLogo.style.filter="invert(0)"; + footer.style.filter = "invert(0)"; + links.style.filter = "invert(0)"; + endLogo.style.filter = "invert(0)"; } - localStorage.setItem('theme', 'dark'); - indicator.style.backgroundImage = "radial-gradient(rgba(255,255,255, 0.608),#00000000,#00000000)"; - document.body.classList.remove('light-mode'); - document.body.classList.add('dark-mode'); + localStorage.setItem("theme", "dark"); + indicator.style.backgroundImage = + "radial-gradient(rgba(255,255,255, 0.608),#00000000,#00000000)"; + document.body.classList.remove("light-mode"); + document.body.classList.add("dark-mode"); const darkButton = document.getElementById("darkButton"); updateIndicator(darkButton); // Update the indicator position and style for the dark button - } windowFunctions.push(dark); function systemDefault() { - const theme = localStorage.getItem('theme'); + const theme = localStorage.getItem("theme"); const defaultButton = document.getElementById("defaultButton"); - if (theme === 'light') { - light(true); - updateIndicator(document.getElementById("lightButton")); // Ensure the indicator moves to the light button - } else if (theme === 'dark') { - dark(true); - updateIndicator(document.getElementById("darkButton")); // Ensure the indicator moves to the dark button + if (theme === "light") { + light(true); + updateIndicator(document.getElementById("lightButton")); // Ensure the indicator moves to the light button + } else if (theme === "dark") { + dark(true); + updateIndicator(document.getElementById("darkButton")); // Ensure the indicator moves to the dark button } else { - // Fallback based on system preference - if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { - dark(true); - updateIndicator(document.getElementById("darkButton")); // If system preference is dark - } else { - light(true); - updateIndicator(document.getElementById("lightButton")); // If system preference is light - } + // Fallback based on system preference + if ( + window.matchMedia && + window.matchMedia("(prefers-color-scheme: dark)").matches + ) { + dark(true); + updateIndicator(document.getElementById("darkButton")); // If system preference is dark + } else { + light(true); + updateIndicator(document.getElementById("lightButton")); // If system preference is light + } } // Move indicator to the default button when explicitly selected @@ -308,7 +343,6 @@ document.getElementById("defaultButton").addEventListener("click", () => { updateIndicator(document.getElementById("defaultButton")); }); - let scrollSpeed = 2; let currentSpeed = scrollSpeed; let autoScroll = true; @@ -316,50 +350,53 @@ let scrollTimeout; // Function to scroll the 'examples' and 'examples2' div automatically function autoScrollExamples() { - if(examples, examples2) { + if ((examples, examples2)) { if (autoScroll) { examples.scrollLeft += currentSpeed; examples2.scrollLeft += currentSpeed * 1.2; - + // Looping the scroll if (examples.scrollLeft + examples.clientWidth >= examples.scrollWidth) { - examples.scrollLeft = 0; + examples.scrollLeft = 0; } - if (examples2.scrollLeft + examples2.clientWidth >= examples2.scrollWidth) { - examples2.scrollLeft = 0; + if ( + examples2.scrollLeft + examples2.clientWidth >= + examples2.scrollWidth + ) { + examples2.scrollLeft = 0; } } } } - setInterval(autoScrollExamples, 30); // Smoothly adjust scroll speed const adjustSpeed = (targetSpeed) => { - clearInterval(scrollTimeout); - scrollTimeout = setInterval(() => { - if (currentSpeed !== targetSpeed) { - currentSpeed += (targetSpeed > currentSpeed ? 0.1 : -0.3); - currentSpeed = Math.abs(currentSpeed - targetSpeed) < 0.1 ? targetSpeed : currentSpeed; - } else { - clearInterval(scrollTimeout); - } - }, 30); + clearInterval(scrollTimeout); + scrollTimeout = setInterval(() => { + if (currentSpeed !== targetSpeed) { + currentSpeed += targetSpeed > currentSpeed ? 0.1 : -0.3; + currentSpeed = + Math.abs(currentSpeed - targetSpeed) < 0.1 ? targetSpeed : currentSpeed; + } else { + clearInterval(scrollTimeout); + } + }, 30); }; // Hovering behavior with smooth stop/resume const stopAutoScroll = () => { - autoScroll = false; - adjustSpeed(0); + autoScroll = false; + adjustSpeed(0); }; const startAutoScroll = () => { - autoScroll = true; - adjustSpeed(scrollSpeed); + autoScroll = true; + adjustSpeed(scrollSpeed); }; -if(examples, examples2) { +if ((examples, examples2)) { examples.addEventListener("mouseenter", stopAutoScroll); examples.addEventListener("mouseleave", startAutoScroll); examples2.addEventListener("mouseenter", stopAutoScroll); @@ -371,93 +408,131 @@ window.addEventListener("scroll", progress); /*CHANGING DIRECTION OF AEROPLANE*/ function updateAngle() { - let randomAngle = Math.floor(Math.random() * 360) + 'deg'; - document.documentElement.style.setProperty('--angle', randomAngle); + let randomAngle = Math.floor(Math.random() * 360) + "deg"; + document.documentElement.style.setProperty("--angle", randomAngle); } setInterval(updateAngle, 10000); -// FORM VALIDATING FUNCTION -function validateForm() { - const form = document.getElementById('feedback-form'); +/*VALIDATE FORM AND CALL BACKEND*/ +window.onload = function () { + let feedbackField; + if (document.forms["feedback-form"]) { + feedbackField = document.forms["feedback-form"]["Message"]; + + feedbackField.addEventListener("focus", () => { + checkFeedbackLength(feedbackField); + }); + + feedbackField.addEventListener("blur", () => { + document.getElementById("feedbackError").style.opacity = "0%"; + }); + } +}; + +function validateForm(event) { + event.preventDefault(); // Prevent form from submitting by default + const nameInput = document.querySelector('[name="Name"]'); const emailInput = document.querySelector('[name="Email"]'); const messageInput = document.querySelector('textarea[name="Message"]'); - const rating = document.querySelector('[name=rating]:checked'); + const rating = document.querySelector('[name="rating"]:checked'); // Get the checked rating - if (nameInput.value === '') { - alert('Please enter your name.'); - return false; + if (nameInput.value === "") { + alert("Please enter your name."); + return false; } - + if (!isValidEmail(emailInput.value)) { - alert('Please enter a valid email address.'); + alert("Please enter a valid email address."); return false; } - - if (messageInput.value === '') { - alert('Please enter your message.'); - return false; + + if (messageInput.value === "") { + alert("Please enter your message."); + return false; } - if (rating === false || rating === null) { + + if (!rating) { alert("Please select a rating."); - return false; - } + return false; + } - if(!checkFeedbackLength(messageInput)) { + if (!checkFeedbackLength(messageInput)) { return false; } - - const formData = { - Name: nameInput.value, - Email: emailInput.value, - Message: messageInput.value - }; - localStorage.setItem('reviewData', JSON.stringify(formData)); - - nameInput.value = ''; - emailInput.value = ''; - messageInput.value = ''; - rating.checked = false; - - showModal('Thank you for your feedback!', 'submit feedback'); - - return false; -} + + const formData = new FormData(); + formData.append("Name", nameInput.value); + formData.append("Email", emailInput.value); + formData.append("Message", messageInput.value); + formData.append("Rating", rating.value); + + // Send the form data to FastAPI using fetch + fetch("http://127.0.0.1:8000/submit_feedback", { + method: "POST", + body: formData, // Send as form data + }) + .then((response) => response.json()) // Parse JSON response from FastAPI + .then((data) => { + // Handle the response from the FastAPI backend (success or error) + if (data.success) { + showModal("Thank you for your feedback!", "submit feedback"); + // Clear form fields after submission + nameInput.value = ""; + emailInput.value = ""; + messageInput.value = ""; + const selectedRating = document.querySelector( + '[name="rating"]:checked' + ); + if (selectedRating) { + selectedRating.checked = false; // Uncheck the selected radio button + } + } else { + alert("Something went wrong, please try again."); + } + }) + .catch((error) => { + console.error("Error:", error); + alert("An error occurred. Please try again."); + }); + + return false; +} function checkFeedbackLength(input) { - if(input.value.length < 10) { - document.getElementById('feedbackError').style.opacity = '100%'; - return false; + if (input.value.length < 10) { + document.getElementById("feedbackError").style.opacity = "100%"; + return false; } else { - document.getElementById('feedbackError').style.opacity = '0%'; - return true; + document.getElementById("feedbackError").style.opacity = "0%"; + return true; } } -windowFunctions.push(checkFeedbackLength); - -// EMAIL VALIDATING FUNCTION +window.checkFeedbackLength = checkFeedbackLength; +window.validateForm = validateForm; + +// EMAIL VALIDATING FUNCTION function isValidEmail(email) { const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; - return emailRegex.test(email); + return emailRegex.test(email); } // Display the current year in the copyright section function displayCopyright() { const year = new Date().getFullYear(); - if( document.getElementById("copyright").innerText = year) { + if ((document.getElementById("copyright").innerText = year)) { document.getElementById("copyright").innerText = year; } } -ScrollReveal().reveal('.reveal', { - distance: '150px', +ScrollReveal().reveal(".reveal", { + distance: "150px", duration: 600, - easing: 'ease-in-out', - origin: 'bottom', + easing: "ease-in-out", + origin: "bottom", interval: 100, // Delay between revealing multiple elements }); windowFunctions.push(ScrollReveal); - -export {windowFunctions}; \ No newline at end of file +export { windowFunctions }; diff --git a/website/styles/reviews.css b/website/styles/reviews.css index e5206f16..736af63e 100644 --- a/website/styles/reviews.css +++ b/website/styles/reviews.css @@ -1,188 +1,194 @@ /* Basic resets */ * { - margin: 0; - padding: 0; - box-sizing: border-box; + margin: 0; + padding: 0; + box-sizing: border-box; } body { - font-family: 'Arial', sans-serif; - background-color: #000000; - color: #ffffff; + font-family: "Arial", sans-serif; + background-color: #000000; + color: #ffffff; } .reviewContainer { - text-align: center; - margin-top: 90px; + text-align: center; + margin-top: 90px; } .reviewHeading { - color: #ffffff; - font-size: 30px; - font-weight: 600; - margin-bottom: 20px; + color: #ffffff; + font-size: 30px; + font-weight: 600; + margin-bottom: 20px; } #average-rating { - padding: 20px; - text-align: center; - font-size: 30px; - margin-bottom: 20px; - width: 90vw; - margin-left: 5vw; - border: 2px solid #dddddd35; - border-radius: 10px; + padding: 20px; + text-align: center; + font-size: 30px; + margin-bottom: 20px; + width: 90vw; + margin-left: 5vw; + border: 2px solid #dddddd35; + border-radius: 10px; } .average-rating-text { - font-size: 22px; + font-size: 22px; } .average-star-rating { - color: gold; + color: gold; } .filled { - color: gold; - text-shadow: 0 0 10px gold; + color: gold; + text-shadow: 0 0 10px gold; } .half { - background: linear-gradient(to right, gold, gold, black, black, black); - background-clip: text; - -webkit-text-fill-color: transparent; - text-shadow: -3px 0 10px gold; + background: linear-gradient(to right, gold, gold, black, black, black); + background-clip: text; + -webkit-text-fill-color: transparent; + text-shadow: -3px 0 10px gold; } .empty { - margin-left: -25px; + margin-left: -25px; } /* Review Container */ .reviews-container { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); - gap: 20px; - padding-bottom: 70px; - width: 90vw; - margin-left: 5vw; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 20px; + padding-bottom: 70px; + width: 90vw; + margin-left: 5vw; } .review { - display: flex; - flex-direction: column; - justify-content: space-between; - position: relative; - background-color: #50505032; - border: 1px solid rgba(202, 202, 202, 0.178); - padding: 15px; - border-radius: 8px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - transition-duration: 0.3s; + display: flex; + flex-direction: column; + justify-content: space-between; + position: relative; + background-color: #50505032; + border: 1px solid rgba(202, 202, 202, 0.178); + padding: 15px; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + transition-duration: 0.3s; } .review-header { - display: flex; - align-items: center; - margin-bottom: 15px; + display: flex; + align-items: center; + margin-bottom: 15px; } .review-header h3 { - font-size: 1.3em; - color: #cbcbcb; - border-bottom: 1px solid rgba(255, 255, 255, 0.145); - width: 100%; - text-align: left; - padding-bottom: 5px; + font-size: 1.3em; + color: #cbcbcb; + border-bottom: 1px solid rgba(255, 255, 255, 0.145); + width: 100%; + text-align: left; + padding-bottom: 5px; } .review-content { - position: relative; - color: #ffffff; - font-size: 1em; - text-align: left; - line-height: 1.6; - display: flex; - flex-direction: column; - height: 100%; + position: relative; + color: #ffffff; + font-size: 1em; + text-align: left; + line-height: 1.6; + display: flex; + flex-direction: column; + height: 100%; } .review-date { - float: left; - font-size: 12px; - color: #888; - font-style: italic; + float: left; + font-size: 12px; + color: #888; + font-style: italic; } .review-footer { - position: relative; - /* Keep it within the parent review div */ - height: 30px; - /* Adjust as needed */ - display: flex; - align-items: flex-end; - /* Push the date to the bottom */ - justify-content: space-between; - /* Align the date to the left */ - text-align: right; + position: relative; + /* Keep it within the parent review div */ + height: 30px; + /* Adjust as needed */ + display: flex; + align-items: flex-end; + /* Push the date to the bottom */ + justify-content: space-between; + /* Align the date to the left */ + text-align: right; } .star-rating { - background: linear-gradient(to right, #ff0000, #fcc100); - background-clip: text; - -webkit-text-fill-color: transparent; - text-shadow: 0 0 10px gold; + background: linear-gradient(to right, #ff0000, #fcc100); + background-clip: text; + -webkit-text-fill-color: transparent; + text-shadow: 0 0 10px gold; } .star-rating star { - margin-right: 5px; - font-size: 1.5rem; + margin-right: 5px; + font-size: 1.5rem; } .goBack { - cursor: pointer; - position: fixed; - bottom: 20px; - left: 20px; - padding: 10px; - background-color: #d0d0d032; - backdrop-filter: blur(7px); - color: rgb(105, 105, 105); - border: none; - border-radius: 7px; - box-shadow: 0 0 5px black; + cursor: pointer; + position: fixed; + bottom: 20px; + left: 20px; + padding: 10px; + background-color: #d0d0d032; + backdrop-filter: blur(7px); + color: rgb(105, 105, 105); + border: none; + border-radius: 7px; + box-shadow: 0 0 5px black; } - .review:hover::before { - opacity: 1; + opacity: 1; } .review::before, .review::after { - content: ""; - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; - border-radius: inherit; - z-index: 2; - opacity: 0; - transition: opacity 500ms; + content: ""; + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + border-radius: inherit; + z-index: 2; + opacity: 0; + transition: opacity 500ms; } .review::before { - background: radial-gradient(500px circle at var(--mouse-x) var(--mouse-y), rgba(255, 255, 255, 0.09), transparent 40%); - z-index: 3; + background: radial-gradient( + 500px circle at var(--mouse-x) var(--mouse-y), + rgba(255, 255, 255, 0.09), + transparent 40% + ); + z-index: 3; } .review::after { - background: radial-gradient(300px circle at var(--mouse-x) var(--mouse-y), rgba(255, 255, 255, 0.3), transparent 40%); - z-index: 1; + background: radial-gradient( + 300px circle at var(--mouse-x) var(--mouse-y), + rgba(255, 255, 255, 0.3), + transparent 40% + ); + z-index: 1; } - -#reviews-container:hover>.card:after { - opacity: 1; -} \ No newline at end of file +#reviews-container:hover > .card:after { + opacity: 1; +} diff --git a/website/styles/style.css b/website/styles/style.css index 1b498dcf..eae29780 100644 --- a/website/styles/style.css +++ b/website/styles/style.css @@ -1209,7 +1209,40 @@ input { padding: 10px; border: 1px solid rgba(128, 128, 128, 0.352); } +/* Infinite scroll */ +#examples, #examples2 { + display: flex; + flex-wrap: nowrap; + overflow: hidden; + width: 100%; +} + +.examples img { + margin-right: 20px; + width: 200px; +} + +#examples:not(:hover) img,#examples2:not(:hover) img{ + animation: scrollImages 10s linear infinite; +} + +#examples:hover img, #examples2:hover img { + animation-play-state: paused; +} + +#examples img:hover, #examples2 img:hover { + animation-play-state: paused; +} + +@keyframes scrollImages { + 0% { + transform: translateX(0); + } + 100% { + transform: translateX(-100%); + } +} /*===========================RESPONSIVE===================================*/ @media screen and (max-width: 660px) { .navbarButton { diff --git a/website/templates/404.html b/website/templates/404.html new file mode 100644 index 00000000..f53f2ba2 --- /dev/null +++ b/website/templates/404.html @@ -0,0 +1,29 @@ + + + + + + Page Not Found :( + + + + +
+ 44 +
+
Oops! Page not found.
+
+ We couldn't find the page you requested. It might be unavailable at the + moment or have a different URL. +
+ Return to Home + + diff --git a/website/pages/contributor.html b/website/templates/contributor.html similarity index 75% rename from website/pages/contributor.html rename to website/templates/contributor.html index 3496910d..86153c07 100644 --- a/website/pages/contributor.html +++ b/website/templates/contributor.html @@ -14,7 +14,7 @@