Upload files to "/"

This commit is contained in:
Mohammed Nuseirat 2024-10-26 02:09:49 +03:00
commit 76eb07896c
5 changed files with 820 additions and 0 deletions

41
README.md Normal file
View File

@ -0,0 +1,41 @@
# AOU GPA Calculator
Visit the site here: [AOU GPA Calculator](https://aougpa.netlify.app/)
## Overview
The AOU GPA Calculator is a web-based tool designed to assist students at the Arab Open University (AOU) in calculating their Grade Point Average (GPA) based on the university's grading system. This calculator provides a user-friendly interface for students to input their grades and study hours, offering a quick and accurate assessment of their academic performance.
![لقطة شاشة 2024-10-24 040541](https://github.com/user-attachments/assets/1a5d73ac-6167-4503-a07f-ed8d0f0286f9)
## Features
- **Dynamic Subject Input**:
Easily add subjects with individual grade and study hour inputs. Remove specific subjects to tailor your GPA calculation.
- **Intuitive User Interface**:
Clear and straightforward design for efficient use. Responsive layout for a seamless experience on various devices.
- **AOU Grading System**:
Follows the AOU grading system, providing accurate GPA calculations. Corresponding grades include A, B+, B, C+, C, D, and F.
## How to Use
1. **Add Subjects**: Click the "Add Subject" button to input your grades and study hours for each subject.
2. **Calculate GPA**: Use the "Calculate GPA" button to obtain your GPA based on the entered subjects.
3. **Remove Subjects**: Click the "Remove All" button to clear all subjects and start fresh.
## Contributing
Contributions are welcome! If you have ideas for improvements, bug fixes, or additional features, feel free to open an issue or submit a pull request.
## Author
This GPA Calculator was created by **Mohammed Nuseirat**. Connect with me on [LinkedIn](https://www.linkedin.com/in/mohammednuseirat/) and [X](https://x.com/MohaNuseirat).
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

35
Update2.0.md Normal file
View File

@ -0,0 +1,35 @@
## Update 2.0 - AOU GPA Calculator
### Overview
In this update, I have introduced several new features and enhancements to improve the user experience of the AOU GPA Calculator.
The primary goal of this release is to provide users with a more intuitive interface and streamline the process of calculating GPA.
## New
![لقطة شاشة 2024-10-24 040541](https://github.com/user-attachments/assets/1a5d73ac-6167-4503-a07f-ed8d0f0286f9)
## Old
![لقطة شاشة 2024-10-23 215500](https://github.com/user-attachments/assets/3a695d15-9d73-4d18-b15b-aae9b42d4891)
### New Features
- **Dynamic Subject Input**: Users can now add multiple subjects dynamically, allowing for a more personalized and flexible GPA calculation experience. Each subject can be easily added and removed as needed.
- **Enhanced Button Functionality**:
- **Calculate GPA**: A dedicated button for calculating GPA has been implemented, making it easier for users to perform calculations with a single click.
- **Remove All Subjects**: Users can now clear all subject inputs with a single button click, providing a quick way to reset the form.
- **Improved User Interface**:
- The overall design has been refined with better spacing, layout adjustments, and a more modern aesthetic.
- The input fields have been styled for improved accessibility and usability, including focused states for better user feedback.
- **Responsive Design**: The application has been optimized for mobile devices, ensuring a seamless experience across various screen sizes.
### Visual Updates
- The logo and overall color scheme have been updated to enhance visual appeal and align with modern design trends.
- Buttons have been styled with hover effects to provide interactive feedback.
### Author
This update was developed by **Mohammed Nuseirat**.
### Future Plans
I am committed to continuously improving the AOU GPA Calculator. In future updates, I plan to explore additional features such as GPA history tracking, a dark mode option, and more detailed grading scales.

121
index.html Normal file
View File

@ -0,0 +1,121 @@
<!-- Created by Mohammed Nuseirat -->
<!DOCTYPE html>
<!-- Setting language to English -->
<html lang="en">
<head>
<!-- Character encoding for Unicode support -->
<meta charset="UTF-8">
<!-- Responsive viewport settings for mobile devices -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Page title showing app name and institution -->
<title>GPA Calculator | AOU</title>
<!-- Link to custom CSS stylesheet -->
<link rel="stylesheet" href="style.css">
<!-- Font Awesome icons library -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
</head>
<body>
<!-- Main container wrapper -->
<main class="container">
<!-- Logo section with glass effect -->
<div class="logo-container glass-effect">
<!-- University logo image -->
<img src="https://www.arabou.edu.sa/assets/common/images/logo-footer-ar.png" alt="AOU Logo" class="logo">
</div>
<!-- Main calculator container with glass effect -->
<div class="calculator-container glass-effect">
<!-- Arabic title for GPA calculator -->
<h1>حساب المعدل التراكمي للجامعة العربية المفتوحة</h1>
<!-- Previous GPA input section -->
<div class="previous-gpa-section">
<div class="input-group">
<!-- Previous GPA input field -->
<div class="input-wrapper">
<label for="prev-gpa"></label>
<input type="number"
id="prev-gpa"
step="0.01"
min="0"
max="4"
placeholder="Enter previous GPA / المعدل السابق">
</div>
<!-- Previous hours input field -->
<div class="input-wrapper">
<label for="prev-hours"></label>
<input type="number"
id="prev-hours"
min="0"
placeholder="Enter previous hours / الساعات السابقة">
</div>
</div>
</div>
<!-- Container for dynamically added subjects -->
<div id="subjects-container" class="subjects-container">
<!-- Subjects will be added here dynamically via JavaScript -->
</div>
<!-- Action buttons group -->
<div class="button-group">
<!-- Add subject button -->
<button class="btn btn-primary" onclick="addSubject()">
<i class="fas fa-plus"></i>
Add Subject
</button>
<!-- Calculate GPA button -->
<button class="btn btn-success" onclick="calculateGPA()">
<i class="fas fa-calculator"></i>
Calculate
</button>
<!-- Clear all inputs button -->
<button class="btn btn-danger" onclick="clearAll()">
<i class="fas fa-trash"></i>
Clear All
</button>
</div>
<!-- Results container (hidden by default) -->
<div id="result" class="result glass-effect hidden">
<!-- Results will be displayed here via JavaScript -->
</div>
</div>
<!-- Footer section -->
<footer class="footer glass-effect">
<div class="footer-content">
<!-- Developer credit -->
<p class="footer-text">Developed by Mohammed Nuseirat
<br>• v2.0</p>
<!-- Social media links -->
<div class="social-links">
<!-- GitHub profile link -->
<a href="https://github.com/nuseirat" target="_blank" class="social-link">
<i class="fab fa-github fa-2x"></i>
</a>
<!-- LinkedIn profile link -->
<a href="https://linkedin.com/in/mohammednuseirat" target="_blank" class="social-link">
<i class="fab fa-linkedin fa-2x"></i>
</a>
<!-- Twitter/X profile link -->
<a href="https://x.com/mohanuseirat" target="_blank" class="social-link">
<i class="fa-brands fa-x-twitter fa-2x"></i>
<!-- Website link -->
<a href="https://nuseirat.github.io/" target="_blank" class="social-link">
<i class="fas fa-globe fa-2x"></i>
</a>
</div>
</div>
</footer>
</main>
<!-- JavaScript libraries and scripts -->
<!-- GSAP animation library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<!-- Custom JavaScript file -->
<script src="script.js"></script>
<!-- Font Awesome kit -->
<script src="https://kit.fontawesome.com/86e812f22e.js" crossorigin="anonymous"></script>
</body>
</html>

262
script.js Normal file
View File

@ -0,0 +1,262 @@
// Created by Mohammed Nuseirat
// script.js
document.addEventListener("DOMContentLoaded", function () {
// Array to store all subject input elements
const subjects = [];
// Add Subject Button Handler
window.addSubject = function() {
// Create container for new subject inputs
const subjectInput = document.createElement("div");
subjectInput.className = "subject";
// Create grade input field
const gradeInput = document.createElement("input");
gradeInput.type = "text";
gradeInput.placeholder = "Enter grade (A, B+, B, C+, C, D, F)";
gradeInput.className = "grade-input";
// Create credit hours input field
const hoursInput = document.createElement("input");
hoursInput.type = "number";
hoursInput.placeholder = "Enter hours";
hoursInput.className = "hours-input";
hoursInput.min = "1";
// Create remove button with icon
const removeButton = document.createElement("button");
removeButton.innerHTML = '<i class="fas fa-times"></i>';
removeButton.className = "remove-button";
// Add animation and removal functionality to remove button
removeButton.addEventListener("click", function () {
gsap.to(subjectInput, {
duration: 0.3,
opacity: 0,
y: -20,
ease: 'power2.in',
onComplete: () => {
subjectInput.remove();
updateSubjectsArray();
}
});
});
// Assemble the subject input container
subjectInput.appendChild(gradeInput);
subjectInput.appendChild(hoursInput);
subjectInput.appendChild(removeButton);
// Add to DOM and subjects array
document.getElementById("subjects-container").appendChild(subjectInput);
subjects.push(subjectInput);
// Animate new subject entry
gsap.from(subjectInput, {
duration: 0.5,
opacity: 0,
y: 20,
ease: 'power2.out'
});
// Validate grade input
gradeInput.addEventListener('input', function() {
this.value = this.value.toUpperCase();
const validGrades = ['A', 'B+', 'B', 'C+', 'C', 'D', 'F'];
if (!validGrades.includes(this.value) && this.value !== '') {
this.classList.add('invalid');
} else {
this.classList.remove('invalid');
}
});
// Validate hours input
hoursInput.addEventListener('input', function() {
if (this.value < 1) {
this.classList.add('invalid');
} else {
this.classList.remove('invalid');
}
});
};
// Clear All Button Handler
window.clearAll = function() {
const subjectInputs = document.querySelectorAll(".subject");
// Animate removal of all subjects
gsap.to(subjectInputs, {
duration: 0.3,
opacity: 0,
y: -20,
stagger: 0.1,
ease: 'power2.in',
onComplete: () => {
// Clear all inputs and results
document.getElementById("subjects-container").innerHTML = '';
subjects.length = 0;
document.getElementById("prev-gpa").value = "";
document.getElementById("prev-hours").value = "";
document.getElementById("result").innerHTML = "";
document.getElementById("result").classList.add("hidden");
}
});
// Animate calculator container
gsap.to('.calculator-container', {
duration: 0.2,
scale: 0.98,
ease: 'power2.in',
yoyo: true,
repeat: 1
});
};
// Calculate GPA Button Handler
window.calculateGPA = function() {
// Validate all inputs before calculation
const invalidInputs = document.querySelectorAll('.invalid');
if (invalidInputs.length > 0) {
showResult("Please correct invalid inputs before calculating", "error");
return;
}
// Calculate totals
const totalGradePoints = calculateTotalGradePoints();
const totalHours = calculateTotalHours();
// Get previous GPA data
const prevGPA = parseFloat(document.getElementById("prev-gpa").value) || 0;
const prevHours = parseFloat(document.getElementById("prev-hours").value) || 0;
// Validate previous GPA
if (prevGPA > 4) {
showResult("Previous GPA cannot be greater than 4.0", "error");
return;
}
// Calculate cumulative statistics
const cumulativeGradePoints = totalGradePoints + (prevGPA * prevHours);
const cumulativeTotalHours = totalHours + prevHours;
// Validate hours
if (totalHours === 0 && prevHours === 0) {
showResult("Please enter valid grades and hours", "error");
return;
}
// Calculate GPAs
const currentGPA = totalHours === 0 ? 0 : totalGradePoints / totalHours;
const cumulativeGPA = cumulativeTotalHours === 0 ? 0 : cumulativeGradePoints / cumulativeTotalHours;
// Format and display results
const resultHTML = `
<div class="result-content">
<div class="result-item">
<span class="result-label">Semester Hours:</span>
<span class="result-value">${totalHours}</span>
</div>
<div class="result-item">
<span class="result-label">Total Hours:</span>
<span class="result-value">${cumulativeTotalHours}</span>
</div>
<div class="result-item">
<span class="result-label">Semester GPA:</span>
<span class="result-value">${currentGPA.toFixed(2)}</span>
</div>
<div class="result-item">
<span class="result-label">Cumulative GPA:</span>
<span class="result-value">${cumulativeGPA.toFixed(2)}</span>
</div>
</div>
`;
showResult(resultHTML);
// Animate result items
gsap.from('.result-item', {
duration: 0.5,
opacity: 0,
y: 20,
stagger: 0.1,
ease: 'power2.out'
});
};
// Helper function to calculate total grade points
function calculateTotalGradePoints() {
let totalGradePoints = 0;
subjects.forEach(subject => {
const grade = subject.querySelector(".grade-input").value;
const hours = parseFloat(subject.querySelector(".hours-input").value) || 0;
const gradePoint = getGradePoint(grade);
totalGradePoints += (gradePoint * hours);
});
return totalGradePoints;
}
// Helper function to calculate total hours
function calculateTotalHours() {
let totalHours = 0;
subjects.forEach(subject => {
const hours = parseFloat(subject.querySelector(".hours-input").value) || 0;
totalHours += hours;
});
return totalHours;
}
// Convert letter grades to grade points
function getGradePoint(grade) {
const gradePoints = {
'A': 4,
'B+': 3.5,
'B': 3,
'C+': 2.5,
'C': 2,
'D': 1.5,
'F': 0
};
return gradePoints[grade.toUpperCase()] || 0;
}
// Display result with animation
function showResult(message, type = "success") {
const resultDiv = document.getElementById("result");
resultDiv.innerHTML = message;
resultDiv.classList.remove("hidden");
resultDiv.style.opacity = "0";
// Animate result appearance
gsap.to(resultDiv, {
duration: 0.5,
opacity: 1,
y: 0,
ease: 'power2.out'
});
// Scroll result into view
resultDiv.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
// Update subjects array after removal
function updateSubjectsArray() {
subjects.length = 0;
document.querySelectorAll(".subject").forEach(subject => subjects.push(subject));
}
// Validate previous GPA input
const prevGPAInput = document.getElementById("prev-gpa");
prevGPAInput.addEventListener('input', function() {
if (this.value > 4) {
this.classList.add('invalid');
} else {
this.classList.remove('invalid');
}
});
});

361
style.css Normal file
View File

@ -0,0 +1,361 @@
/* Created by Mohammed Nuseirat*/
/* Define CSS custom properties (variables) for consistent theming across the application */
:root {
--primary-bg: linear-gradient(135deg, #020617, #1e293b); /* Dark gradient background */
--glass-bg: rgba(30, 41, 59, 0.7); /* Semi-transparent background for glass effect */
--text-color: #e2e8f0; /* Light text color for dark theme */
--input-bg: rgba(30, 41, 59, 0.8); /* Slightly darker background for input fields */
--btn-primary: #1e3a8a; /* Deep blue for primary buttons */
--btn-success: #059669; /* Green for success buttons */
--btn-danger: #991b1b; /* Red for danger/delete buttons */
--shadow: 0 8px 32px rgba(0, 0, 0, 0.3); /* Subtle shadow for depth */
--border: rgba(255, 255, 255, 0.1); /* Light border for glass effect */
--invalid-color: #ef4444; /* Red for validation errors */
--success-color: #10b981; /* Green for success states */
--warning-color: #f59e0b; /* Orange for warnings */
}
/* Reset default browser styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* Base body styles with flexbox centering */
body {
font-family: 'Inter', system-ui, -apple-system, sans-serif;
min-height: 100vh;
background: var(--primary-bg);
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
padding: 2rem;
}
/* Reusable glass morphism effect */
.glass-effect {
background: var(--glass-bg);
backdrop-filter: blur(10px);
border: 1px solid var(--border);
border-radius: 1.5rem;
box-shadow: var(--shadow);
}
/* Main container layout */
.container {
width: 100%;
max-width: 900px;
display: flex;
flex-direction: column;
gap: 2rem;
}
/* Logo section styling */
.logo-container {
position: relative;
text-align: center;
padding: 2rem;
}
/* Logo image styling with smooth transition */
.logo {
height: 100px;
width: auto;
transition: all 0.3s ease;
}
/* Calculator section container */
.calculator-container {
padding: 2rem;
}
/* Main heading styles */
h1 {
text-align: center;
margin-bottom: 2rem;
font-size: 2.5rem;
font-weight: 700;
}
/* Grid layout for input fields */
.input-group {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
margin-bottom: 2rem;
}
/* Individual input field container */
.input-wrapper {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
/* Input field styling */
input {
width: 100%;
padding: 1rem;
border: 1px solid var(--border);
border-radius: 0.75rem;
background: var(--input-bg);
color: var(--text-color);
font-size: 1rem;
transition: all 0.3s ease;
}
/* Input focus state */
input:focus {
outline: none;
border-color: var(--btn-primary);
box-shadow: 0 0 0 2px rgba(30, 58, 138, 0.3);
}
/* Input label styling */
.input-label {
font-size: 0.875rem;
color: var(--text-color);
opacity: 0.9;
}
/* Container for subject entries */
.subjects-container {
display: flex;
flex-direction: column;
gap: 1rem;
margin-bottom: 2rem;
}
/* Individual subject entry styling */
.subject {
display: grid;
grid-template-columns: 1.5fr 1fr auto;
gap: 1rem;
align-items: center;
padding: 1rem;
background: var(--glass-bg);
border-radius: 0.75rem;
animation: slideIn 0.3s ease forwards;
}
/* Button group layout */
.button-group {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
margin-bottom: 2rem;
}
/* Base button styles */
.btn {
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
padding: 1rem;
border: none;
border-radius: 0.75rem;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
color: white;
}
/* Icon sizing within buttons */
.btn i {
font-size: 1.2rem;
}
/* Button variants */
.btn-primary { background: var(--btn-primary); }
.btn-success { background: var(--btn-success); }
.btn-danger { background: var(--btn-danger); }
/* Button hover effects */
.btn:hover {
transform: translateY(-2px);
box-shadow: var(--shadow);
opacity: 0.9;
}
/* Button active state */
.btn:active {
transform: translateY(0);
}
/* Remove button styling */
.remove-button {
background: var(--btn-danger);
color: white;
border: none;
border-radius: 50%;
width: 30px;
height: 30px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
transition: all 0.3s ease;
}
/* Remove button hover effect */
.remove-button:hover {
transform: scale(1.1);
opacity: 0.9;
}
/* Results section container */
.result {
padding: 2rem;
margin-top: 2rem;
}
/* Results content layout */
.result-content {
display: flex;
flex-direction: column;
gap: 1rem;
}
/* Individual result item styling */
.result-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
background: rgba(30, 41, 59, 0.5);
border-radius: 0.75rem;
}
/* Result label styling */
.result-label {
font-weight: 500;
opacity: 0.9;
}
/* Result value styling */
.result-value {
font-weight: 600;
font-size: 1.1rem;
}
/* Footer section styling */
.footer-content {
text-align: center;
padding: 2rem;
opacity: 0.8;
}
/* Social links container */
.social-links {
display: flex;
justify-content: center;
gap: 2rem;
margin-top: 1rem;
}
/* Individual social link styling */
.social-link {
color: var(--text-color);
opacity: 0.8;
transition: all 0.3s ease;
}
/* Social link hover effect */
.social-link:hover {
opacity: 1;
transform: translateY(-3px);
}
/* Invalid input state */
.invalid {
border-color: var(--invalid-color);
}
/* Hide elements */
.hidden {
display: none;
}
/* Animation keyframes */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Responsive design for mobile devices */
@media (max-width: 768px) {
body {
padding: 1rem;
}
.input-group,
.button-group {
grid-template-columns: 1fr;
}
.subject {
grid-template-columns: 1fr;
gap: 0.75rem;
}
h1 {
font-size: 1.8rem;
}
.result-item {
flex-direction: column;
text-align: center;
gap: 0.5rem;
}
}
/* Loading state styles */
.loading {
position: relative;
}
/* Loading overlay animation */
.loading::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(30, 41, 59, 0.7);
border-radius: 0.75rem;
display: flex;
justify-content: center;
align-items: center;
animation: pulse 1.5s infinite;
}
/* Loading pulse animation */
@keyframes pulse {
0% { opacity: 0.7; }
50% { opacity: 0.4; }
100% { opacity: 0.7; }
}