Skip to content

Commit

Permalink
Feat: Marks possibilities and update README
Browse files Browse the repository at this point in the history
skybird23333 committed Sep 4, 2024
1 parent 54796be commit cfd1739
Showing 10 changed files with 132 additions and 50 deletions.
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -4,21 +4,25 @@

Save and track your mark progression across different assessments. I made this to learn React and for my own usage.

![image](https://github.com/skybird23333/grading-calculator-react/assets/58811224/11466e6d-d1bf-40b2-abd1-40977cb13acf)


![img.png](img.png)

This is a work in progress

Features:
- Looks ok
- Calculate grading based off assessments that were completed
- Calculate minimum marks required to stay on the goal for incomplete assessments
- Otherwise, the maximum marks you can get for your subject
- **Calculate grading based off assessments that were completed**
- **Calculate minimum marks required to stay on the goal for incomplete assessments**
- **Otherwise, the maximum marks you can get for your subject**
- **Show how assessments you have done have affected your current mark**
- **Show how future assessments could possibly impact your current mark**
- Sync your data across devices(via supabase)
- Has a progress bar
- Will not threaten to kill you if you misinput
- A different progress bar which shows different assessments as well as what you have scored(soon)
- Being able to save and come back later(done)
- Show the highest weighting assessments(soon)
- Show stuff when hover over progress bar(soon)
- Clean, organised, readable code(soon)

### Poopy code alert
I made this very very back when I started to learn how to use react. Plus class syntax was still a thing back then. If you venture into some very inconsistent code styles make sure to git blame and see when it was written.
Binary file added img.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 changes: 54 additions & 25 deletions src/components/Assessment.js
Original file line number Diff line number Diff line change
@@ -5,8 +5,23 @@ import { Input } from "./Input";
import { Label } from "./Label";
import { Progress } from "./Progress";
import { Select } from "./SingleSelect";
import MarkDisplay from "./MarkDisplay";

export class Assessment extends React.Component {
/**
* @param props {Object}
* @param props.g {Object}
* @param props.g.name {String}
* @param props.g.weighting {Number}
* @param props.g.grading {Number}
* @param props.g.due {Boolean}
* @param props.g.changeToTotalMark {Number}
* @param props.g.gradePossibilities {[Number, Number]}
* @param props.keyy {Number}
* @param props.mode {String}
* @param props.onAssessmentChange {Function}
* @param props.onAssessmentDelete {Function}
*/
constructor(props) {
super(props);

@@ -73,25 +88,40 @@ export class Assessment extends React.Component {
return this.props.g.grading >= 50 ? "orange" : "red";
})();

console.log(this.props.g.gradePossibilities)

const changeElement = (this.props.g.changeToTotalMark && this.props.g.grading) ? <span
style={{color: this.props.g.changeToTotalMark >= 0 ? "lightgreen" : "red"}}
title="This indicates how much this assessment has affected your total mark."
>
{ this.props.g.changeToTotalMark >= 0 ? FaArrowUp() : FaArrowDown()} {this.props.g.changeToTotalMark.toFixed(2)}%
</span> : <></>

const changeElement =
(this.props.g.changeToTotalMark && this.props.g.grading) ?
<span
style={{color: this.props.g.changeToTotalMark >= 0 ? "lightgreen" : "red"}}
title="This indicates how much this assessment has affected your total mark."
>
{ this.props.g.changeToTotalMark >= 0 ? FaArrowUp() : FaArrowDown()} {MarkDisplay(this.props.g.changeToTotalMark)}
</span> :
this.props.g.gradePossibilities ?
<>
50%: <span
style={{color: "indianred"}}
title="How your mark will be affected if you get 50%."
><b>{MarkDisplay(this.props.g.gradePossibilities[0])}</b></span> {" "}100%:
<span
style={{color: "forestgreen"}}
title="How your mark will be affected if you get 100%."
><b>{MarkDisplay(this.props.g.gradePossibilities[1])}</b></span>
</>
: null;
switch (this.props.mode) {
case "edit":
return (
<div
className="card background"
style={{ borderLeft: `5px solid grey` }}
>
<div style={{ display: "block" }}>
<Input
value={this.props.g.name}
style={{ fontSize: "large", fontWeight: "bold" }}
<div
className="card background"
style={{borderLeft: `5px solid grey`}}
>
<div style={{display: "block"}}>
<Input
value={this.props.g.name}
style={{fontSize: "large", fontWeight: "bold" }}
onChange={this.handleNameChange}
/>

@@ -144,10 +174,6 @@ export class Assessment extends React.Component {
let actualWeighting = `${((this.props.g.grading * this.props.g.weighting) / 100).toFixed(2)
}%/`;

const marks = this.props.g.due
? "Due"
: `${this.props.g.grading}%`;

const progressbar = this.props.g.due ? null : (
<Progress max={100} val={this.props.g.grading} color={this.color}>
<div
@@ -171,7 +197,7 @@ export class Assessment extends React.Component {
return (
<div
className="card background"
style={{borderLeft: `5px solid ${this.color}`}}
style={{borderLeft: `5px solid ${this.color}`, margin: '10px 0px 0px 0px'}}
>
<div
style={{background: "grey", width: "95%", height: "100%"}}
@@ -181,7 +207,10 @@ export class Assessment extends React.Component {

{progressbar}

<div style={{ float: "right", fontSize: "x-large" }}>{marks}</div>
<div style={{ float: "right", fontSize: "x-large" }}>
{this.props.g.due
? "Due"
: MarkDisplay(this.props.g.grading)}</div>

<span style={{ textAlign: "center" }}>
<Label>
@@ -192,8 +221,8 @@ export class Assessment extends React.Component {
{changeElement}
</Label>
</span>
</div>
);
}
}
}
</div>
);
}
}
}
9 changes: 5 additions & 4 deletions src/components/GradeOverview.js
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ import {
} from "react-icons/fa";
import React from "react";
import calculateColorFromGrade from "../utils/calculateColorFromGrade";
import MarkDisplay from "./MarkDisplay";

/**
*
@@ -34,28 +35,28 @@ export default function GradeOverview({currentGrade, currentGradeTotal, goal, ma
<Label>
<span title={`You are currently at ${currentGrade.toFixed(2)}`}
style={{color: "var(--" + calculateColorFromGrade(currentGrade) + ")", filter: 'brightness(150%)' }}>
<FaCheckCircle/> <b>{currentGrade.toFixed(2)}%</b>
<FaCheckCircle/> {MarkDisplay(currentGrade)}
</span>
</Label>

<Label>
<span title={`You will currently achieve a minimum of ${currentGradeTotal.toFixed(2)}`}
style={{color: "rgb(255, 100, 100)"}}>
<FaAngleLeft/> {currentGradeTotal.toFixed(2)}%
<FaAngleLeft/> {MarkDisplay(currentGradeTotal)}
</span>
</Label>

<Label>
<span title={`You are aiming for ${goal.toFixed(2)}`}
style={{color: "rgba(0, 153, 255, 1)"}}>
<FaDotCircle/> {goal.toFixed(2)}%
<FaDotCircle/> {MarkDisplay(goal)}
</span>
</Label>

<Label>
<span title={`You will currently achieve a maximum of ${maximumGrade.toFixed(2)}`}
style={{color: "yellow"}}>
<FaAngleRight/> {maximumGrade.toFixed(2)}%
<FaAngleRight/> {MarkDisplay(maximumGrade)}
</span>
</Label>

17 changes: 17 additions & 0 deletions src/components/MarkDisplay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Display a mark
* @param mark {Number}
* @param showDecimal
* @constructor
*/
export default function MarkDisplay(mark, showDecimal = true) {
const fixedMark = mark.toFixed(2)
return (
<span>
{fixedMark.split('.')[0]}
{showDecimal &&
(<><span style={{fontSize: 'smaller'}}>.{fixedMark.split('.')[1]}</span></>)
}
</span>
);
}
9 changes: 5 additions & 4 deletions src/components/SubjectComponentCompact.js
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import {Button} from "./Button";
import {calculateInformation} from "../utils/calculateInformation";
import gradeOverview from "./GradeOverview";
import calculateColorFromGrade from "../utils/calculateColorFromGrade";
import MarkDisplay from "./MarkDisplay";

// this is a copy paste of SubjectComponent with a few features removed.
export function SubjectComponentCompact({subject, id, isActive, onClick}) {
@@ -33,22 +34,22 @@ export function SubjectComponentCompact({subject, id, isActive, onClick}) {

let minimumScore = (
<span>
🎯 {Math.ceil(info.minimumGrade)}%
🎯 {MarkDisplay(subject.goal, false)}
</span>
);

if (info.minimumGrade >= 100) {
minimumScore = (
<span>
{info.maximumGrade.toFixed(2)}%.
{MarkDisplay(info.maximumGrade, false)}
</span>
);
}

if (info.minimumGrade <= 0) {
minimumScore = (
<span>
🥳 {info.currentGradeTotal.toFixed(2)}%.
🥳 {MarkDisplay(info.currentGradeTotal, false)}%
</span>
);
}
@@ -84,7 +85,7 @@ export function SubjectComponentCompact({subject, id, isActive, onClick}) {
<b>{subject.name} <span style={{
color: `var(--${color})`,
filter: 'brightness(200%)'
}}>{info.currentGrade.toFixed(1)}%</span></b> {minimumScore}
}}>{MarkDisplay(info.currentGrade)}</span></b> {minimumScore}
</span>

</div>
6 changes: 3 additions & 3 deletions src/index.css
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ main {
.navbar {
height: 60px;
margin: 15px;
background: rgba(26, 26, 26, 0.5);
background: rgba(26, 26, 26, 0.8);
-webkit-backdrop-filter: blur(4px);
backdrop-filter: blur(4px);
z-index: 10;
@@ -281,13 +281,13 @@ h3 {

/* The sidebar menu */
.side-contents {
height: 75%;
height: 78%;
width: 300px;
position: fixed;
z-index: 1;
top: 0;
left: 5px;
background: rgba(26, 26, 26, 0.5);
background: rgba(26, 26, 26, 0.8);
-webkit-backdrop-filter: blur(4px);
backdrop-filter: blur(4px);
overflow-x: hidden;
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ const App = () => {
const [collapsed, setCollapsed] = useState(true);

useEffect(() => {
//trigger the animation on start
if (window.innerWidth < 1300) return
setCollapsed(false)
}, [])

28 changes: 25 additions & 3 deletions src/routes/Index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, {useState} from "react";
import React, {useEffect, useState} from "react";
import {Button} from "../components/Button";
import {SubjectComponent} from '../components/SubjectComponent'
import {createSubject, getAllSubjects, onStorageChanged, setSubjectIndex} from "../utils/storagehelper";
import {Link} from "react-router-dom";
import {FaCloud} from "react-icons/fa";
import {FaBook, FaCloud} from "react-icons/fa";

export function IndexRoute() {
const [subjects, setSubjects] = useState(getAllSubjects());
@@ -46,10 +46,32 @@ export function IndexRoute() {
setSubjectIndex(subjects.map(s => s[1]))
}

//fetch github for latest release notes
const [latestRelease, setLatestRelease] = useState(null)
useEffect(() => {
fetch("https://api.github.com/repos/skybird23333/grading-calculator-react/releases/latest")
.then(response => response.json())
.then(data => {
setLatestRelease(data)
console.log(latestRelease)
})
.catch((error) => {
console.error('Error:', error);
})
}, [])

return (
<div>
<div className="content-header">
<h2>All Subjects</h2>
<h2>Home</h2>
{
latestRelease && (
<a href={latestRelease?.html_url} target="_blank" rel="noreferrer" className={"link"}
>
<FaBook/> Latest release notes - <b>{latestRelease?.name}</b> - {new Date(latestRelease?.published_at).toLocaleDateString()}
</a>
)
}
</div>
<div className="content-content">
{subjects.map((subject, index) => (
16 changes: 12 additions & 4 deletions src/routes/SubjectPage.js
Original file line number Diff line number Diff line change
@@ -90,13 +90,21 @@ export function Subject() {
};

const recalculateChangeToTotalMark = (assessments) => {
return assessments.map((a, i) => {
if (i > 0 && a.grading) {
a.changeToTotalMark = calculateInformation(assessments.slice(0, i + 1)).currentGrade
- calculateInformation(assessments.slice(0, i)).currentGrade;
const newAssessments = assessments.map((a, i) => {
if (i > 0) {
const currentGrade = calculateInformation(assessments.slice(0, i)).currentGrade;
if (!a.due) { // If marks exist
a.changeToTotalMark = calculateInformation(assessments.slice(0, i + 1)).currentGrade - currentGrade;
} else { // Marks don't exist, calculate possibility
a.gradePossibilities = [
calculateInformation(assessments.slice(0, i).concat({ ...a, grading: 50, due: false })).currentGrade - currentGrade,
calculateInformation(assessments.slice(0, i).concat({ ...a, grading: 100, due: false })).currentGrade - currentGrade,
];
}
}
return a;
});
return newAssessments;
};

const { currentGrade, currentGradeTotal, weightTotal, currentWeightTotal, completedAssessmentCount, totalAssessmentCount, unallocatedWeight, minimumGrade, maximumGrade } = calculateInformation(info.assessments, info.goal);

0 comments on commit cfd1739

Please sign in to comment.