Skip to content

Commit

Permalink
Merge pull request #7 from SimonB50/main
Browse files Browse the repository at this point in the history
Librusek v0.2.0
  • Loading branch information
SimonB50 authored Oct 14, 2024
2 parents 5ebc4a3 + fff20f4 commit b475a93
Show file tree
Hide file tree
Showing 36 changed files with 789 additions and 308 deletions.
39 changes: 30 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,47 @@ Librusek is an alternative app for [Librus Synergia](https://synergia.librus.pl/

## Supported Synergia features
- **Logging in with Synergia**<br/>
To use Librusek, you don't need to link your Synergia account with Librus. Just log in with your Synergia credentials and you're good to go.
Librusek doesn't require you to create a Librus account, instead opting for the ability to login with your pre-existing Synergia account created for you by your school administrator.
- **Landing page**<br/>
All your account information, like name, school and current lucky number in one place.
All your account information, like your name, school and the current lucky number is available in one place.
- **Grades**<br/>
View your grades and averages for each subject. You can also see the grades added in the last day. <ins>Librusek supports both traditional and points-based grading systems.</ins>
View your grades and averages for each subject. You can also see the grades added the day prior. <ins>Librusek supports both the traditional and points-based (percentages) grading systems.</ins>
- **Exams**<br/>
Know when your next exam is. You can view all your upcoming exams and their dates, everything properly sorted and highlighted.
Know when your upcoming exams take place. You can view all your upcoming and previous exams along with their dates, all neatly sorted and highlighted for your convenience.
- **Timetable**<br/>
Be up-to-date with your daily schedule. You can view your timetable for the each week including all substitutions and cancelled classes.
Stay up-to-date with your daily schedule. You can view your timetable for each week, including all substitutions and cancelled classes.

## Unique app features
Since Librusek is fully custom application, it has some unique features that are not available in the original Librus Synergia.
Since Librusek is a fully custom application, it has some unique features that are not available in the original Librus Synergia.
- **Light and dark mode**<br/>
Librusek supports both light and dark mode based on your system preferences.
Librusek supports both light and dark modes based on your system preferences.
- **Responsive design**<br/>
Thanks to the responsive design, Librusek looks great on all kinds of devices like smartphones and tablets.
Thanks to the responsive design, Librusek looks great on various mobile devices like smartphones and tablets.
- **Fast and reliable**<br/>
Librusek is built with modern technologies it's - in most cases - faster than the official Synergia. It also uses optimized methods for fetching data from the Synergia API.
- **Modern design**<br/>
Librusek has a modern and clean design that is easy to use and looks great.

## Get it here!

<a href="https://apps.obtainium.imranr.dev/redirect?r=obtainium://add/https://github.com/SimonB50/librusek">
<img src="graphics/obtanium.png">
</a>

## Check it out in action
<p float="left">
<img src="graphics/home.jpg" alt="Home Page" height=450 />
<img src="graphics/grades.jpg" alt="Grades Page Retracted" height=450 />
<img src="graphics/grades2.jpg" alt="Grades Page Full" height=450 />
<img src="graphics/grades3.jpg" alt="Points Based Grades Page" height=450 />
<img src="graphics/exams.jpg" alt="Exams Page" height=450 />
<img src="graphics/timetable.jpg" alt="Timetable Page" height=450 />
</p>

## This project was brought to you by our awesome contributors!
<a href="https://github.com/SimonB50/librusek/graphs/contributors">
<img src="https://contrib.rocks/image?repo=SimonB50/librusek" />
</a>

## Credits
The idea for Librusek was born because of [Librusik](https://github.com/dani3l0/librusik) created by [dani3l0](https://github.com/dani3l0). Some of the code (especially for authorization) was inspired from this project.
The idea for Librusek was born because of [Librusik](https://github.com/dani3l0/librusik) created by [dani3l0](https://github.com/dani3l0). Some of the code (especially for authorization) was inspired by this project.
44 changes: 34 additions & 10 deletions components/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@ import { useRouter } from "next/router";
import Link from "next/link";

import { useState, useEffect } from "react";
import { Person, House, List, ListOl, VectorPen, Calendar2Week, PersonExclamation } from "react-bootstrap-icons";
import {
Person,
House,
List,
ListOl,
VectorPen,
Calendar2Week,
PersonExclamation,
ClipboardCheck,
} from "react-bootstrap-icons";

import { getUser } from "@/lib/user";
import { logout, refreshSession } from "@/lib/auth";
Expand All @@ -17,7 +26,7 @@ const Layout = ({ children, setAuthData }) => {
const data = await getUser();
if (!data) return window.location.replace("/auth");
setUserData(data); // Local
setAuthData(data); // Global
if (setAuthData) setAuthData(data); // Global
};
if (!userData) fetchData();

Expand Down Expand Up @@ -70,15 +79,15 @@ const Layout = ({ children, setAuthData }) => {
tabIndex="0"
className="menu menu-sm dropdown-content bg-base-300 rounded-box z-[1] mt-3 w-52 p-2 shadow"
>
{/* <li>
{/* <li>
<a className="justify-between">
Profile
<span className="badge">New</span>
</a>
</li>
<li>
<a>Settings</a>
</li> */}
<li>
<Link href="/settings">Settings</Link>
</li>
<li>
<button
onClick={async () => {
Expand Down Expand Up @@ -117,10 +126,7 @@ const Layout = ({ children, setAuthData }) => {
</Link>
</li>
<li>
<Link
href="/exams"
className="flex flex-row items-center text-xl"
>
<Link href="/exams" className="flex flex-row items-center text-xl">
<VectorPen />
Exams
</Link>
Expand All @@ -134,6 +140,24 @@ const Layout = ({ children, setAuthData }) => {
Timetable
</Link>
</li>
<li>
<Link
href="/teacherAbsences"
className="flex flex-row items-center text-xl"
>
<PersonExclamation />
Teacher absences
</Link>
</li>
<li>
<Link
href="/attendance"
className="flex flex-row items-center text-xl"
>
<ClipboardCheck />
Attendance
</Link>
</li>
</ul>
</div>
</div>
Expand Down
Binary file added graphics/exams.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added graphics/grades.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added graphics/grades2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added graphics/grades3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added graphics/home.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added graphics/obtanium.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added graphics/timetable.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 23 additions & 8 deletions lib/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ const activate = async () => {

const authenticate = async () => {
try {
await fetch(authUrls.init);
const initRequest = await fetch(authUrls.init, {
connectTimeout: 5000,
});
if (!initRequest.ok) return { error: "unknown" };
const formData = new FormData();
formData.append("action", "login");
formData.append("login", localStorage.getItem("login"));
Expand All @@ -35,7 +38,19 @@ const authenticate = async () => {
body: formData,
credentials: "include",
});
if (authorization.status !== 200) return false;
if (authorization.status !== 200) {
const errorBody = await authorization.json();
if (!errorBody || errorBody.errors.length <= 0) return { error: "unknown" };
switch (errorBody.errors[0].message) {
case "Minął termin ważności konta - skontaktuj się z Administratorem Szkoły.":
return { error: "invalidUser" };
case "Nieprawidłowy login i/lub hasło.":
return { error: "invalidCredentials" };
case "Proszę zaznaczyć pole \"Nie jestem robotem\".":
return { error: "invalidCaptcha" };
}
return { error: "unknown" };
}
const grant = await fetch(authUrls.grant);
if (!grant.ok) return false;
return await activate();
Expand All @@ -57,11 +72,11 @@ const refreshSession = async () => {
};

const logout = async () => {
try {
await fetch(`https://synergia.librus.pl/wyloguj`);
} catch (error) {
console.error(error);
}
}
try {
await fetch(`https://synergia.librus.pl/wyloguj`);
} catch (error) {
console.error(error);
}
};

export { authenticate, refreshSession, logout };
46 changes: 43 additions & 3 deletions lib/core.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,47 @@
import { getVersion as getAppVersion } from "@tauri-apps/api/app";
import { useEffect, useState } from "react";

const apiUrl = "https://synergia.librus.pl/gateway/api/2.0";
const repoApiUrl = "https://api.github.com/repos/SimonB50/librusek";

const getVersion = async () => {
const appVersion = await getAppVersion();
const versionRequest = await fetch(`${repoApiUrl}/releases/latest`);
if (versionRequest.status !== 200) return {
currentVersion: appVersion,
latestVersion: "unknown",
updateAvailable: false,
}
const versionResponse = await versionRequest.json();
const latestVersion = versionResponse.tag_name.slice(1);
return {
currentVersion: appVersion,
latestVersion: latestVersion,
updateAvailable: appVersion !== latestVersion,
}
}
const useVersion = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
const fetchData = async () => {
try {
const data = await getVersion();
setData(data);
setError(null);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};

fetchData();
}, []);

const upperFirst = (string) => {
return string.charAt(0).toUpperCase() + string.slice(1);
return { data, loading, error };
}

export { apiUrl, upperFirst };
export { apiUrl, repoApiUrl, getVersion, useVersion };
60 changes: 53 additions & 7 deletions lib/lessons.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,42 @@ import { apiUrl } from "./core";

import { useState, useEffect } from "react";

const getLessons = async (id) => {
if (!id) return false;
const lessonsRequest = await fetch(`${apiUrl}/Lessons/0,${id}`);
if (!lessonsRequest.ok) return false;
const lessonsData = await lessonsRequest.json();
return lessonsData;
};
const useLessons = (id) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
const fetchData = async () => {
try {
const data = await getLessons(id);
if (!data) throw new Error("Failed to fetch lessons data");
setData(data);
setError(null);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};

fetchData();
}, [id]);

return { data, loading, error };
};

const getAttendance = async () => {
const attendanceRequest = await fetch(`${apiUrl}/Attendances`);
const attendanceRequest = await fetch(
`${apiUrl}/Attendances`
);
if (!attendanceRequest.ok) return false;
const attendanceData = await attendanceRequest.json();
return attendanceData;
Expand Down Expand Up @@ -35,21 +69,24 @@ const useAttendance = () => {
return { data, loading, error };
};

const getAttendancesTypes = async () => {
const attendanceTypesRequest = await fetch(`${apiUrl}/Attendances/Types`);
const getAttendancesTypes = async (id) => {
if (!id) return false;
const attendanceTypesRequest = await fetch(
`${apiUrl}/Attendances/Types/0,${id}`
);
if (!attendanceTypesRequest.ok) return false;
const attendanceTypesData = await attendanceTypesRequest.json();
return attendanceTypesData;
};
const useAttendancesTypes = () => {
const useAttendancesTypes = (id) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
const fetchData = async () => {
try {
const data = await getAttendancesTypes();
const data = await getAttendancesTypes(id);
if (!data) throw new Error("Failed to fetch attendance types data");
setData(data);
setError(null);
Expand All @@ -61,7 +98,7 @@ const useAttendancesTypes = () => {
};

fetchData();
}, []);
}, [id]);

return { data, loading, error };
};
Expand Down Expand Up @@ -97,4 +134,13 @@ const useLuckyNumber = () => {
return { data, loading, error };
};

export { getAttendance, useAttendance, getAttendancesTypes, useAttendancesTypes, getLuckyNumber, useLuckyNumber };
export {
getLessons,
useLessons,
getAttendance,
useAttendance,
getAttendancesTypes,
useAttendancesTypes,
getLuckyNumber,
useLuckyNumber,
};
Loading

0 comments on commit b475a93

Please sign in to comment.