This commit is contained in:
Turtle committed 2025-12-31 12:13:23 +01:00
1 parent bdeaece35a
commit 16bc5ea38e
11 files changed
+445 -338

No files matched your search

+26
View File
@@ -0,0 +1,26 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="./auth.js"></script>
<script src="./lib/basic.js"></script>
<script src="./help/data_xample.js"></script>
</head>
<body>
<script>
(async () => {
try {
await createTemp();
location.href = "./login.html";
} catch(err) {
alert("Ein Fehler ist aufgetreten: " + err);
}
})();
</script>
</body>
</html>
+28 -19
View File
@@ -3,12 +3,11 @@ async function createTemp() {
await couchDB.set("users", {
"turtle": {
"password": "1234",
"level": 1,
"level": 0,
"xp": 0,
"avatar": null,
"abilities": [
],
"enabled_abilities": [
],
"abilities": [],
"enabled_abilities": [],
"role": "student",
"courses": [
"weiche_ware",
@@ -17,8 +16,9 @@ async function createTemp() {
},
"dumble-door": {
"password": "bla",
"level": 4,
"avatar": "https://static.turtle-coding-gbr.de/profile-pic.png",
"level": 10,
"xp": 0,
"avatar": "https://static.turtle-coding-gbr.de/gmci/profile-pic.png",
"abilities": [
],
"enabled_abilities": [
@@ -30,9 +30,18 @@ async function createTemp() {
},
"narrator": {
"password": "stanley",
"level": 4,
"avatar": "https://static.turtle-coding-gbr.de/st",
"level": 10,
"xp": 0,
"avatar": "https://static.turtle-coding-gbr.de/gmci/st",
"abilities": [
"dark_mode",
"accent_color",
"pink_mode",
"profile_pic",
"simple_border",
"gold_border",
"epic_border",
"title_of_honor"
],
"enabled_abilities": [
],
@@ -72,17 +81,17 @@ async function createTemp() {
{
"date": "2025-12-27",
"questions": [
{ "title": "1", "type": "text" },
{ "title": "2", "type": "scala" }
{ "title": "Wie war die Vorlesung?", "type": "text" },
{ "title": "Wie war der Professor?", "type": "scala" }
],
"result": [
["Gut, aber ...", 5],
["Schlecht", 1]
["Gut, weil ...", 5],
["Schlecht, weil ...", 1]
]
}
],
"start_date": "2025-12-23",
"expire_date": "2025-12-27"
"expire_date": "2026-02-20"
});
await couchDB.set("course:weiche_ware", {
"title": "Weiche Ware",
@@ -99,8 +108,8 @@ async function createTemp() {
}
],
"result": [
["Gut, aber ...", 5],
["Schlecht", 1]
["Gut, weil ...", 5],
["Schlecht, weil ...", 1]
],
"result_history": [
{
@@ -110,12 +119,12 @@ async function createTemp() {
{ "title": "2", "type": "scala" }
],
"result": [
["Gut, aber ...", 5],
["Schlecht", 1]
["Gut, weil ...", 5],
["Schlecht, weil ...", 1]
]
}
],
"start_date": "2025-12-23",
"expire_date": "2025-12-27"
"expire_date": "2025-02-20"
});
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

+64 -8
View File
@@ -374,14 +374,15 @@ var userData = null;
const userDataListener = [];
async function setUserData(couchDB) {
const res = await getUserData(couchDB);
if (res == null) {
const user = await getUserData(couchDB);
if (user == null) {
location.href = "../login.html";
return false;
}
userData = res;
updateAllProfiles(res);
_constructProfile(res);
userData = user;
updateAllProfiles(user);
_constructProfile(user);
await checkUserCourses(couchDB, user);
while (userDataListener.length > 0) {
const call = userDataListener[userDataListener.length - 1];
@@ -407,18 +408,51 @@ async function updateUserData(couchDB, user = userData) {
function updateAllProfiles(user) {
const hasProfilAbility = user.enabled_abilities.includes("profile_pic");
const features = user.enabled_abilities ??= [];
const hasProfilAbility = features.includes("profile_pic");
const profiles = document.getElementsByClassName("profile");
for (var i of profiles) {
if (hasProfilAbility) {
if (hasProfilAbility && user.avatar) {
i.style.backgroundImage = `url("${user.avatar}")`;
} else {
i.style.removeProperty("backgroundImage");
if (features.includes("pink_mode")) {
i.style.backgroundImage = `url("https://static.turtle-coding-gbr.de/gmci/rosa_profil.png")`;
} else {
i.style.backgroundImage = `url("https://static.turtle-coding-gbr.de/gmci/standart_profil.png")`;
}
}
i.onclick = _onProfileClick;
}
}
async function checkUserCourses(couchDB, userData) {
for (var cName of userData.courses) {
const course = await couchDB.get("course:" + cName);
if (!course || !course.expire_date) continue;
const expire_date = new Date(course.expire_date);
if (expire_date < new Date()) {
course.result_history ??= [];
course.questions ??= [];
course.result ??= [];
if (course.questions.length == 0 || course.result.length == 0) continue;
course.result_history.push({
"date": course.start_date ?? expire_date,
"questions": course.questions,
"result": course.result
});
course.questions = [];
course.result = [];
await couchDB.set("course:" + cName, course);
console.log(`Course ${cName} expired`);
}
}
}
function _onProfileClick(event) {
var buttonRect = this.getBoundingClientRect();
@@ -542,4 +576,26 @@ function getUserDisplayName(user) {
const hasHonor = (user.enabled_abilities ??= []).includes("title_of_honor");
const title_of_honor = hasHonor ? "(Sir) " : "";
return `${title_of_honor}${ !user.title ? "" : user.title + " " }${ form1(user.name) }`;
}
async function levelUpXP(couchDB, user = userData, xp = 1) {
var resLevelUp = false;
userData.xp ??= 0;
userData.level ??= 0;
userData.xp += xp;
while (userData.xp >= userData.level+1) {
userData.xp -= (userData.level + 1);
userData.level++;
resLevelUp = true;
}
if (userData.xp < 0) {
console.error("UserData-XP was below zero!");
userData.xp = 0;
}
await updateUserData(couchDB, user);
return resLevelUp;
}
+135 -141
View File
@@ -1,160 +1,154 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Login</title>
<link rel="stylesheet" href="./lib/basic.css" />
<script src="./auth.js" defer></script>
<script src="./lib/basic.js" defer></script>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Login</title>
<link rel="stylesheet" href="./lib/basic.css" />
<script src="./auth.js"></script>
<script src="./lib/basic.js"></script>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f6f8fa;
}
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f6f8fa;
}
.login-card {
background-color: #fff;
padding: 2.5rem 2rem;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
width: 100%;
max-width: 400px;
}
.login-card {
background-color: #fff;
padding: 2.5rem 2rem;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
width: 100%;
max-width: 400px;
}
.login-card h1 {
text-align: center;
margin-bottom: 1.5rem;
font-size: 1.5rem;
}
.login-card h1 {
text-align: center;
margin-bottom: 1.5rem;
font-size: 1.5rem;
}
label {
display: block;
margin-bottom: 0.3rem;
font-weight: 600;
font-size: 0.9rem;
}
label {
display: block;
margin-bottom: 0.3rem;
font-weight: 600;
font-size: 0.9rem;
}
input {
width: 100%;
padding: 0.5rem 0.75rem;
margin-bottom: 1rem;
border: 1px solid #d1d5da;
border-radius: 6px;
font-size: 1rem;
}
input {
width: 100%;
padding: 0.5rem 0.75rem;
margin-bottom: 1rem;
border: 1px solid #d1d5da;
border-radius: 6px;
font-size: 1rem;
}
button {
width: 100%;
padding: 0.6rem;
background-color: #2ea44f;
color: #fff;
border: none;
border-radius: 6px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
}
button {
width: 100%;
padding: 0.6rem;
background-color: var(--accent_color_fg);
color: #fff;
border: none;
border-radius: 6px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
}
button:hover {
background-color: #279f43;
}
button:hover {
background-color: var(--accent_color_fg_o);
}
.signup-text {
text-align: center;
margin-top: 1rem;
font-size: 0.9rem;
}
.signup-text {
text-align: center;
margin-top: 1rem;
font-size: 0.9rem;
}
.signup-text a {
color: #0366d6;
text-decoration: none;
}
.signup-text a {
color: #0366d6;
text-decoration: none;
}
.signup-text a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="login-card">
<h1>Sign in</h1>
.signup-text a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="login-card">
<h1>Sign in</h1>
<label for="username">Username</label>
<input
type="text"
id="username"
name="username"
autocomplete="username"
autocapitalize="off"
autocorrect="off"
required
autofocus
/>
<label for="username">Username</label>
<input
type="text"
id="username"
name="username"
autocomplete="username"
autocapitalize="off"
autocorrect="off"
required
autofocus
/>
<label for="password">Password</label>
<input
type="password"
id="password"
name="password"
autocomplete="current-password"
required
/>
<label for="password">Password</label>
<input
type="password"
id="password"
name="password"
autocomplete="current-password"
required
/>
<button type="button" onclick="login()">Sign in</button>
<button type="button" onclick="login()">Sign in</button>
<div class="signup-text">
Don't have an account yet? <a href="signup.html">Sign up</a>
</div>
</div>
<div class="signup-text">Don't have an account yet? <a href="signup.html">Sign up</a></div>
</div>
<script>
const couchDB = new CouchDB();
<script>
const couchDB = new CouchDB();
async function getData() {
try {
const res = await couchDB.get("users");
return res || {};
} catch (e) {
console.error("Failed to get users:", e);
return {};
}
}
async function getData() {
try {
const res = await couchDB.get("users");
return res ?? {};
} catch (e) {
console.error("Failed to get users:", e);
return {};
}
}
async function login() {
const data = await getData();
const username = document
.getElementById("username")
.value.trim()
.toLowerCase();
const password = document.getElementById("password").value;
async function login() {
const data = await getData();
const username = document.getElementById("username").value.trim().toLowerCase();
const password = document.getElementById("password").value;
const user = data[username];
if (user == undefined) {
return alert("User doesn't exist!");
}
if (user.password != password) {
return alert("Incorrect password!");
}
if (user.role === "student") {
location.href =
"./student/uebersicht.html?user=" + username;
return;
} else {
location.href = "./prof/uebersicht.html?user=" + username;
return;
}
}
</script>
</body>
const user = data[username];
if (user == undefined) {
return alert("User doesn't exist!");
}
if (user.password != password) {
return alert("Incorrect password!");
}
if (user.role === "student") {
location.href = "./student/uebersicht.html?user=" + username;
return;
} else {
location.href = "./prof/uebersicht.html?user=" + username;
return;
}
}
</script>
</body>
</html>
+7 -2
View File
@@ -108,9 +108,14 @@ const _skillCb = {
},
"unset_epic_border": () => unsetProfileBorder(),
"title_of_honor": function() {},
"profile_pic": () => updateAllProfiles(userData)
"profile_pic": () => updateEverything()
};
function updateEverything() {
updateAllProfiles(userData);
refreshOverlays();
}
function chooseAccentColor() {
var elm = getId("skill_chooseAccentColor");
if (!elm) {
@@ -142,7 +147,7 @@ function setAccentColor(elm) {
hideOverlay("skill_chooseAccentColor");
_skillCb.accent_color();
refreshOverlays();
updateEverything()
}
+1 -1
View File
@@ -140,7 +140,7 @@ async function activateSkill(elm, data) {
try { eval(data.call_unset); } catch (err) { console.error(err); }
}
}
refreshOverlays();
updateEverything();
}
async function _buySkill(data) {
const elms = document.getElementsByClassName("skill_" + data.id);
+160 -164
View File
@@ -1,189 +1,185 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sign Up</title>
<link rel="stylesheet" href="./lib/basic.css" />
<script src="./auth.js" defer></script>
<script src="./lib/basic.js" defer></script>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sign Up</title>
<link rel="stylesheet" href="./lib/basic.css" />
<script src="./auth.js"></script>
<script src="./lib/basic.js"></script>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f6f8fa;
padding: 2rem 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f6f8fa;
padding: 2rem 0;
}
.signup-card {
background-color: #fff;
padding: 2.5rem 2rem;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
width: 100%;
max-width: 400px;
}
.signup-card {
background-color: #fff;
padding: 2.5rem 2rem;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
width: 100%;
max-width: 400px;
}
.signup-card h1 {
text-align: center;
margin-bottom: 1.5rem;
font-size: 1.5rem;
}
.signup-card h1 {
text-align: center;
margin-bottom: 1.5rem;
font-size: 1.5rem;
}
label {
display: block;
margin-bottom: 0.3rem;
font-weight: 600;
font-size: 0.9rem;
}
label {
display: block;
margin-bottom: 0.3rem;
font-weight: 600;
font-size: 0.9rem;
}
input,
select {
width: 100%;
padding: 0.5rem 0.75rem;
margin-bottom: 1rem;
border: 1px solid #d1d5da;
border-radius: 6px;
font-size: 1rem;
}
input,
select {
width: 100%;
padding: 0.5rem 0.75rem;
margin-bottom: 1rem;
border: 1px solid #d1d5da;
border-radius: 6px;
font-size: 1rem;
}
button {
width: 100%;
padding: 0.6rem;
background-color: #2ea44f;
color: #fff;
border: none;
border-radius: 6px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
}
button {
width: 100%;
padding: 0.6rem;
background-color: var(--accent_color_fg);
color: #fff;
border: none;
border-radius: 6px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
}
button:hover {
background-color: #279f43;
}
button:hover {
background-color: var(--accent_color_fg_o);
}
.login-text {
text-align: center;
margin-top: 1rem;
font-size: 0.9rem;
}
.login-text {
text-align: center;
margin-top: 1rem;
font-size: 0.9rem;
}
.login-text a {
color: #0366d6;
text-decoration: none;
}
.login-text a {
color: #0366d6;
text-decoration: none;
}
.login-text a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="signup-card">
<h1>Create Account</h1>
.login-text a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="signup-card">
<h1>Create Account</h1>
<label for="username">Username</label>
<input
type="text"
id="username"
name="username"
autocomplete="username"
autocapitalize="off"
autocorrect="off"
required
autofocus
/>
<label for="username">Username</label>
<input
type="text"
id="username"
name="username"
autocomplete="username"
autocapitalize="off"
autocorrect="off"
required
autofocus
/>
<label for="password">Password</label>
<input
type="password"
id="password"
name="password"
autocomplete="new-password"
required
/>
<label for="password">Password</label>
<input
type="password"
id="password"
name="password"
autocomplete="new-password"
required
/>
<label for="role">Role</label>
<select id="role" name="role" required>
<option value="student">Student</option>
<option value="professor">Professor</option>
</select>
<label for="role">Role</label>
<select id="role" name="role" required>
<option value="student">Student</option>
<option value="professor">Professor</option>
</select>
<button type="button" onclick="signup()">Sign up</button>
<button type="button" onclick="signup()">Sign up</button>
<div class="login-text">
Already have an account? <a href="login.html">Sign in</a>
</div>
</div>
<div class="login-text">Already have an account? <a href="login.html">Sign in</a></div>
</div>
<script>
const couchDB = new CouchDB();
<script>
const couchDB = new CouchDB();
async function getData() {
try {
const res = await couchDB.get("users");
return res || {};
} catch (e) {
console.error("Failed to get users:", e);
return {};
}
}
async function getData() {
try {
const res = await couchDB.get("users");
return res || {};
} catch (e) {
console.error("Failed to get users:", e);
return {};
}
}
async function signup() {
const username = document
.getElementById("username")
.value.trim()
.toLowerCase();
const password = document.getElementById("password").value;
const role = document.getElementById("role").value;
async function signup() {
const username = document.getElementById("username").value.trim().toLowerCase();
const password = document.getElementById("password").value;
const role = document.getElementById("role").value;
if (!username) {
return alert("Please enter a username!");
}
if (!password) {
return alert("Please enter a password!");
}
if (!username) {
return alert("Please enter a username!");
}
if (!password) {
return alert("Please enter a password!");
}
const data = await getData();
const data = await getData();
// Check if user already exists
if (data[username]) {
return alert("Username already exists!");
}
// Check if user already exists
if (data[username]) {
return alert("Username already exists!");
}
// Create new user object
data[username] = {
password: password,
level: 1,
avatar: null,
abilities: [],
enabled_abilities: [],
role: role,
courses: [],
};
// Create new user object
data[username] = {
password: password,
level: 1,
xp: 0,
avatar: null,
abilities: [],
enabled_abilities: [],
role: role,
courses: [],
};
try {
// Save to database
await couchDB.set("users", data);
alert("Account created successfully!");
try {
// Save to database
await couchDB.set("users", data);
alert("Account created successfully!");
// Redirect to login page
location.href = "./login.html";
} catch (e) {
console.error("Failed to create account:", e);
alert("Failed to create account. Please try again.");
}
}
</script>
</body>
// Redirect to login page
location.href = "./login.html";
} catch (e) {
console.error("Failed to create account:", e);
alert("Failed to create account. Please try again.");
}
}
</script>
</body>
</html>
+24 -3
View File
@@ -103,7 +103,7 @@
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
border-radius: var(--border-radius);
}
.weiter-btn:hover {
background-color: #0056b3;
@@ -130,6 +130,7 @@
<div class="main-container" id="container2" style="display: none;">
<div class="end-dialog">
<h2>Vielen Dank für deine Teilnahme!</h2><br>
<h3 id="level_up" style="display: none;">Level up!<br><br><br></h3>
<button class="weiter-btn" onclick="onEndEvent(this)">
Zurück zur Hauptseite
</button>
@@ -263,7 +264,7 @@ function updateContent(old_page, page) {
if (page >= courseData.questions.length) {
getId("container1").style.display = "none";
getId("container2").style.display = "unset";
updateCourseAnswer(finalResult);
onFinishEva();
return;
}
@@ -272,7 +273,15 @@ function updateContent(old_page, page) {
getId("content").querySelector(".input").focus();
}
async function onEndEvent(elm) {
async function onFinishEva() {
await playLevelSound();
await updateCourseAnswer(finalResult);
if (await levelUpXP(couchDB)) {
const lvlElm = getId("level_up");
if (lvlElm) lvlElm.style.display = "unset";
}
}
async function onEndEvent() {
location.href = "./uebersicht.html?user=" + userData.name;
}
@@ -283,6 +292,18 @@ async function onEndEvent(elm) {
return;
}
if (!courseData.expire_date) {
console.error("courseData.expire_date is null");
return;
}
const expire_date = new Date(courseData.expire_date);
if (courseData.questions.length == 0 || expire_date < new Date()) {
onEndEvent();
return;
}
finalResult = [];
for (var i in courseData.questions) finalResult.push(null);