Skip to content

Commit 429f635

Browse files
committed
add: tutorialProgress
1 parent c16a3de commit 429f635

File tree

9 files changed

+334
-19
lines changed

9 files changed

+334
-19
lines changed

models/tutorialProgress.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// jshint esversion: 6
2+
// jshint node: true
3+
"use strict";
4+
5+
const mongoose = require("mongoose");
6+
7+
const QuestionProgressSchema = new mongoose.Schema(
8+
{
9+
answered: {
10+
type: Boolean,
11+
default: false,
12+
},
13+
correct: {
14+
type: Boolean,
15+
default: false,
16+
},
17+
answer: {
18+
type: mongoose.Schema.Types.Mixed,
19+
},
20+
},
21+
{ _id: false }
22+
);
23+
24+
const StepProgressSchema = new mongoose.Schema(
25+
{
26+
seen: {
27+
type: Boolean,
28+
default: false,
29+
},
30+
31+
questions: {
32+
type: Map,
33+
of: QuestionProgressSchema,
34+
default: {},
35+
},
36+
},
37+
{ _id: false }
38+
);
39+
40+
const TutorialProgressSchema = new mongoose.Schema(
41+
{
42+
user: {
43+
type: mongoose.Schema.Types.ObjectId,
44+
ref: "User",
45+
required: true,
46+
index: true,
47+
},
48+
49+
tutorial: {
50+
type: mongoose.Schema.Types.ObjectId,
51+
ref: "Tutorial",
52+
required: true,
53+
index: true,
54+
},
55+
56+
steps: {
57+
type: Map,
58+
of: StepProgressSchema,
59+
default: {},
60+
},
61+
},
62+
{
63+
timestamps: true,
64+
}
65+
);
66+
67+
// 🔐 Ein User darf pro Tutorial nur EIN Progress-Dokument haben
68+
TutorialProgressSchema.index({ user: 1, tutorial: 1 }, { unique: true });
69+
70+
module.exports = mongoose.model("TutorialProgress", TutorialProgressSchema);

routes/tutorial/index.js

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,78 @@
22
// jshint node: true
33
"use strict";
44

5-
var express = require("express");
6-
var TutorialRouter = express.Router();
5+
const express = require("express");
6+
const TutorialRouter = express.Router();
77

88
const { userAuthorization } = require("../../helper/userAuthorization");
99
const { upload } = require("../../helper/imageUpload");
1010

11-
TutorialRouter.route("/").post(
12-
userAuthorization,
13-
upload.any(),
14-
require("./postTutorial").postTutorial
15-
);
11+
// Tutorial CRUD
12+
const { postTutorial } = require("./postTutorial");
13+
const { putTutorial } = require("./putTutorial");
14+
const { deleteTutorial } = require("./deleteTutorial");
15+
const { getTutorial } = require("./getTutorial");
16+
const { getTutorials } = require("./getTutorials");
17+
const { getAllTutorials } = require("./getAllTutorials");
18+
const { getUserTutorials } = require("./getUserTutorials");
19+
20+
// Tutorial Progress
21+
const { startTutorial } = require("./tutorialProgress/startTutorial");
22+
const { markStepSeen } = require("./tutorialProgress/markStepSeen");
23+
const { answerQuestion } = require("./tutorialProgress/answerQuestion");
24+
const { getProgress } = require("./tutorialProgress/getProgress");
25+
const { deleteProgress } = require("./tutorialProgress/deleteProgress");
26+
const { getAllProgress } = require("./tutorialProgress/getAllProgress");
27+
28+
/* ---------- LIST ROUTES ---------- */
29+
30+
TutorialRouter.get("/", getTutorials);
31+
TutorialRouter.get("/getAllTutorials", userAuthorization, getAllTutorials);
32+
TutorialRouter.get("/getUserTutorials", userAuthorization, getUserTutorials);
33+
34+
/* ---------- PROGRESS ROUTES (FIRST!) ---------- */
35+
36+
// 🔥 alle Progresse des Users
37+
TutorialRouter.get("/progress", userAuthorization, getAllProgress);
1638

17-
TutorialRouter.route("/:tutorialId").put(
39+
// 🔥 Progress eines Tutorials
40+
TutorialRouter.get("/:tutorialId/progress", userAuthorization, getProgress);
41+
42+
TutorialRouter.post(
43+
"/:tutorialId/steps/:stepId/seen",
1844
userAuthorization,
19-
upload.any(),
20-
require("./putTutorial").putTutorial
45+
markStepSeen
2146
);
2247

23-
TutorialRouter.route("/:tutorialId").delete(
48+
TutorialRouter.post(
49+
"/:tutorialId/steps/:stepId/questions/answer",
2450
userAuthorization,
25-
require("./deleteTutorial").deleteTutorial
51+
answerQuestion
2652
);
2753

28-
TutorialRouter.route("/").get(require("./getTutorials").getTutorials);
54+
TutorialRouter.post("/:tutorialId/start", userAuthorization, startTutorial);
2955

30-
TutorialRouter.route("/getAllTutorials").get(
56+
TutorialRouter.delete(
57+
"/:tutorialId/progress",
3158
userAuthorization,
32-
require("./getAllTutorials").getAllTutorials
59+
deleteProgress
3360
);
3461

35-
TutorialRouter.route("/getUserTutorials").get(
62+
/* ---------- CRUD ROUTES ---------- */
63+
64+
TutorialRouter.post("/", userAuthorization, upload.any(), postTutorial);
65+
66+
TutorialRouter.put(
67+
"/:tutorialId",
3668
userAuthorization,
37-
require("./getUserTutorials").getUserTutorials
69+
upload.any(),
70+
putTutorial
3871
);
3972

40-
TutorialRouter.route("/:tutorialId").get(require("./getTutorial").getTutorial);
73+
TutorialRouter.delete("/:tutorialId", userAuthorization, deleteTutorial);
74+
75+
/* ---------- LAST: SINGLE TUTORIAL ---------- */
76+
77+
TutorialRouter.get("/:tutorialId", getTutorial);
4178

4279
module.exports = TutorialRouter;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// jshint esversion: 8
2+
// jshint node: true
3+
"use strict";
4+
5+
const TutorialProgress = require("../../../models/tutorialProgress");
6+
7+
/**
8+
* @api {post} /tutorial/:tutorialId/steps/:stepId/questions/:questionId/answer
9+
* @apiName answerQuestion
10+
* @apiDescription Stores the user's answer for a question.
11+
* @apiGroup Tutorial
12+
*/
13+
const answerQuestion = async function (req, res) {
14+
try {
15+
const { tutorialId, stepId } = req.params;
16+
const userId = req.user._id;
17+
const { correct, questionId } = req.body;
18+
await TutorialProgress.findOneAndUpdate(
19+
{ user: userId, tutorial: tutorialId },
20+
{
21+
$set: {
22+
[`steps.${stepId}.questions.${questionId}`]: {
23+
answered: true,
24+
correct: !!correct,
25+
},
26+
},
27+
},
28+
{ upsert: false }
29+
);
30+
31+
return res.status(200).send({
32+
message: "Question answered.",
33+
});
34+
} catch (err) {
35+
return res.status(500).send(err);
36+
}
37+
};
38+
39+
module.exports = {
40+
answerQuestion,
41+
};
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"use strict";
2+
3+
const TutorialProgress = require("../../../models/tutorialProgress");
4+
5+
const deleteProgress = async (req, res) => {
6+
try {
7+
const { tutorialId } = req.params;
8+
const userId = req.user._id;
9+
10+
await TutorialProgress.deleteOne({
11+
user: userId,
12+
tutorial: tutorialId,
13+
});
14+
15+
return res.status(200).send({
16+
message: "Tutorial progress reset.",
17+
});
18+
} catch (err) {
19+
return res.status(500).send(err);
20+
}
21+
};
22+
23+
module.exports = {
24+
deleteProgress,
25+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const TutorialProgress = require("../../../models/tutorialProgress");
2+
3+
const getAllProgress = async (req, res) => {
4+
try {
5+
const userId = req.user._id;
6+
7+
const progress = await TutorialProgress.find({ user: userId });
8+
9+
return res.status(200).send({
10+
progress,
11+
});
12+
} catch (err) {
13+
return res.status(500).send(err);
14+
}
15+
};
16+
17+
module.exports = { getAllProgress };
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const TutorialProgress = require("../../../models/tutorialProgress");
2+
3+
const getProgress = async (req, res) => {
4+
try {
5+
const { tutorialId } = req.params;
6+
const userId = req.user._id;
7+
8+
const progress = await TutorialProgress.findOne({
9+
user: userId,
10+
tutorial: tutorialId,
11+
});
12+
13+
return res.status(200).send({
14+
progress,
15+
});
16+
} catch (err) {
17+
return res.status(500).send(err);
18+
}
19+
};
20+
21+
module.exports = {
22+
getProgress,
23+
};
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// jshint esversion: 8
2+
// jshint node: true
3+
"use strict";
4+
5+
const TutorialProgress = require("../../../models/tutorialProgress");
6+
7+
/**
8+
* @api {post} /tutorial/:tutorialId/steps/:stepId/seen
9+
* @apiName markStepSeen
10+
* @apiDescription Marks a tutorial step as seen for the current user.
11+
* @apiGroup Tutorial
12+
*/
13+
const markStepSeen = async function (req, res) {
14+
try {
15+
const { tutorialId, stepId } = req.params;
16+
const userId = req.user._id;
17+
18+
await TutorialProgress.findOneAndUpdate(
19+
{ user: userId, tutorial: tutorialId },
20+
{
21+
$set: {
22+
[`steps.${stepId}.seen`]: true,
23+
},
24+
},
25+
{ upsert: false }
26+
);
27+
28+
return res.status(200).send({
29+
message: "Step marked as seen.",
30+
});
31+
} catch (err) {
32+
return res.status(500).send(err);
33+
}
34+
};
35+
36+
module.exports = {
37+
markStepSeen,
38+
};
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// jshint esversion: 8
2+
// jshint node: true
3+
"use strict";
4+
5+
const mongoose = require("mongoose");
6+
7+
const Tutorial = require("../../../models/tutorial");
8+
const TutorialProgress = require("../../../models/tutorialProgress");
9+
10+
/**
11+
* @api {post} /tutorial/:tutorialId/start Start tutorial
12+
* @apiName startTutorial
13+
* @apiDescription Initializes tutorial progress for the current user.
14+
* @apiGroup Tutorial
15+
*
16+
* @apiParam {ObjectId} tutorialId ID of the tutorial to start
17+
*
18+
* @apiSuccess (200) {String} message Tutorial started successfully.
19+
* @apiSuccess (200) {Object} progress Tutorial progress document
20+
*
21+
* @apiError (404) Tutorial not found
22+
* @apiError (500) Database error
23+
*/
24+
const startTutorial = async function (req, res) {
25+
try {
26+
const tutorialId = req.params.tutorialId;
27+
const userId = req.user._id;
28+
29+
// 🔍 Tutorial existiert?
30+
const tutorialExists = await Tutorial.exists({ _id: tutorialId });
31+
if (!tutorialExists) {
32+
return res.status(404).send({
33+
message: "Tutorial not found.",
34+
});
35+
}
36+
37+
// 🔁 Progress schon vorhanden?
38+
let progress = await TutorialProgress.findOne({
39+
user: userId,
40+
tutorial: tutorialId,
41+
});
42+
43+
// 🆕 Falls nicht → anlegen
44+
if (!progress) {
45+
progress = await TutorialProgress.create({
46+
user: userId,
47+
tutorial: tutorialId,
48+
steps: {},
49+
});
50+
}
51+
52+
return res.status(200).send({
53+
message: "Tutorial started successfully.",
54+
progress,
55+
});
56+
} catch (err) {
57+
return res.status(500).send(err);
58+
}
59+
};
60+
61+
module.exports = {
62+
startTutorial,
63+
};

routes/user/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ var UserRouter = express.Router();
77

88
const { userAuthorization } = require("../../helper/userAuthorization");
99
const { requestPasswordReset, resetPassword } = require("./user/resetPassword");
10-
const { refresh } = require("./user/refresh");UserRouter.route("/register").post(require("./user/register").register);
10+
const { refresh } = require("./user/refresh");
11+
UserRouter.route("/register").post(require("./user/register").register);
1112

1213
UserRouter.route("/login").post(require("./user/nativeLogin").nativeLogin);
1314

0 commit comments

Comments
 (0)