From d83c97e87bf953d399dfda1bfd12e2dd7edcf5a7 Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Thu, 4 Jan 2024 22:50:10 -0500 Subject: [PATCH 01/18] update .env example to add more documentation --- .env.example | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.example b/.env.example index 67057f9..f3502b0 100644 --- a/.env.example +++ b/.env.example @@ -25,5 +25,6 @@ GOOGLE_ID = x GOOGLE_SECRET = x # Only needed if you are using Github login +# For local development, use http://localhost:3000 as the homepage URL and http://localhost:3000/oauth/github/callback as the callback URL GITHUB_ID = x GITHUB_SECRET = x From 4e7ad8cdcd69317d02a9df4ff73bca73f53e0b34 Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Fri, 5 Jan 2024 00:27:11 -0500 Subject: [PATCH 02/18] add tags --- src/controllers/lessons.js | 7 +++++++ src/models/Lesson.js | 3 +++ src/tailwind.css | 2 ++ src/views/addLesson.pug | 6 ++++++ src/views/lesson.pug | 9 +++++++++ src/views/mixins/lessonCard.pug | 10 ++++++++++ 6 files changed, 37 insertions(+) diff --git a/src/controllers/lessons.js b/src/controllers/lessons.js index f37ab42..36f4200 100644 --- a/src/controllers/lessons.js +++ b/src/controllers/lessons.js @@ -55,6 +55,13 @@ export const addEditLesson = async (req, res) => { motivationTitle: req.body.motivationTitle, cohort: req.body.cohort, note: req.body.note, + tags: req.body.tags + ? req.body.tags + .split(",") + .map((tag) => tag.trim().toLowerCase()) + // add space after comma + .map((tag) => tag.replace(/,/g, ", ")) + : [], timestamps: timestamps, }; const lesson = await Lesson.findByIdAndUpdate( diff --git a/src/models/Lesson.js b/src/models/Lesson.js index 39edb50..134944e 100644 --- a/src/models/Lesson.js +++ b/src/models/Lesson.js @@ -49,6 +49,9 @@ const lessonSchema = new Schema({ motivationTitle: { type: String }, + tags: { + type: [String] + }, timestamps: [TimestampSchema], cohort: Number, note: String diff --git a/src/tailwind.css b/src/tailwind.css index 72f6497..0228592 100644 --- a/src/tailwind.css +++ b/src/tailwind.css @@ -85,6 +85,8 @@ @apply text-right; } #timestamps, + #tags, + /* do i need to add this here? */ #hw-items, #pw-items { @apply mt-3 w-full border px-5 py-3; diff --git a/src/views/addLesson.pug b/src/views/addLesson.pug index 8f96ba1..9ab7c18 100644 --- a/src/views/addLesson.pug +++ b/src/views/addLesson.pug @@ -53,6 +53,12 @@ block content input#ts-time-0(type="number", name="tsTime") label(for="ts-title-0") Title input#ts-title-0(type="text", name="tsTitle") + #tags.col-span-4 + h2.col-span-4.mb-3 Tags + label(for="tags") Tags + textarea#tag.col-span-4(type="text", name="tags" + placeholder="html, css, javascript, js, career help,ejs" + ) #{edit? lesson.tags : ""} .col-span-4.mt-3.text-center button(class="btn primary max-w-1/3 mx-auto" type="submit") #{action} Class diff --git a/src/views/lesson.pug b/src/views/lesson.pug index 25bb9bc..c8ed4ed 100644 --- a/src/views/lesson.pug +++ b/src/views/lesson.pug @@ -68,6 +68,15 @@ block content h2 Motivation a(href=lesson.motivationLink) #{lesson.motivationTitle} + if lesson.tags && lesson.tags.length + h2 Tags + .tags.flex.flex-wrap.px-3.py-2 + each tag in lesson.tags + .text-xs.mr-2.border + = tag + + + if lesson.videoId && !lesson.twitchVideo h3.mt-8 Feed the algorithm and spread the word! p #[a(href=`https://www.youtube.com/watch?v=${lesson.videoId}` target="_blank") Like, comment and subscribe on Youtube] diff --git a/src/views/mixins/lessonCard.pug b/src/views/mixins/lessonCard.pug index ce065f7..cadb7b3 100644 --- a/src/views/mixins/lessonCard.pug +++ b/src/views/mixins/lessonCard.pug @@ -26,11 +26,21 @@ mixin lessonCard(lesson) input.checkedin.hidden(type="checkbox" id="checkedin-" + lesson._id autocomplete="off" checked) | No Check In + + .card-main-content.py-4.px-6.mb-12.group a(href=`/class/${lesson.permalink}` class="hover:no-underline") img.w-full.mb-2(src=lesson.thumbnail class="group-hover:shadow-[0_0_10px_rgba(0,_0,_0,_0.2)]") h2(class="group-hover:text-pink-900") #{lesson.title} + //- show tags if there are any + if lesson.tags && lesson.tags.length + h5.mt-3 Tags + .tags.flex.flex-wrap.px-3.py-2.mt-5 + each tag in lesson.tags + .text-xs.mr-2.border + = tag + a.absolute.bottom-3(href=`/class/${lesson.permalink}`) More Info if lesson.videoId && !lesson.twitchVideo a.absolute.bottom-3.right-6.text-right(href=`https://youtu.be/${lesson.videoId}`) Youtube From ddc3a6fd6c530dd800a3c249644a6aa8e5f9f4bb Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Sun, 7 Jan 2024 13:29:56 -0500 Subject: [PATCH 03/18] fix styling and add link to query --- src/views/lesson.pug | 4 ++-- src/views/mixins/lessonCard.pug | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/views/lesson.pug b/src/views/lesson.pug index c8ed4ed..3644a5b 100644 --- a/src/views/lesson.pug +++ b/src/views/lesson.pug @@ -72,7 +72,7 @@ block content h2 Tags .tags.flex.flex-wrap.px-3.py-2 each tag in lesson.tags - .text-xs.mr-2.border + a.text-xs.mr-2(href=`/class?tags=${tag}`) = tag @@ -82,7 +82,7 @@ block content p #[a(href=`https://www.youtube.com/watch?v=${lesson.videoId}` target="_blank") Like, comment and subscribe on Youtube] if assigned.length - h2.mt-10.mb-3 Homework Assigned + h2.mt-10.mb-3.border-2.border-orange-600.border-dashed Homework Assigned each homework in assigned .mb-6(class="shadow-[0_2px_10px_0_rgba(0,0,0,0.1)]") +homework(homework) diff --git a/src/views/mixins/lessonCard.pug b/src/views/mixins/lessonCard.pug index cadb7b3..c9f3f79 100644 --- a/src/views/mixins/lessonCard.pug +++ b/src/views/mixins/lessonCard.pug @@ -33,12 +33,12 @@ mixin lessonCard(lesson) img.w-full.mb-2(src=lesson.thumbnail class="group-hover:shadow-[0_0_10px_rgba(0,_0,_0,_0.2)]") h2(class="group-hover:text-pink-900") #{lesson.title} - //- show tags if there are any + if lesson.tags && lesson.tags.length - h5.mt-3 Tags - .tags.flex.flex-wrap.px-3.py-2.mt-5 + h4.mt-3 Tags + .tags.flex.flex-wrap.px-3.py-2 each tag in lesson.tags - .text-xs.mr-2.border + a.text-xs.mr-2(href=`/class?tags=${tag}`) = tag a.absolute.bottom-3(href=`/class/${lesson.permalink}`) More Info From cd5c3a180afc5cc45faea6da1529a172821d43fb Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Sun, 7 Jan 2024 14:24:13 -0500 Subject: [PATCH 04/18] add router logic for filtering by tags --- src/controllers/lessons.js | 360 +++++++++++++++++--------------- src/routes/lessonRouter.js | 1 + src/views/lesson.pug | 2 +- src/views/mixins/lessonCard.pug | 2 +- 4 files changed, 190 insertions(+), 175 deletions(-) diff --git a/src/controllers/lessons.js b/src/controllers/lessons.js index 36f4200..ad27a37 100644 --- a/src/controllers/lessons.js +++ b/src/controllers/lessons.js @@ -8,203 +8,217 @@ import Homework from "../models/Homework.js"; import { getHwProgress } from "./homework.js"; export const addEditLessonForm = async (req, res) => { - if (!req.isAuthenticated() || !req.user.admin) return res.redirect("/"); - const edit = !!req.params.id; - let lesson = null; - if (edit) { - lesson = await Lesson.findById(req.params.id).lean(); - lesson.classNo = lesson.classNo.join(","); - lesson.checkin = lesson.checkin.join(","); - lesson.slides = lesson.slides.join(","); - lesson.dates = lesson.dates - .map((date) => { - return date.toISOString().split("T")[0]; - }) - .join(","); - } - res.render("addLesson", { edit, lesson }); + if (!req.isAuthenticated() || !req.user.admin) return res.redirect("/"); + const edit = !!req.params.id; + let lesson = null; + if (edit) { + lesson = await Lesson.findById(req.params.id).lean(); + lesson.classNo = lesson.classNo.join(","); + lesson.checkin = lesson.checkin.join(","); + lesson.slides = lesson.slides.join(","); + lesson.dates = lesson.dates + .map((date) => { + return date.toISOString().split("T")[0]; + }) + .join(","); + } + res.render("addLesson", { edit, lesson }); }; export const addEditLesson = async (req, res) => { - if (!req.isAuthenticated() || !req.user.admin) return res.redirect("/"); - try { - let dates = []; - if (req.body.date) { - dates = req.body.date.split(",").map((date) => new Date(date)); - } - let slides = []; - const timestamps = []; - for (let i = 0; i < req.body.tsTime.length; i++) { - timestamps.push({ - time: Number(req.body.tsTime[i]), - title: req.body.tsTitle[i], - }); - } - const lessonData = { - videoId: req.body.videoId, - twitchVideo: !!req.body.twitch ? true : false, - title: req.body.videoTitle, - dates: dates, - permalink: req.body.permalink, - thumbnail: req.body.thumbnail, - classNo: req.body.number ? req.body.number.split(",") : [], - slides: req.body.slides ? req.body.slides.split(",") : [], - materials: req.body.materials, - checkin: req.body.checkin ? req.body.checkin.split(",") : [], - motivationLink: req.body.motivationLink, - motivationTitle: req.body.motivationTitle, - cohort: req.body.cohort, - note: req.body.note, - tags: req.body.tags - ? req.body.tags - .split(",") - .map((tag) => tag.trim().toLowerCase()) - // add space after comma - .map((tag) => tag.replace(/,/g, ", ")) - : [], - timestamps: timestamps, - }; - const lesson = await Lesson.findByIdAndUpdate( - req.params.id || mongoose.Types.ObjectId(), - lessonData, - { upsert: true, new: true } - ); + if (!req.isAuthenticated() || !req.user.admin) return res.redirect("/"); + try { + let dates = []; + if (req.body.date) { + dates = req.body.date.split(",").map((date) => new Date(date)); + } + let slides = []; + const timestamps = []; + for (let i = 0; i < req.body.tsTime.length; i++) { + timestamps.push({ + time: Number(req.body.tsTime[i]), + title: req.body.tsTitle[i], + }); + } + const lessonData = { + videoId: req.body.videoId, + twitchVideo: !!req.body.twitch ? true : false, + title: req.body.videoTitle, + dates: dates, + permalink: req.body.permalink, + thumbnail: req.body.thumbnail, + classNo: req.body.number ? req.body.number.split(",") : [], + slides: req.body.slides ? req.body.slides.split(",") : [], + materials: req.body.materials, + checkin: req.body.checkin ? req.body.checkin.split(",") : [], + motivationLink: req.body.motivationLink, + motivationTitle: req.body.motivationTitle, + cohort: req.body.cohort, + note: req.body.note, + tags: req.body.tags + ? req.body.tags + .split(",") + .map((tag) => tag.trim().toLowerCase()) + // add space after comma + .map((tag) => tag.replace(/,/g, ", ")) + : [], + timestamps: timestamps, + }; + const lesson = await Lesson.findByIdAndUpdate( + req.params.id || mongoose.Types.ObjectId(), + lessonData, + { upsert: true, new: true } + ); - // if this is a new class, update all users with current class = null so this is now their current class - if (!req.params.id) { - await User.updateMany( - { currentClass: null }, - { currentClass: lesson._id } - ); - } - req.session.flash = { - type: "success", - message: [`Class ${!!req.params.id ? "updated" : "added"}`], - }; - } catch (err) { - console.log(err); - req.session.flash = { - type: "error", - message: [`Class not ${!!req.params.id ? "updated" : "added"}`], - }; - } finally { - res.redirect("/class/add"); - } + // if this is a new class, update all users with current class = null so this is now their current class + if (!req.params.id) { + await User.updateMany( + { currentClass: null }, + { currentClass: lesson._id } + ); + } + req.session.flash = { + type: "success", + message: [`Class ${!!req.params.id ? "updated" : "added"}`], + }; + } catch (err) { + console.log(err); + req.session.flash = { + type: "error", + message: [`Class not ${!!req.params.id ? "updated" : "added"}`], + }; + } finally { + res.redirect("/class/add"); + } }; export const getAllLessonsProgress = async (userId, lessons) => { - const lessonProgress = await LessonProgress.find({ user: userId }).lean(); - const lessonsObj = {}; - lessonProgress.forEach((p) => { - lessonsObj[p.lesson] = { - watched: p.watched, - checkedIn: p.checkedIn, - }; - }); - lessons.forEach((lesson) => { - const prog = lessonsObj[lesson._id]; - lesson.watched = prog ? !!prog.watched : false; - lesson.checkedIn = prog ? !!prog.checkedIn : false; - }); - return lessons; + const lessonProgress = await LessonProgress.find({ user: userId }).lean(); + const lessonsObj = {}; + lessonProgress.forEach((p) => { + lessonsObj[p.lesson] = { + watched: p.watched, + checkedIn: p.checkedIn, + }; + }); + lessons.forEach((lesson) => { + const prog = lessonsObj[lesson._id]; + lesson.watched = prog ? !!prog.watched : false; + lesson.checkedIn = prog ? !!prog.checkedIn : false; + }); + return lessons; }; export const allLessons = async (req, res) => { - let lessons = await Lesson.find().lean().sort({ _id: 1 }); - if (req.isAuthenticated()) { - lessons = await getAllLessonsProgress(req.user.id, lessons); - } - res.render("allLessons", { lessons }); + let lessons = await Lesson.find().lean().sort({ _id: 1 }); + if (req.isAuthenticated()) { + lessons = await getAllLessonsProgress(req.user.id, lessons); + } + res.render("allLessons", { lessons }); }; export const getLessonProgress = async (userId, lesson) => { - const progress = await LessonProgress.findOne({ - user: userId, - lesson: lesson._id, - }); - lesson.watched = progress ? progress.watched : false; - lesson.checkedIn = progress ? progress.checkedIn : false; - return lesson; + const progress = await LessonProgress.findOne({ + user: userId, + lesson: lesson._id, + }); + lesson.watched = progress ? progress.watched : false; + lesson.checkedIn = progress ? progress.checkedIn : false; + return lesson; }; export const showLesson = async (req, res) => { - try { - let lesson = await Lesson.findOne({ - permalink: req.params.permalink, - }).lean(); - let next = await Lesson.find({ _id: { $gt: lesson._id } }) - .sort({ _id: 1 }) - .limit(1); - next = next.length ? next[0].permalink : null; - let prev = await Lesson.find({ _id: { $lt: lesson._id } }) - .sort({ _id: -1 }) - .limit(1); - prev = prev.length ? prev[0].permalink : null; - let assigned = await Homework.find({ classNo: { $in: lesson.classNo } }) - .lean() - .sort({ _id: 1 }) - .populate(["items", "extras"]); - let due = await Homework.find({ dueNo: { $in: lesson.classNo } }) - .lean() - .sort({ _id: 1 }) - .populate(["items", "extras"]); + try { + let lesson = await Lesson.findOne({ + permalink: req.params.permalink, + }).lean(); + let next = await Lesson.find({ _id: { $gt: lesson._id } }) + .sort({ _id: 1 }) + .limit(1); + next = next.length ? next[0].permalink : null; + let prev = await Lesson.find({ _id: { $lt: lesson._id } }) + .sort({ _id: -1 }) + .limit(1); + prev = prev.length ? prev[0].permalink : null; + let assigned = await Homework.find({ classNo: { $in: lesson.classNo } }) + .lean() + .sort({ _id: 1 }) + .populate(["items", "extras"]); + let due = await Homework.find({ dueNo: { $in: lesson.classNo } }) + .lean() + .sort({ _id: 1 }) + .populate(["items", "extras"]); - if (req.isAuthenticated()) { - lesson = await getLessonProgress(req.user.id, lesson); - if (assigned.length) - assigned = await getHwProgress(req.user.id, assigned); - if (due.length) due = await getHwProgress(req.user.id, due); - } - res.render("lesson", { lesson, next, prev, assigned, due }); - } catch (err) { - console.log(err); - res.redirect("/class/all"); - } + if (req.isAuthenticated()) { + lesson = await getLessonProgress(req.user.id, lesson); + if (assigned.length) + assigned = await getHwProgress(req.user.id, assigned); + if (due.length) due = await getHwProgress(req.user.id, due); + } + res.render("lesson", { lesson, next, prev, assigned, due }); + } catch (err) { + console.log(err); + res.redirect("/class/all"); + } +}; + +export const filterByTags = async (req, res) => { + try { + const tags = req.query.tags?.split(",").map((tag) => tag.trim()); + console.log({ tags }); + const lessons = await Lesson.find(tags ? { tags: { $in: tags } } : {}) + .sort({ _id: 1 }) + .lean(); + res.render("allLessons", { lessons }); + } catch (err) { + console.log(err); + res.redirect("/class/all"); + } }; export const deleteLesson = async (req, res) => { - if (!req.user?.admin) return res.redirect("/"); - try { - const lessonId = req.params.id; - let next = await Lesson.find({ _id: { $gt: lessonId } }) - .sort({ _id: 1 }) - .limit(1); - const nextId = next[0] ? next[0]._id : null; - const res = await User.updateMany( - { currentClass: lessonId }, - { currentClass: nextId } - ); - await LessonProgress.deleteMany({ lesson: lessonId }); - await Lesson.deleteOne({ _id: lessonId }); - } catch (err) { - console.log(err); - } finally { - res.redirect("/class/all"); - } + if (!req.user?.admin) return res.redirect("/"); + try { + const lessonId = req.params.id; + let next = await Lesson.find({ _id: { $gt: lessonId } }) + .sort({ _id: 1 }) + .limit(1); + const nextId = next[0] ? next[0]._id : null; + const res = await User.updateMany( + { currentClass: lessonId }, + { currentClass: nextId } + ); + await LessonProgress.deleteMany({ lesson: lessonId }); + await Lesson.deleteOne({ _id: lessonId }); + } catch (err) { + console.log(err); + } finally { + res.redirect("/class/all"); + } }; export const toggleWatched = async (req, res) => { - if (!req.user) return res.status(401).json({ msg: "not logged in" }); - try { - const userId = req.user.id; - const lessonId = req.params.id; - await LessonProgress.toggleWatched(lessonId, userId); - res.json({ msg: "toggled lesson watched" }); - } catch (err) { - console.log(err); - res.status(err.status || 500).json({ error: err.message }); - } + if (!req.user) return res.status(401).json({ msg: "not logged in" }); + try { + const userId = req.user.id; + const lessonId = req.params.id; + await LessonProgress.toggleWatched(lessonId, userId); + res.json({ msg: "toggled lesson watched" }); + } catch (err) { + console.log(err); + res.status(err.status || 500).json({ error: err.message }); + } }; export const toggleCheckedIn = async (req, res) => { - if (!req.user) return res.status(401).json({ msg: "not logged in" }); - try { - const userId = req.user.id; - const lessonId = req.params.id; - await LessonProgress.toggleCheckedIn(lessonId, req.user.id); - res.json({ msg: "toggled lesson checked in" }); - } catch (err) { - console.log(err); - res.status(err.status || 500).json({ error: err.message }); - } + if (!req.user) return res.status(401).json({ msg: "not logged in" }); + try { + const userId = req.user.id; + const lessonId = req.params.id; + await LessonProgress.toggleCheckedIn(lessonId, req.user.id); + res.json({ msg: "toggled lesson checked in" }); + } catch (err) { + console.log(err); + res.status(err.status || 500).json({ error: err.message }); + } }; diff --git a/src/routes/lessonRouter.js b/src/routes/lessonRouter.js index d589e64..9d39acd 100644 --- a/src/routes/lessonRouter.js +++ b/src/routes/lessonRouter.js @@ -11,6 +11,7 @@ router.post("/add", lessons.addEditLesson); router.post("/edit/:id", lessons.addEditLesson); router.get("/all", lessons.allLessons); +router.get("/tags", lessons.filterByTags); router.get("/:permalink", lessons.showLesson); router.put("/watched/:id", lessons.toggleWatched); diff --git a/src/views/lesson.pug b/src/views/lesson.pug index 3644a5b..2680118 100644 --- a/src/views/lesson.pug +++ b/src/views/lesson.pug @@ -72,7 +72,7 @@ block content h2 Tags .tags.flex.flex-wrap.px-3.py-2 each tag in lesson.tags - a.text-xs.mr-2(href=`/class?tags=${tag}`) + a.text-xs.mr-2(href=`/class/tags?tags=${tag}`) = tag diff --git a/src/views/mixins/lessonCard.pug b/src/views/mixins/lessonCard.pug index c9f3f79..2961af0 100644 --- a/src/views/mixins/lessonCard.pug +++ b/src/views/mixins/lessonCard.pug @@ -38,7 +38,7 @@ mixin lessonCard(lesson) h4.mt-3 Tags .tags.flex.flex-wrap.px-3.py-2 each tag in lesson.tags - a.text-xs.mr-2(href=`/class?tags=${tag}`) + a.text-xs.mr-2(href=`/class/tags?tags=${tag}`) = tag a.absolute.bottom-3(href=`/class/${lesson.permalink}`) More Info From f7300b671dc98af28687d1d92b448d22b7321b77 Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Sun, 7 Jan 2024 14:40:59 -0500 Subject: [PATCH 05/18] add filteredLessons page --- src/controllers/lessons.js | 9 ++++++--- src/views/filteredLessons.pug | 25 +++++++++++++++++++++++++ src/views/mixins/lessonCard.pug | 2 +- 3 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 src/views/filteredLessons.pug diff --git a/src/controllers/lessons.js b/src/controllers/lessons.js index ad27a37..9cd8b9c 100644 --- a/src/controllers/lessons.js +++ b/src/controllers/lessons.js @@ -165,11 +165,14 @@ export const showLesson = async (req, res) => { export const filterByTags = async (req, res) => { try { const tags = req.query.tags?.split(",").map((tag) => tag.trim()); - console.log({ tags }); - const lessons = await Lesson.find(tags ? { tags: { $in: tags } } : {}) + let lessons = await Lesson.find(tags ? { tags: { $in: tags } } : {}) .sort({ _id: 1 }) .lean(); - res.render("allLessons", { lessons }); + + if (req.isAuthenticated()) { + lessons = await getAllLessonsProgress(req.user.id, lessons); + } + res.render("filteredLessons", { lessons }); } catch (err) { console.log(err); res.redirect("/class/all"); diff --git a/src/views/filteredLessons.pug b/src/views/filteredLessons.pug new file mode 100644 index 0000000..1f07a58 --- /dev/null +++ b/src/views/filteredLessons.pug @@ -0,0 +1,25 @@ +extends layouts/default.pug +include ./mixins/lessonCard.pug + +block variables + - title = "Classes" + - active = "Classes" + +block content + if !loggedIn + include ./partials/flash-nologin.pug + + h1.mb-4 Filter Results + + + + #lessons.grid.gap-y-12.gap-x-16(class="sm:gap-y-24 grid-cols-[repeat(auto-fill,_minmax(285px,_1fr))]") + each lesson in lessons + +lessonCard(lesson) + + include ./partials/to-top.pug + +append scripts + if loggedIn + script(src="/js/lessonProgress.js") + script(src="/js/lessonDone.js") diff --git a/src/views/mixins/lessonCard.pug b/src/views/mixins/lessonCard.pug index 2961af0..3f4fa38 100644 --- a/src/views/mixins/lessonCard.pug +++ b/src/views/mixins/lessonCard.pug @@ -35,7 +35,7 @@ mixin lessonCard(lesson) if lesson.tags && lesson.tags.length - h4.mt-3 Tags + .tags.flex.flex-wrap.px-3.py-2 each tag in lesson.tags a.text-xs.mr-2(href=`/class/tags?tags=${tag}`) From ecc2d26dde7bdab63858676fc0cb3f680f4add38 Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Sun, 7 Jan 2024 17:07:54 -0500 Subject: [PATCH 06/18] improve filter page --- src/controllers/lessons.js | 10 +++++++++- src/views/filteredLessons.pug | 6 +++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/controllers/lessons.js b/src/controllers/lessons.js index 9cd8b9c..6e764e7 100644 --- a/src/controllers/lessons.js +++ b/src/controllers/lessons.js @@ -169,10 +169,18 @@ export const filterByTags = async (req, res) => { .sort({ _id: 1 }) .lean(); + const allTags = await Lesson.aggregate([ + { $unwind: "$tags" }, + { $group: { _id: "$tags", count: { $sum: 1 } } }, + { $sort: { _id: 1 } }, + ]); + + console.log({allTags}); + if (req.isAuthenticated()) { lessons = await getAllLessonsProgress(req.user.id, lessons); } - res.render("filteredLessons", { lessons }); + res.render("filteredLessons", { lessons, allTags }); } catch (err) { console.log(err); res.redirect("/class/all"); diff --git a/src/views/filteredLessons.pug b/src/views/filteredLessons.pug index 1f07a58..7a9b029 100644 --- a/src/views/filteredLessons.pug +++ b/src/views/filteredLessons.pug @@ -11,7 +11,11 @@ block content h1.mb-4 Filter Results - + h4 Results: #{lessons.length} + + h4 Filters: + each tag in allTags || [] + a.mr-2(href=`/class/tags?tags=${tag._id}`) #{tag._id} #lessons.grid.gap-y-12.gap-x-16(class="sm:gap-y-24 grid-cols-[repeat(auto-fill,_minmax(285px,_1fr))]") each lesson in lessons From 350b134249286aeef7eb8e3c2914fc09f09091b9 Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Sun, 7 Jan 2024 19:32:14 -0500 Subject: [PATCH 07/18] added ability to use filter buttons --- src/assets/js/filterTags.js | 32 ++++++++++++++++++++++++++++++++ src/controllers/lessons.js | 4 +--- src/views/filteredLessons.pug | 26 +++++++++++++++++++++----- src/views/lesson.pug | 2 +- 4 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 src/assets/js/filterTags.js diff --git a/src/assets/js/filterTags.js b/src/assets/js/filterTags.js new file mode 100644 index 0000000..9ec2171 --- /dev/null +++ b/src/assets/js/filterTags.js @@ -0,0 +1,32 @@ +const filterTags = document.querySelectorAll(".filter-tag"); +const clearFilterBtn = document.getElementById("filter-tag-clear"); + +// when clicking on a filter tag, add it to the url query string like /class/tags?tags=tag1,tag2 +filterTags.forEach((tag) => { + tag.addEventListener("click", (e) => { + let tag = e.target.innerText; + //url encode the tag + tag = encodeURIComponent(e.target.innerText); + const tags = new URLSearchParams(window.location.search).get("tags"); + + // if the tag is already in the query string, remove it + let newTags = ""; + if (tags) { + const tagArr = tags.split(","); + if (tagArr.includes(tag)) { + tagArr.splice(tagArr.indexOf(tag), 1); + } else { + tagArr.push(tag); + } + newTags = tagArr.join(","); + } else { + newTags = tag; + } + + window.location.href = `/class/tags?tags=${newTags}`; + }); +}); + +clearFilterBtn.addEventListener("click", (e) => { + window.location.href = `/class/tags`; +}); diff --git a/src/controllers/lessons.js b/src/controllers/lessons.js index 6e764e7..343c0ed 100644 --- a/src/controllers/lessons.js +++ b/src/controllers/lessons.js @@ -175,12 +175,10 @@ export const filterByTags = async (req, res) => { { $sort: { _id: 1 } }, ]); - console.log({allTags}); - if (req.isAuthenticated()) { lessons = await getAllLessonsProgress(req.user.id, lessons); } - res.render("filteredLessons", { lessons, allTags }); + res.render("filteredLessons", { lessons, allTags, selectedTags: tags }); } catch (err) { console.log(err); res.redirect("/class/all"); diff --git a/src/views/filteredLessons.pug b/src/views/filteredLessons.pug index 7a9b029..de8ea13 100644 --- a/src/views/filteredLessons.pug +++ b/src/views/filteredLessons.pug @@ -11,11 +11,26 @@ block content h1.mb-4 Filter Results - h4 Results: #{lessons.length} - - h4 Filters: - each tag in allTags || [] - a.mr-2(href=`/class/tags?tags=${tag._id}`) #{tag._id} + h4.mt-2 Filters Available: + + #tags + .flex.flex-wrap.mb-4 + each tag in allTags || [] + button.mr-1.text-xs.mb-1( + type="button" + class=`filter-tag px-2 py-1 rounded ${ + selectedTags && selectedTags.includes(tag._id) ? "bg-pink-500 text-white" : "bg-pink-200 text-pink-800" + }`) #{tag._id} + + + button.mr-1.text-xs.mb-1( + type="button" + id="filter-tag-clear" + class=`px-2 py-1 rounded ${ + selectedTags && selectedTags.length > 1 ? "bg-pink-500 text-white" : "bg-pink-200 text-pink-800" + }`) Clear Filters + + h4.mt-2.mb-2 Results: #{lessons.length} #lessons.grid.gap-y-12.gap-x-16(class="sm:gap-y-24 grid-cols-[repeat(auto-fill,_minmax(285px,_1fr))]") each lesson in lessons @@ -27,3 +42,4 @@ append scripts if loggedIn script(src="/js/lessonProgress.js") script(src="/js/lessonDone.js") + script(src="/js/filterTags.js") diff --git a/src/views/lesson.pug b/src/views/lesson.pug index 2680118..25c77a5 100644 --- a/src/views/lesson.pug +++ b/src/views/lesson.pug @@ -82,7 +82,7 @@ block content p #[a(href=`https://www.youtube.com/watch?v=${lesson.videoId}` target="_blank") Like, comment and subscribe on Youtube] if assigned.length - h2.mt-10.mb-3.border-2.border-orange-600.border-dashed Homework Assigned + h2.mt-10.mb-3 Homework Assigned each homework in assigned .mb-6(class="shadow-[0_2px_10px_0_rgba(0,0,0,0.1)]") +homework(homework) From 96dfee4700d4fbbb417d4520dd76c26221c84d7c Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Sun, 7 Jan 2024 19:42:01 -0500 Subject: [PATCH 08/18] add count of filter results for each tag --- src/assets/js/filterTags.js | 6 ++++-- src/views/filteredLessons.pug | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/assets/js/filterTags.js b/src/assets/js/filterTags.js index 9ec2171..e697748 100644 --- a/src/assets/js/filterTags.js +++ b/src/assets/js/filterTags.js @@ -4,9 +4,11 @@ const clearFilterBtn = document.getElementById("filter-tag-clear"); // when clicking on a filter tag, add it to the url query string like /class/tags?tags=tag1,tag2 filterTags.forEach((tag) => { tag.addEventListener("click", (e) => { - let tag = e.target.innerText; + let tag = e.target.value; + //url encode the tag - tag = encodeURIComponent(e.target.innerText); + tag = encodeURIComponent(tag); + const tags = new URLSearchParams(window.location.search).get("tags"); // if the tag is already in the query string, remove it diff --git a/src/views/filteredLessons.pug b/src/views/filteredLessons.pug index de8ea13..827054e 100644 --- a/src/views/filteredLessons.pug +++ b/src/views/filteredLessons.pug @@ -18,10 +18,10 @@ block content each tag in allTags || [] button.mr-1.text-xs.mb-1( type="button" + value=tag._id class=`filter-tag px-2 py-1 rounded ${ selectedTags && selectedTags.includes(tag._id) ? "bg-pink-500 text-white" : "bg-pink-200 text-pink-800" - }`) #{tag._id} - + }`) #{tag._id} (#{tag.count}) button.mr-1.text-xs.mb-1( type="button" @@ -30,7 +30,7 @@ block content selectedTags && selectedTags.length > 1 ? "bg-pink-500 text-white" : "bg-pink-200 text-pink-800" }`) Clear Filters - h4.mt-2.mb-2 Results: #{lessons.length} + h3.mt-2.mb-2 Results: #{lessons.length} #lessons.grid.gap-y-12.gap-x-16(class="sm:gap-y-24 grid-cols-[repeat(auto-fill,_minmax(285px,_1fr))]") each lesson in lessons From af04d41c2f82e0986ca1c13a8ee7539f9765911f Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Sun, 7 Jan 2024 19:52:52 -0500 Subject: [PATCH 09/18] fix styling on edit class page --- src/views/addLesson.pug | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/addLesson.pug b/src/views/addLesson.pug index 9ab7c18..73428b2 100644 --- a/src/views/addLesson.pug +++ b/src/views/addLesson.pug @@ -55,9 +55,10 @@ block content input#ts-title-0(type="text", name="tsTitle") #tags.col-span-4 h2.col-span-4.mb-3 Tags - label(for="tags") Tags + label(for="tags" class="absolute -ml-2 -mt-2 px-1 text-xs bg-white text-gray-500 rounded") Tags (separated by commas. e.g. html, css, job hunt) textarea#tag.col-span-4(type="text", name="tags" placeholder="html, css, javascript, js, career help,ejs" + class="h-24 w-full " ) #{edit? lesson.tags : ""} .col-span-4.mt-3.text-center button(class="btn primary max-w-1/3 mx-auto" type="submit") #{action} Class From 7610f44e06eb5318cc19ce565fc17a0f8919f064 Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Sun, 7 Jan 2024 20:00:50 -0500 Subject: [PATCH 10/18] update the redirect when editing a class --- src/controllers/lessons.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/lessons.js b/src/controllers/lessons.js index 343c0ed..f91ac53 100644 --- a/src/controllers/lessons.js +++ b/src/controllers/lessons.js @@ -88,7 +88,7 @@ export const addEditLesson = async (req, res) => { message: [`Class not ${!!req.params.id ? "updated" : "added"}`], }; } finally { - res.redirect("/class/add"); + res.redirect(`/class/edit/${req.params.id}`); } }; From 2f27451653ac0bcecfa4e68194f3266cfcf36277 Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Sun, 7 Jan 2024 22:05:34 -0500 Subject: [PATCH 11/18] add some final styling --- src/assets/js/addLesson.js | 61 ++++++++++++++++++--------------- src/assets/js/filterTags.js | 10 +++--- src/controllers/lessons.js | 10 +++--- src/views/addLesson.pug | 4 +-- src/views/filteredLessons.pug | 12 +++---- src/views/lesson.pug | 11 +++--- src/views/mixins/lessonCard.pug | 2 +- 7 files changed, 57 insertions(+), 53 deletions(-) diff --git a/src/assets/js/addLesson.js b/src/assets/js/addLesson.js index 1e223fb..5178856 100644 --- a/src/assets/js/addLesson.js +++ b/src/assets/js/addLesson.js @@ -1,48 +1,53 @@ -const title = document.getElementById('video-title'); -const videoId = document.getElementById('video-id'); -const classNo = document.getElementById('number'); -const addTsButton = document.getElementById('add-timestamp'); -let tsIndex = document.querySelectorAll('.timestamp').length; +const title = document.getElementById("video-title"); +const videoId = document.getElementById("video-id"); +const classNo = document.getElementById("number"); +const addTsButton = document.getElementById("add-timestamp"); +let tsIndex = document.querySelectorAll(".timestamp").length; title.addEventListener("change", addPermalink); videoId.addEventListener("change", addThumbnail); classNo.addEventListener("change", updateSlidesClass); -addTsButton.addEventListener('click', addTimestamp); +addTsButton.addEventListener("click", addTimestamp); function addPermalink() { - const permalink = title.value - - .split('') - .map(c => c.toLowerCase()) - .filter(c => { - return (c.charCodeAt(0) >= 97 && c.charCodeAt(0) <= 122) || c.charCodeAt(0) === 32; - }) - .join('') - .replace(/\s/g, "-"); - document.getElementById("permalink").value = permalink; - document.getElementById("permalink").disabled = false; + const permalink = title.value + + .split("") + .map((c) => c.toLowerCase()) + .filter((c) => { + return ( + (c.charCodeAt(0) >= 97 && c.charCodeAt(0) <= 122) || + c.charCodeAt(0) === 32 + ); + }) + .join("") + .replace(/\s/g, "-"); + document.getElementById("permalink").value = permalink; + document.getElementById("permalink").disabled = false; } function addThumbnail() { - const thumbnail = `https://i3.ytimg.com/vi/${videoId.value}/maxresdefault.jpg`; - document.getElementById("thumbnail").value = thumbnail; - document.getElementById("thumbnail").disabled = false; + const thumbnail = `https://i3.ytimg.com/vi/${videoId.value}/maxresdefault.jpg`; + document.getElementById("thumbnail").value = thumbnail; + document.getElementById("thumbnail").disabled = false; } function updateSlidesClass() { - document.getElementById("slides-classNo").value = classNo.value; + document.getElementById("slides-classNo").value = classNo.value; } function addTimestamp(e) { - e.preventDefault(); - const newTs = document.createElement('div'); - newTs.classList.add('timestamp'); - newTs.innerHTML = ` + e.preventDefault(); + const newTs = document.createElement("div"); + newTs.classList.add("timestamp"); + newTs.innerHTML = ` `; - tsIndex += 1; - document.getElementById('timestamps').append(newTs); -} \ No newline at end of file + tsIndex += 1; + document.getElementById("timestamps").append(newTs); +} + + diff --git a/src/assets/js/filterTags.js b/src/assets/js/filterTags.js index e697748..0c4532d 100644 --- a/src/assets/js/filterTags.js +++ b/src/assets/js/filterTags.js @@ -4,27 +4,27 @@ const clearFilterBtn = document.getElementById("filter-tag-clear"); // when clicking on a filter tag, add it to the url query string like /class/tags?tags=tag1,tag2 filterTags.forEach((tag) => { tag.addEventListener("click", (e) => { - let tag = e.target.value; + const tag = e.target.value; //url encode the tag - tag = encodeURIComponent(tag); - + const encodedTag = encodeURIComponent(tag); + const tags = new URLSearchParams(window.location.search).get("tags"); // if the tag is already in the query string, remove it let newTags = ""; + if (tags) { const tagArr = tags.split(","); if (tagArr.includes(tag)) { tagArr.splice(tagArr.indexOf(tag), 1); } else { - tagArr.push(tag); + tagArr.push(encodedTag); } newTags = tagArr.join(","); } else { newTags = tag; } - window.location.href = `/class/tags?tags=${newTags}`; }); }); diff --git a/src/controllers/lessons.js b/src/controllers/lessons.js index f91ac53..2b2cae8 100644 --- a/src/controllers/lessons.js +++ b/src/controllers/lessons.js @@ -169,11 +169,11 @@ export const filterByTags = async (req, res) => { .sort({ _id: 1 }) .lean(); - const allTags = await Lesson.aggregate([ - { $unwind: "$tags" }, - { $group: { _id: "$tags", count: { $sum: 1 } } }, - { $sort: { _id: 1 } }, - ]); + const allTags = await Lesson.aggregate([ + { $unwind: "$tags" }, + { $group: { _id: "$tags", count: { $sum: 1 } } }, + { $sort: { _id: 1 } }, + ]); if (req.isAuthenticated()) { lessons = await getAllLessonsProgress(req.user.id, lessons); diff --git a/src/views/addLesson.pug b/src/views/addLesson.pug index 73428b2..50ad590 100644 --- a/src/views/addLesson.pug +++ b/src/views/addLesson.pug @@ -56,9 +56,9 @@ block content #tags.col-span-4 h2.col-span-4.mb-3 Tags label(for="tags" class="absolute -ml-2 -mt-2 px-1 text-xs bg-white text-gray-500 rounded") Tags (separated by commas. e.g. html, css, job hunt) - textarea#tag.col-span-4(type="text", name="tags" + textarea.col-span-4(type="text", name="tags" placeholder="html, css, javascript, js, career help,ejs" - class="h-24 w-full " + class="h-24 w-full" ) #{edit? lesson.tags : ""} .col-span-4.mt-3.text-center button(class="btn primary max-w-1/3 mx-auto" type="submit") #{action} Class diff --git a/src/views/filteredLessons.pug b/src/views/filteredLessons.pug index 827054e..9b81bdf 100644 --- a/src/views/filteredLessons.pug +++ b/src/views/filteredLessons.pug @@ -11,26 +11,24 @@ block content h1.mb-4 Filter Results - h4.mt-2 Filters Available: #tags + h3.mt-2.mb-2 Filters Available: .flex.flex-wrap.mb-4 each tag in allTags || [] - button.mr-1.text-xs.mb-1( + button.text-xs.m-1( type="button" value=tag._id class=`filter-tag px-2 py-1 rounded ${ - selectedTags && selectedTags.includes(tag._id) ? "bg-pink-500 text-white" : "bg-pink-200 text-pink-800" + selectedTags && selectedTags.includes(tag._id) ? "outline outline-1 outline-pink-800 bg-pink-800 text-white" : "outline outline-1 text-pink-800" }`) #{tag._id} (#{tag.count}) button.mr-1.text-xs.mb-1( type="button" id="filter-tag-clear" - class=`px-2 py-1 rounded ${ - selectedTags && selectedTags.length > 1 ? "bg-pink-500 text-white" : "bg-pink-200 text-pink-800" - }`) Clear Filters + class=`px-2 py-1 rounded bg-pink-800 text-white`) Clear Filters - h3.mt-2.mb-2 Results: #{lessons.length} + h3.mt-2.mb-4 Results: #{lessons.length} #lessons.grid.gap-y-12.gap-x-16(class="sm:gap-y-24 grid-cols-[repeat(auto-fill,_minmax(285px,_1fr))]") each lesson in lessons diff --git a/src/views/lesson.pug b/src/views/lesson.pug index 25c77a5..473cd73 100644 --- a/src/views/lesson.pug +++ b/src/views/lesson.pug @@ -69,11 +69,11 @@ block content a(href=lesson.motivationLink) #{lesson.motivationTitle} if lesson.tags && lesson.tags.length - h2 Tags - .tags.flex.flex-wrap.px-3.py-2 - each tag in lesson.tags - a.text-xs.mr-2(href=`/class/tags?tags=${tag}`) - = tag + h2 Tags + .tags.flex.flex-wrap.py-2 + each tag in lesson.tags + a.text-sm.mr-2(href=`/class/tags?tags=${tag}`) + = tag @@ -93,6 +93,7 @@ block content .mb-6(class="shadow-[0_2px_10px_0_rgba(0,0,0,0.1)]") +homework(homework) + append scripts if loggedIn script(src="/js/hwDone.js") diff --git a/src/views/mixins/lessonCard.pug b/src/views/mixins/lessonCard.pug index 3f4fa38..fe66252 100644 --- a/src/views/mixins/lessonCard.pug +++ b/src/views/mixins/lessonCard.pug @@ -36,7 +36,7 @@ mixin lessonCard(lesson) if lesson.tags && lesson.tags.length - .tags.flex.flex-wrap.px-3.py-2 + .tags.flex.flex-wrap.py-2 each tag in lesson.tags a.text-xs.mr-2(href=`/class/tags?tags=${tag}`) = tag From e0b2961491119636253eb4e94567f236e757dab2 Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Sun, 7 Jan 2024 22:27:31 -0500 Subject: [PATCH 12/18] change endpoint to /filter instead of /tags --- src/assets/js/filterTags.js | 6 +++--- src/routes/lessonRouter.js | 2 +- src/views/lesson.pug | 2 +- src/views/mixins/lessonCard.pug | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/assets/js/filterTags.js b/src/assets/js/filterTags.js index 0c4532d..c957c86 100644 --- a/src/assets/js/filterTags.js +++ b/src/assets/js/filterTags.js @@ -1,7 +1,7 @@ const filterTags = document.querySelectorAll(".filter-tag"); const clearFilterBtn = document.getElementById("filter-tag-clear"); -// when clicking on a filter tag, add it to the url query string like /class/tags?tags=tag1,tag2 +// when clicking on a filter tag, add it to the url query string like /class/filter?tags=tag1,tag2 filterTags.forEach((tag) => { tag.addEventListener("click", (e) => { const tag = e.target.value; @@ -25,10 +25,10 @@ filterTags.forEach((tag) => { } else { newTags = tag; } - window.location.href = `/class/tags?tags=${newTags}`; + window.location.href = `/class/filter?tags=${newTags}`; }); }); clearFilterBtn.addEventListener("click", (e) => { - window.location.href = `/class/tags`; + window.location.href = `/class/filter`; }); diff --git a/src/routes/lessonRouter.js b/src/routes/lessonRouter.js index 9d39acd..9ea9de9 100644 --- a/src/routes/lessonRouter.js +++ b/src/routes/lessonRouter.js @@ -11,7 +11,7 @@ router.post("/add", lessons.addEditLesson); router.post("/edit/:id", lessons.addEditLesson); router.get("/all", lessons.allLessons); -router.get("/tags", lessons.filterByTags); +router.get("/filter", lessons.filterByTags); router.get("/:permalink", lessons.showLesson); router.put("/watched/:id", lessons.toggleWatched); diff --git a/src/views/lesson.pug b/src/views/lesson.pug index 473cd73..efbb952 100644 --- a/src/views/lesson.pug +++ b/src/views/lesson.pug @@ -72,7 +72,7 @@ block content h2 Tags .tags.flex.flex-wrap.py-2 each tag in lesson.tags - a.text-sm.mr-2(href=`/class/tags?tags=${tag}`) + a.text-sm.mr-2(href=`/class/filter?tags=${tag}`) = tag diff --git a/src/views/mixins/lessonCard.pug b/src/views/mixins/lessonCard.pug index fe66252..6f9cda5 100644 --- a/src/views/mixins/lessonCard.pug +++ b/src/views/mixins/lessonCard.pug @@ -38,7 +38,7 @@ mixin lessonCard(lesson) .tags.flex.flex-wrap.py-2 each tag in lesson.tags - a.text-xs.mr-2(href=`/class/tags?tags=${tag}`) + a.text-xs.mr-2(href=`/class/filter?tags=${tag}`) = tag a.absolute.bottom-3(href=`/class/${lesson.permalink}`) More Info From 7152aa35677aca24b9b4f93da50d9b0b905eaa7d Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Sun, 7 Jan 2024 22:43:06 -0500 Subject: [PATCH 13/18] whitespace/formatting changes --- src/assets/js/addLesson.js | 61 +++++++++++++++------------------ src/views/mixins/lessonCard.pug | 6 +--- 2 files changed, 29 insertions(+), 38 deletions(-) diff --git a/src/assets/js/addLesson.js b/src/assets/js/addLesson.js index 5178856..5b8070c 100644 --- a/src/assets/js/addLesson.js +++ b/src/assets/js/addLesson.js @@ -1,53 +1,48 @@ -const title = document.getElementById("video-title"); -const videoId = document.getElementById("video-id"); -const classNo = document.getElementById("number"); -const addTsButton = document.getElementById("add-timestamp"); -let tsIndex = document.querySelectorAll(".timestamp").length; +const title = document.getElementById('video-title'); +const videoId = document.getElementById('video-id'); +const classNo = document.getElementById('number'); +const addTsButton = document.getElementById('add-timestamp'); +let tsIndex = document.querySelectorAll('.timestamp').length; title.addEventListener("change", addPermalink); videoId.addEventListener("change", addThumbnail); classNo.addEventListener("change", updateSlidesClass); -addTsButton.addEventListener("click", addTimestamp); +addTsButton.addEventListener('click', addTimestamp); function addPermalink() { - const permalink = title.value - - .split("") - .map((c) => c.toLowerCase()) - .filter((c) => { - return ( - (c.charCodeAt(0) >= 97 && c.charCodeAt(0) <= 122) || - c.charCodeAt(0) === 32 - ); - }) - .join("") - .replace(/\s/g, "-"); - document.getElementById("permalink").value = permalink; - document.getElementById("permalink").disabled = false; + const permalink = title.value + + .split('') + .map(c => c.toLowerCase()) + .filter(c => { + return (c.charCodeAt(0) >= 97 && c.charCodeAt(0) <= 122) || c.charCodeAt(0) === 32; + }) + .join('') + .replace(/\s/g, "-"); + document.getElementById("permalink").value = permalink; + document.getElementById("permalink").disabled = false; } function addThumbnail() { - const thumbnail = `https://i3.ytimg.com/vi/${videoId.value}/maxresdefault.jpg`; - document.getElementById("thumbnail").value = thumbnail; - document.getElementById("thumbnail").disabled = false; + const thumbnail = `https://i3.ytimg.com/vi/${videoId.value}/maxresdefault.jpg`; + document.getElementById("thumbnail").value = thumbnail; + document.getElementById("thumbnail").disabled = false; } function updateSlidesClass() { - document.getElementById("slides-classNo").value = classNo.value; + document.getElementById("slides-classNo").value = classNo.value; } function addTimestamp(e) { - e.preventDefault(); - const newTs = document.createElement("div"); - newTs.classList.add("timestamp"); - newTs.innerHTML = ` + e.preventDefault(); + const newTs = document.createElement('div'); + newTs.classList.add('timestamp'); + newTs.innerHTML = ` `; - tsIndex += 1; - document.getElementById("timestamps").append(newTs); -} - - + tsIndex += 1; + document.getElementById('timestamps').append(newTs); +} \ No newline at end of file diff --git a/src/views/mixins/lessonCard.pug b/src/views/mixins/lessonCard.pug index 6f9cda5..5765d0b 100644 --- a/src/views/mixins/lessonCard.pug +++ b/src/views/mixins/lessonCard.pug @@ -26,16 +26,12 @@ mixin lessonCard(lesson) input.checkedin.hidden(type="checkbox" id="checkedin-" + lesson._id autocomplete="off" checked) | No Check In - - .card-main-content.py-4.px-6.mb-12.group a(href=`/class/${lesson.permalink}` class="hover:no-underline") img.w-full.mb-2(src=lesson.thumbnail class="group-hover:shadow-[0_0_10px_rgba(0,_0,_0,_0.2)]") h2(class="group-hover:text-pink-900") #{lesson.title} - - - if lesson.tags && lesson.tags.length + if lesson.tags && lesson.tags.length .tags.flex.flex-wrap.py-2 each tag in lesson.tags a.text-xs.mr-2(href=`/class/filter?tags=${tag}`) From 0eb9bba51521a1c8f62237815a3379eeaf2909cf Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Sun, 7 Jan 2024 22:49:00 -0500 Subject: [PATCH 14/18] remove comment --- src/tailwind.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tailwind.css b/src/tailwind.css index 0228592..4c51e49 100644 --- a/src/tailwind.css +++ b/src/tailwind.css @@ -86,7 +86,6 @@ } #timestamps, #tags, - /* do i need to add this here? */ #hw-items, #pw-items { @apply mt-3 w-full border px-5 py-3; From f27de2f22f2d7827405718c008edd60d00c290e6 Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Mon, 8 Jan 2024 01:33:33 -0500 Subject: [PATCH 15/18] fix bug with crash when there are no tags --- src/controllers/lessons.js | 2 ++ src/views/lesson.pug | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/controllers/lessons.js b/src/controllers/lessons.js index 2b2cae8..fec5945 100644 --- a/src/controllers/lessons.js +++ b/src/controllers/lessons.js @@ -149,6 +149,8 @@ export const showLesson = async (req, res) => { .sort({ _id: 1 }) .populate(["items", "extras"]); + lesson?.tags ? lesson.tags.sort() : (lesson.tags = []); + if (req.isAuthenticated()) { lesson = await getLessonProgress(req.user.id, lesson); if (assigned.length) diff --git a/src/views/lesson.pug b/src/views/lesson.pug index efbb952..02f7d5a 100644 --- a/src/views/lesson.pug +++ b/src/views/lesson.pug @@ -69,11 +69,11 @@ block content a(href=lesson.motivationLink) #{lesson.motivationTitle} if lesson.tags && lesson.tags.length - h2 Tags - .tags.flex.flex-wrap.py-2 - each tag in lesson.tags - a.text-sm.mr-2(href=`/class/filter?tags=${tag}`) - = tag + h2 Tags + .tags.flex.flex-wrap.py-2 + each tag in lesson.tags + a.text-sm.mr-2(href=`/class/filter?tags=${tag}`) + = tag From 10f29e3b032f9870f58d871fb8b9461f26217c17 Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Thu, 11 Jan 2024 22:06:56 -0500 Subject: [PATCH 16/18] changes from self review --- src/assets/css/index.css | 149 ++++++++++++++++++------------------ src/assets/js/filterTags.js | 6 +- src/controllers/lessons.js | 4 +- 3 files changed, 82 insertions(+), 77 deletions(-) diff --git a/src/assets/css/index.css b/src/assets/css/index.css index b68f0f4..39a1343 100644 --- a/src/assets/css/index.css +++ b/src/assets/css/index.css @@ -902,6 +902,7 @@ h3 { } #timestamps, + #tags, #hw-items, #pw-items { margin-top: 0.75rem; @@ -1243,6 +1244,10 @@ dd ol { top: 0px; } +.left-10 { + left: 2.5rem; +} + .top-12 { top: 3rem; } @@ -1259,34 +1264,6 @@ dd ol { right: 0.25rem; } -.top-10 { - top: 2.5rem; -} - -.top-8 { - top: 2rem; -} - -.-left-20 { - left: -5rem; -} - -.left-0 { - left: 0px; -} - -.left-10 { - left: 2.5rem; -} - -.left-5 { - left: 1.25rem; -} - -.left-9 { - left: 2.25rem; -} - .z-10 { z-index: 10; } @@ -1315,6 +1292,10 @@ dd ol { margin: 0.5rem; } +.m-1 { + margin: 0.25rem; +} + .m-0 { margin: 0px; } @@ -1365,6 +1346,14 @@ dd ol { margin-top: 0.75rem; } +.-ml-2 { + margin-left: -0.5rem; +} + +.-mt-2 { + margin-top: -0.5rem; +} + .mb-4 { margin-bottom: 1rem; } @@ -1373,6 +1362,10 @@ dd ol { margin-right: auto; } +.mr-1 { + margin-right: 0.25rem; +} + .mb-1 { margin-bottom: 0.25rem; } @@ -1441,8 +1434,8 @@ dd ol { height: auto; } -.min-h-screen { - min-height: 100vh; +.h-24 { + height: 6rem; } .w-full { @@ -1566,6 +1559,10 @@ dd ol { white-space: nowrap; } +.rounded { + border-radius: 0.25rem; +} + .border { border-width: 1px; } @@ -1597,6 +1594,11 @@ dd ol { border-color: rgb(231 229 228 / var(--tw-border-opacity)); } +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); +} + .bg-pink-800 { --tw-bg-opacity: 1; background-color: rgb(157 23 77 / var(--tw-bg-opacity)); @@ -1612,11 +1614,6 @@ dd ol { background-color: rgb(250 250 249 / var(--tw-bg-opacity)); } -.bg-white { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity)); -} - .p-6 { padding: 1.5rem; } @@ -1625,6 +1622,21 @@ dd ol { padding: 0px; } +.px-1 { + padding-left: 0.25rem; + padding-right: 0.25rem; +} + +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; @@ -1689,14 +1701,15 @@ dd ol { text-align: right; } -.font-main { - font-family: Poppins, sans-serif; -} - .font-logo { font-family: Orienta, serif; } +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + .text-xl { font-size: 1.25rem; line-height: 1.75rem; @@ -1707,11 +1720,6 @@ dd ol { line-height: 1.5rem; } -.text-lg { - font-size: 1.125rem; - line-height: 1.75rem; -} - .text-sm { font-size: 0.875rem; line-height: 1.25rem; @@ -1722,11 +1730,6 @@ dd ol { line-height: 2rem; } -.text-xs { - font-size: 0.75rem; - line-height: 1rem; -} - .text-3xl { font-size: 1.875rem; line-height: 2.25rem; @@ -1766,14 +1769,14 @@ dd ol { color: rgb(239 68 68 / var(--tw-text-opacity)); } -.text-twilight-900 { +.text-gray-500 { --tw-text-opacity: 1; - color: rgb(21 63 81 / var(--tw-text-opacity)); + color: rgb(107 114 128 / var(--tw-text-opacity)); } -.text-stone-500 { +.text-twilight-900 { --tw-text-opacity: 1; - color: rgb(120 113 108 / var(--tw-text-opacity)); + color: rgb(21 63 81 / var(--tw-text-opacity)); } .text-white { @@ -1786,14 +1789,14 @@ dd ol { color: rgb(157 23 77 / var(--tw-text-opacity)); } -.text-twilight-800 { +.text-stone-500 { --tw-text-opacity: 1; - color: rgb(34 101 129 / var(--tw-text-opacity)); + color: rgb(120 113 108 / var(--tw-text-opacity)); } -.text-stone-800 { +.text-twilight-800 { --tw-text-opacity: 1; - color: rgb(41 37 36 / var(--tw-text-opacity)); + color: rgb(34 101 129 / var(--tw-text-opacity)); } .text-red-800 { @@ -1805,15 +1808,15 @@ dd ol { color: rgb(168 162 158 / 0.8); } -.text-pink-800\/80 { - color: rgb(157 23 77 / 0.8); -} - .text-red-700 { --tw-text-opacity: 1; color: rgb(185 28 28 / var(--tw-text-opacity)); } +.text-pink-800\/80 { + color: rgb(157 23 77 / 0.8); +} + .no-underline { text-decoration-line: none; } @@ -1842,6 +1845,18 @@ dd ol { box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } +.outline { + outline-style: solid; +} + +.outline-1 { + outline-width: 1px; +} + +.outline-pink-800 { + outline-color: #9d174d; +} + .filter { filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); } @@ -1883,10 +1898,6 @@ dd ol { } @media (min-width: 780px) { - .sm\:left-8 { - left: 2rem; - } - .sm\:left-12 { left: 3rem; } @@ -1955,14 +1966,6 @@ dd ol { } @media (min-width: 1024px) { - .lg\:left-14 { - left: 3.5rem; - } - - .lg\:left-16 { - left: 4rem; - } - .lg\:left-20 { left: 5rem; } diff --git a/src/assets/js/filterTags.js b/src/assets/js/filterTags.js index c957c86..3b9efef 100644 --- a/src/assets/js/filterTags.js +++ b/src/assets/js/filterTags.js @@ -9,13 +9,13 @@ filterTags.forEach((tag) => { //url encode the tag const encodedTag = encodeURIComponent(tag); - const tags = new URLSearchParams(window.location.search).get("tags"); + const existTags = new URLSearchParams(window.location.search).get("tags"); // if the tag is already in the query string, remove it let newTags = ""; - if (tags) { - const tagArr = tags.split(","); + if (existTags) { + const tagArr = existTags.split(","); if (tagArr.includes(tag)) { tagArr.splice(tagArr.indexOf(tag), 1); } else { diff --git a/src/controllers/lessons.js b/src/controllers/lessons.js index fec5945..298cfab 100644 --- a/src/controllers/lessons.js +++ b/src/controllers/lessons.js @@ -149,7 +149,9 @@ export const showLesson = async (req, res) => { .sort({ _id: 1 }) .populate(["items", "extras"]); - lesson?.tags ? lesson.tags.sort() : (lesson.tags = []); + if (!lesson?.tags) { + lesson.tags = []; + } if (req.isAuthenticated()) { lesson = await getLessonProgress(req.user.id, lesson); From 4d421ce3447b6b1793c0c5c80781c5cf5a25b37d Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Thu, 11 Jan 2024 23:27:32 -0500 Subject: [PATCH 17/18] revert index.css --- src/assets/css/index.css | 149 +++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 76 deletions(-) diff --git a/src/assets/css/index.css b/src/assets/css/index.css index 39a1343..b68f0f4 100644 --- a/src/assets/css/index.css +++ b/src/assets/css/index.css @@ -902,7 +902,6 @@ h3 { } #timestamps, - #tags, #hw-items, #pw-items { margin-top: 0.75rem; @@ -1244,10 +1243,6 @@ dd ol { top: 0px; } -.left-10 { - left: 2.5rem; -} - .top-12 { top: 3rem; } @@ -1264,6 +1259,34 @@ dd ol { right: 0.25rem; } +.top-10 { + top: 2.5rem; +} + +.top-8 { + top: 2rem; +} + +.-left-20 { + left: -5rem; +} + +.left-0 { + left: 0px; +} + +.left-10 { + left: 2.5rem; +} + +.left-5 { + left: 1.25rem; +} + +.left-9 { + left: 2.25rem; +} + .z-10 { z-index: 10; } @@ -1292,10 +1315,6 @@ dd ol { margin: 0.5rem; } -.m-1 { - margin: 0.25rem; -} - .m-0 { margin: 0px; } @@ -1346,14 +1365,6 @@ dd ol { margin-top: 0.75rem; } -.-ml-2 { - margin-left: -0.5rem; -} - -.-mt-2 { - margin-top: -0.5rem; -} - .mb-4 { margin-bottom: 1rem; } @@ -1362,10 +1373,6 @@ dd ol { margin-right: auto; } -.mr-1 { - margin-right: 0.25rem; -} - .mb-1 { margin-bottom: 0.25rem; } @@ -1434,8 +1441,8 @@ dd ol { height: auto; } -.h-24 { - height: 6rem; +.min-h-screen { + min-height: 100vh; } .w-full { @@ -1559,10 +1566,6 @@ dd ol { white-space: nowrap; } -.rounded { - border-radius: 0.25rem; -} - .border { border-width: 1px; } @@ -1594,11 +1597,6 @@ dd ol { border-color: rgb(231 229 228 / var(--tw-border-opacity)); } -.bg-white { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity)); -} - .bg-pink-800 { --tw-bg-opacity: 1; background-color: rgb(157 23 77 / var(--tw-bg-opacity)); @@ -1614,6 +1612,11 @@ dd ol { background-color: rgb(250 250 249 / var(--tw-bg-opacity)); } +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); +} + .p-6 { padding: 1.5rem; } @@ -1622,21 +1625,6 @@ dd ol { padding: 0px; } -.px-1 { - padding-left: 0.25rem; - padding-right: 0.25rem; -} - -.px-2 { - padding-left: 0.5rem; - padding-right: 0.5rem; -} - -.py-1 { - padding-top: 0.25rem; - padding-bottom: 0.25rem; -} - .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; @@ -1701,13 +1689,12 @@ dd ol { text-align: right; } -.font-logo { - font-family: Orienta, serif; +.font-main { + font-family: Poppins, sans-serif; } -.text-xs { - font-size: 0.75rem; - line-height: 1rem; +.font-logo { + font-family: Orienta, serif; } .text-xl { @@ -1720,6 +1707,11 @@ dd ol { line-height: 1.5rem; } +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + .text-sm { font-size: 0.875rem; line-height: 1.25rem; @@ -1730,6 +1722,11 @@ dd ol { line-height: 2rem; } +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + .text-3xl { font-size: 1.875rem; line-height: 2.25rem; @@ -1769,14 +1766,14 @@ dd ol { color: rgb(239 68 68 / var(--tw-text-opacity)); } -.text-gray-500 { +.text-twilight-900 { --tw-text-opacity: 1; - color: rgb(107 114 128 / var(--tw-text-opacity)); + color: rgb(21 63 81 / var(--tw-text-opacity)); } -.text-twilight-900 { +.text-stone-500 { --tw-text-opacity: 1; - color: rgb(21 63 81 / var(--tw-text-opacity)); + color: rgb(120 113 108 / var(--tw-text-opacity)); } .text-white { @@ -1789,14 +1786,14 @@ dd ol { color: rgb(157 23 77 / var(--tw-text-opacity)); } -.text-stone-500 { +.text-twilight-800 { --tw-text-opacity: 1; - color: rgb(120 113 108 / var(--tw-text-opacity)); + color: rgb(34 101 129 / var(--tw-text-opacity)); } -.text-twilight-800 { +.text-stone-800 { --tw-text-opacity: 1; - color: rgb(34 101 129 / var(--tw-text-opacity)); + color: rgb(41 37 36 / var(--tw-text-opacity)); } .text-red-800 { @@ -1808,15 +1805,15 @@ dd ol { color: rgb(168 162 158 / 0.8); } +.text-pink-800\/80 { + color: rgb(157 23 77 / 0.8); +} + .text-red-700 { --tw-text-opacity: 1; color: rgb(185 28 28 / var(--tw-text-opacity)); } -.text-pink-800\/80 { - color: rgb(157 23 77 / 0.8); -} - .no-underline { text-decoration-line: none; } @@ -1845,18 +1842,6 @@ dd ol { box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } -.outline { - outline-style: solid; -} - -.outline-1 { - outline-width: 1px; -} - -.outline-pink-800 { - outline-color: #9d174d; -} - .filter { filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); } @@ -1898,6 +1883,10 @@ dd ol { } @media (min-width: 780px) { + .sm\:left-8 { + left: 2rem; + } + .sm\:left-12 { left: 3rem; } @@ -1966,6 +1955,14 @@ dd ol { } @media (min-width: 1024px) { + .lg\:left-14 { + left: 3.5rem; + } + + .lg\:left-16 { + left: 4rem; + } + .lg\:left-20 { left: 5rem; } From c56f36e41e51e83554a1ce92656894e9505dfaa9 Mon Sep 17 00:00:00 2001 From: Mario Nikhil Pereira <45009203+nmpereira@users.noreply.github.com> Date: Thu, 11 Jan 2024 23:29:22 -0500 Subject: [PATCH 18/18] update to remove login condition for js script --- src/views/filteredLessons.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/filteredLessons.pug b/src/views/filteredLessons.pug index 9b81bdf..fc3fb84 100644 --- a/src/views/filteredLessons.pug +++ b/src/views/filteredLessons.pug @@ -37,7 +37,7 @@ block content include ./partials/to-top.pug append scripts + script(src="/js/filterTags.js") if loggedIn script(src="/js/lessonProgress.js") script(src="/js/lessonDone.js") - script(src="/js/filterTags.js")